[automerger skipped] Merge "Merge history of cts/hostsidetests/statsd" into rvc-qpr-dev-plus-aosp am: 01f2286d3b -s ours

am skip reason: Change-Id I8312ddeb83d513a8d5f07dd51cd7990cdc88d38b with SHA-1 057246e8b5 is in history

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/modules/StatsD/+/13203117

MUST ONLY BE SUBMITTED BY AUTOMERGER

Change-Id: I18dde7fe200f6db01006a6cfcc5d6f91601cfb95
diff --git a/apex/TEST_MAPPING b/apex/TEST_MAPPING
index 93f1087..331fe77 100644
--- a/apex/TEST_MAPPING
+++ b/apex/TEST_MAPPING
@@ -6,5 +6,14 @@
     {
       "name" : "LibStatsPullTests"
     }
+  ],
+
+  "postsubmit" : [
+    {
+      "name" : "CtsStatsdHostTestCases"
+    },
+    {
+      "name" : "GtsStatsdHostTestCases"
+    }
   ]
 }
diff --git a/apex/aidl/Android.bp b/apex/aidl/Android.bp
index 04339e6..f66cf7c 100644
--- a/apex/aidl/Android.bp
+++ b/apex/aidl/Android.bp
@@ -30,6 +30,7 @@
         "android/os/StatsDimensionsValueParcel.aidl",
         "android/util/StatsEventParcel.aidl",
     ],
+    host_supported: true,
     backend: {
         java: {
             enabled: false, // framework-statsd and service-statsd use framework-statsd-aidl-sources
diff --git a/apex/apex_manifest.json b/apex/apex_manifest.json
index e2972e7..1d029c6 100644
--- a/apex/apex_manifest.json
+++ b/apex/apex_manifest.json
@@ -1,5 +1,5 @@
 {
   "name": "com.android.os.statsd",
-  "version": 300000000
+  "version": 309999900
 }
 
diff --git a/apex/framework/java/android/app/StatsManager.java b/apex/framework/java/android/app/StatsManager.java
index a7d2057..41803cf 100644
--- a/apex/framework/java/android/app/StatsManager.java
+++ b/apex/framework/java/android/app/StatsManager.java
@@ -547,7 +547,7 @@
 
         @Override
         public void onPullAtom(int atomTag, IPullAtomResultReceiver resultReceiver) {
-            long token = Binder.clearCallingIdentity();
+            final long token = Binder.clearCallingIdentity();
             try {
                 mExecutor.execute(() -> {
                     List<StatsEvent> data = new ArrayList<>();
diff --git a/apex/framework/test/Android.bp b/apex/framework/test/Android.bp
index b113d59..5cc5647 100644
--- a/apex/framework/test/Android.bp
+++ b/apex/framework/test/Android.bp
@@ -14,12 +14,8 @@
 
 android_test {
     name: "FrameworkStatsdTest",
-    platform_apis: true,
-    srcs: [
-        // TODO(b/147705194): Use framework-statsd as a lib dependency instead.
-        ":framework-statsd-sources",
-        "**/*.java",
-    ],
+    sdk_version: "module_current",
+    srcs: [ "**/*.java" ],
     manifest: "AndroidManifest.xml",
     static_libs: [
         "androidx.test.rules",
@@ -28,9 +24,10 @@
     libs: [
         "android.test.runner.stubs",
         "android.test.base.stubs",
+        "framework-statsd.impl",
     ],
     test_suites: [
         "device-tests",
         "mts",
     ],
-}
\ No newline at end of file
+}
diff --git a/apex/service/java/com/android/server/stats/StatsCompanionService.java b/apex/service/java/com/android/server/stats/StatsCompanionService.java
index b5e7224..fbda86f 100644
--- a/apex/service/java/com/android/server/stats/StatsCompanionService.java
+++ b/apex/service/java/com/android/server/stats/StatsCompanionService.java
@@ -54,6 +54,7 @@
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -194,40 +195,38 @@
                 int numRecords = 0;
                 // Add in all the apps for every user/profile.
                 for (UserHandle userHandle : users) {
-                    List<PackageInfo> pi =
-                            pm.getInstalledPackagesAsUser(PackageManager.MATCH_UNINSTALLED_PACKAGES
-                                            | PackageManager.MATCH_ANY_USER
-                                            | PackageManager.MATCH_APEX,
-                                    userHandle.getIdentifier());
-                    for (int j = 0; j < pi.size(); j++) {
-                        if (pi.get(j).applicationInfo != null) {
+                    List<PackageInfo> packagesPlusApex = getAllPackagesWithApex(pm, userHandle);
+                    for (int j = 0; j < packagesPlusApex.size(); j++) {
+                        if (packagesPlusApex.get(j).applicationInfo != null) {
                             String installer;
                             try {
-                                installer = pm.getInstallerPackageName(pi.get(j).packageName);
+                                installer = pm.getInstallerPackageName(
+                                        packagesPlusApex.get(j).packageName);
                             } catch (IllegalArgumentException e) {
                                 installer = "";
                             }
                             long applicationInfoToken =
                                     output.start(ProtoOutputStream.FIELD_TYPE_MESSAGE
                                             | ProtoOutputStream.FIELD_COUNT_REPEATED
-                                                    | APPLICATION_INFO_FIELD_ID);
+                                            | APPLICATION_INFO_FIELD_ID);
                             output.write(ProtoOutputStream.FIELD_TYPE_INT32
-                                    | ProtoOutputStream.FIELD_COUNT_SINGLE | UID_FIELD_ID,
-                                            pi.get(j).applicationInfo.uid);
+                                            | ProtoOutputStream.FIELD_COUNT_SINGLE | UID_FIELD_ID,
+                                    packagesPlusApex.get(j).applicationInfo.uid);
                             output.write(ProtoOutputStream.FIELD_TYPE_INT64
-                                    | ProtoOutputStream.FIELD_COUNT_SINGLE
-                                            | VERSION_FIELD_ID, pi.get(j).getLongVersionCode());
+                                            | ProtoOutputStream.FIELD_COUNT_SINGLE
+                                            | VERSION_FIELD_ID,
+                                    packagesPlusApex.get(j).getLongVersionCode());
+                            output.write(ProtoOutputStream.FIELD_TYPE_STRING
+                                            | ProtoOutputStream.FIELD_COUNT_SINGLE
+                                            | VERSION_STRING_FIELD_ID,
+                                    packagesPlusApex.get(j).versionName);
                             output.write(ProtoOutputStream.FIELD_TYPE_STRING
                                     | ProtoOutputStream.FIELD_COUNT_SINGLE
-                                    | VERSION_STRING_FIELD_ID,
-                                            pi.get(j).versionName);
+                                    | PACKAGE_NAME_FIELD_ID, packagesPlusApex.get(j).packageName);
                             output.write(ProtoOutputStream.FIELD_TYPE_STRING
-                                    | ProtoOutputStream.FIELD_COUNT_SINGLE
-                                            | PACKAGE_NAME_FIELD_ID, pi.get(j).packageName);
-                            output.write(ProtoOutputStream.FIELD_TYPE_STRING
-                                    | ProtoOutputStream.FIELD_COUNT_SINGLE
+                                            | ProtoOutputStream.FIELD_COUNT_SINGLE
                                             | INSTALLER_FIELD_ID,
-                                                    installer == null ? "" : installer);
+                                    installer == null ? "" : installer);
                             numRecords++;
                             output.end(applicationInfoToken);
                         }
@@ -245,6 +244,26 @@
         });
     }
 
+    private static List<PackageInfo> getAllPackagesWithApex(PackageManager pm,
+            UserHandle userHandle) {
+        // We want all the uninstalled packages because uninstalled package uids can still be logged
+        // to statsd.
+        List<PackageInfo> allPackages = new ArrayList<>(
+                pm.getInstalledPackagesAsUser(PackageManager.MATCH_UNINSTALLED_PACKAGES
+                                | PackageManager.MATCH_ANY_USER,
+                        userHandle.getIdentifier()));
+        // We make a second query to package manager for the apex modules because package manager
+        // returns both installed and uninstalled apexes with
+        // PackageManager.MATCH_UNINSTALLED_PACKAGES flag. We only want active apexes because
+        // inactive apexes can conflict with active ones.
+        for (PackageInfo packageInfo : pm.getInstalledPackages(PackageManager.MATCH_APEX)) {
+            if (packageInfo.isApex) {
+                allPackages.add(packageInfo);
+            }
+        }
+        return allPackages;
+    }
+
     private static class WakelockThread extends Thread {
         private final PowerManager.WakeLock mWl;
         private final Runnable mRunnable;
diff --git a/apex/service/java/com/android/server/stats/StatsManagerService.java b/apex/service/java/com/android/server/stats/StatsManagerService.java
index 97846f2..1e3846b 100644
--- a/apex/service/java/com/android/server/stats/StatsManagerService.java
+++ b/apex/service/java/com/android/server/stats/StatsManagerService.java
@@ -27,6 +27,7 @@
 import android.os.IPullAtomCallback;
 import android.os.IStatsManagerService;
 import android.os.IStatsd;
+import android.os.PowerManager;
 import android.os.Process;
 import android.os.RemoteException;
 import android.util.ArrayMap;
@@ -412,8 +413,13 @@
     @Override
     public byte[] getData(long key, String packageName) throws IllegalStateException {
         enforceDumpAndUsageStatsPermission(packageName);
+        PowerManager powerManager = (PowerManager)
+                mContext.getSystemService(Context.POWER_SERVICE);
+        PowerManager.WakeLock wl = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+                /*tag=*/ StatsManagerService.class.getCanonicalName());
         int callingUid = Binder.getCallingUid();
         final long token = Binder.clearCallingIdentity();
+        wl.acquire();
         try {
             IStatsd statsd = waitForStatsd();
             if (statsd != null) {
@@ -423,6 +429,7 @@
             Log.e(TAG, "Failed to getData with statsd");
             throw new IllegalStateException(e.getMessage(), e);
         } finally {
+            wl.release();
             Binder.restoreCallingIdentity(token);
         }
         throw new IllegalStateException("Failed to connect to statsd to getData");
diff --git a/bin/Android.bp b/bin/Android.bp
index 529eb4f..5fb1c1a 100644
--- a/bin/Android.bp
+++ b/bin/Android.bp
@@ -40,15 +40,16 @@
         "src/external/StatsPullerManager.cpp",
         "src/external/TrainInfoPuller.cpp",
         "src/FieldValue.cpp",
+        "src/flags/flags.cpp",
         "src/guardrail/StatsdStats.cpp",
         "src/hash.cpp",
         "src/HashableDimensionKey.cpp",
         "src/logd/LogEvent.cpp",
         "src/logd/LogEventQueue.cpp",
-        "src/matchers/CombinationLogMatchingTracker.cpp",
+        "src/matchers/CombinationAtomMatchingTracker.cpp",
         "src/matchers/EventMatcherWizard.cpp",
         "src/matchers/matcher_util.cpp",
-        "src/matchers/SimpleLogMatchingTracker.cpp",
+        "src/matchers/SimpleAtomMatchingTracker.cpp",
         "src/metadata_util.cpp",
         "src/metrics/CountMetricProducer.cpp",
         "src/metrics/duration_helper/MaxDurationTracker.cpp",
@@ -57,8 +58,9 @@
         "src/metrics/EventMetricProducer.cpp",
         "src/metrics/GaugeMetricProducer.cpp",
         "src/metrics/MetricProducer.cpp",
-        "src/metrics/metrics_manager_util.cpp",
         "src/metrics/MetricsManager.cpp",
+        "src/metrics/parsing_utils/config_update_utils.cpp",
+        "src/metrics/parsing_utils/metrics_manager_util.cpp",
         "src/metrics/ValueMetricProducer.cpp",
         "src/packages/UidMap.cpp",
         "src/shell/shell_config.proto",
@@ -91,6 +93,7 @@
         "libstatslog_statsd",
         "libsysutils",
         "libutils",
+        "server_configurable_flags",
         "statsd-aidl-ndk_platform",
     ],
     shared_libs: [
@@ -143,7 +146,8 @@
     export_generated_headers: ["statslog_statsdtest.h"],
     shared_libs: [
         "libstatssocket",
-    ]
+        "libstatspull",
+    ],
 }
 
 cc_library_static {
@@ -157,7 +161,11 @@
     ],
     shared_libs: [
         "libstatssocket",
-    ]
+        "libstatspull",
+    ],
+    export_shared_lib_headers: [
+        "libstatspull",
+    ],
 }
 
 // =========
@@ -188,10 +196,6 @@
             //    address: true,
             //},
         },
-        debuggable: {
-            // Add a flag to enable stats log printing from statsd on debug builds.
-            cflags: ["-DVERY_VERBOSE_PRINTING"],
-        },
     },
 
     proto: {
@@ -263,6 +267,8 @@
         "tests/e2e/Anomaly_duration_sum_e2e_test.cpp",
         "tests/e2e/Attribution_e2e_test.cpp",
         "tests/e2e/ConfigTtl_e2e_test.cpp",
+        "tests/e2e/ConfigUpdate_e2e_ab_test.cpp",
+        "tests/e2e/ConfigUpdate_e2e_test.cpp",
         "tests/e2e/CountMetric_e2e_test.cpp",
         "tests/e2e/DurationMetric_e2e_test.cpp",
         "tests/e2e/GaugeMetric_e2e_pull_test.cpp",
@@ -292,6 +298,8 @@
         "tests/metrics/metrics_test_helper.cpp",
         "tests/metrics/OringDurationTracker_test.cpp",
         "tests/metrics/ValueMetricProducer_test.cpp",
+        "tests/metrics/parsing_utils/config_update_utils_test.cpp",
+        "tests/metrics/parsing_utils/metrics_manager_util_test.cpp",
         "tests/MetricsManager_test.cpp",
         "tests/shell/ShellSubscriber_test.cpp",
         "tests/state/StateTracker_test.cpp",
@@ -405,6 +413,33 @@
     },
 }
 
+java_library {
+    name: "statsdprotonano",
+    sdk_version: "9",
+    proto: {
+        type: "nano",
+        output_params: ["store_unknown_fields=true"],
+        include_dirs: [
+            "external/protobuf/src",
+            "frameworks/proto_logging/stats",
+        ],
+    },
+    srcs: [
+        ":libstats_atoms_proto",
+        "src/shell/shell_config.proto",
+        "src/shell/shell_data.proto",
+        "src/stats_log.proto",
+        "src/statsd_config.proto",
+    ],
+    static_libs: [
+        "platformprotosnano",
+    ],
+    // Protos have lots of MissingOverride and similar.
+    errorprone: {
+        javacflags: ["-XepDisableAllChecks"],
+    },
+}
+
 // Filegroup for statsd config proto definition.
 filegroup {
     name: "statsd-config-proto-def",
diff --git a/bin/TEST_MAPPING b/bin/TEST_MAPPING
index 8dee073..a7a4cf1 100644
--- a/bin/TEST_MAPPING
+++ b/bin/TEST_MAPPING
@@ -3,5 +3,15 @@
     {
       "name" : "statsd_test"
     }
+  ],
+
+  "postsubmit" : [
+    {
+      "name" : "CtsStatsdHostTestCases"
+    },
+    {
+      "name" : "GtsStatsdHostTestCases"
+    }
   ]
-}
\ No newline at end of file
+
+}
diff --git a/bin/src/StatsLogProcessor.cpp b/bin/src/StatsLogProcessor.cpp
index 6de77b9..fa69c45 100644
--- a/bin/src/StatsLogProcessor.cpp
+++ b/bin/src/StatsLogProcessor.cpp
@@ -24,12 +24,13 @@
 #include <packages/modules/StatsD/bin/src/active_config_list.pb.h>
 #include <packages/modules/StatsD/bin/src/experiment_ids.pb.h>
 
+#include "StatsService.h"
 #include "android-base/stringprintf.h"
 #include "external/StatsPullerManager.h"
+#include "flags/flags.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"
@@ -408,11 +409,9 @@
         onWatchdogRollbackOccurredLocked(event);
     }
 
-#ifdef VERY_VERBOSE_PRINTING
     if (mPrintAllLogs) {
         ALOGI("%s", event->ToString().c_str());
     }
-#endif
     resetIfConfigTtlExpiredLocked(eventElapsedTimeNs);
 
     // Hard-coded logic to update the isolated uid's in the uid-map.
@@ -525,22 +524,37 @@
                                         const StatsdConfig& config) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
     WriteDataToDiskLocked(key, timestampNs, CONFIG_UPDATED, NO_TIME_CONSTRAINTS);
-    OnConfigUpdatedLocked(timestampNs, key, config);
+    bool modularUpdate = getFlagBool(PARTIAL_CONFIG_UPDATE_FLAG, "false");
+    OnConfigUpdatedLocked(timestampNs, key, config, modularUpdate);
 }
 
-void StatsLogProcessor::OnConfigUpdatedLocked(
-        const int64_t timestampNs, const ConfigKey& key, const StatsdConfig& config) {
+void StatsLogProcessor::OnConfigUpdatedLocked(const int64_t timestampNs, const ConfigKey& key,
+                                              const StatsdConfig& config, bool modularUpdate) {
     VLOG("Updated configuration for key %s", key.ToString().c_str());
-    sp<MetricsManager> newMetricsManager =
-            new MetricsManager(key, config, mTimeBaseNs, timestampNs, mUidMap, mPullerManager,
-                               mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
-    if (newMetricsManager->isConfigValid()) {
-        newMetricsManager->init();
-        mUidMap->OnConfigUpdated(key);
-        newMetricsManager->refreshTtl(timestampNs);
-        mMetricsManagers[key] = newMetricsManager;
-        VLOG("StatsdConfig valid");
+    // Create new config if this is not a modular update or if this is a new config.
+    const auto& it = mMetricsManagers.find(key);
+    bool configValid = false;
+    if (!modularUpdate || it == mMetricsManagers.end()) {
+        sp<MetricsManager> newMetricsManager =
+                new MetricsManager(key, config, mTimeBaseNs, timestampNs, mUidMap, mPullerManager,
+                                   mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
+        configValid = newMetricsManager->isConfigValid();
+        if (configValid) {
+            newMetricsManager->init();
+            mUidMap->OnConfigUpdated(key);
+            newMetricsManager->refreshTtl(timestampNs);
+            mMetricsManagers[key] = newMetricsManager;
+            VLOG("StatsdConfig valid");
+        }
     } else {
+        // Preserve the existing MetricsManager, update necessary components and metadata in place.
+        configValid = it->second->updateConfig(config, mTimeBaseNs, timestampNs,
+                                               mAnomalyAlarmMonitor, mPeriodicAlarmMonitor);
+        if (configValid) {
+            mUidMap->OnConfigUpdated(key);
+        }
+    }
+    if (!configValid) {
         // If there is any error in the config, don't use it.
         // Remove any existing config with the same key.
         ALOGE("StatsdConfig NOT valid");
@@ -706,7 +720,8 @@
     for (const auto& key : configs) {
         StatsdConfig config;
         if (StorageManager::readConfigFromDisk(key, &config)) {
-            OnConfigUpdatedLocked(timestampNs, key, config);
+            // Force a full update when resetting a config.
+            OnConfigUpdatedLocked(timestampNs, key, config, /*modularUpdate=*/false);
             StatsdStats::getInstance().noteConfigReset(key);
         } else {
             ALOGE("Failed to read backup config from disk for : %s", key.ToString().c_str());
diff --git a/bin/src/StatsLogProcessor.h b/bin/src/StatsLogProcessor.h
index 099ad4b..4404c18 100644
--- a/bin/src/StatsLogProcessor.h
+++ b/bin/src/StatsLogProcessor.h
@@ -134,10 +134,8 @@
     int64_t getLastReportTimeNs(const ConfigKey& key);
 
     inline void setPrintLogs(bool enabled) {
-#ifdef VERY_VERBOSE_PRINTING
         std::lock_guard<std::mutex> lock(mMetricsMutex);
         mPrintAllLogs = enabled;
-#endif
     }
 
     // Add a specific config key to the possible configs to dump ASAP.
@@ -189,8 +187,8 @@
 
     void resetIfConfigTtlExpiredLocked(const int64_t timestampNs);
 
-    void OnConfigUpdatedLocked(
-        const int64_t currentTimestampNs, const ConfigKey& key, const StatsdConfig& config);
+    void OnConfigUpdatedLocked(const int64_t currentTimestampNs, const ConfigKey& key,
+                               const StatsdConfig& config, bool modularUpdate);
 
     void GetActiveConfigsLocked(const int uid, vector<int64_t>& outActiveConfigs);
 
@@ -292,9 +290,7 @@
     // The time for the next anomaly alarm for alerts.
     int64_t mNextAnomalyAlarmTime = 0;
 
-#ifdef VERY_VERBOSE_PRINTING
     bool mPrintAllLogs = false;
-#endif
 
     FRIEND_TEST(StatsLogProcessorTest, TestOutOfOrderLogs);
     FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
@@ -342,6 +338,10 @@
     FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithSameDeactivation);
     FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
 
+    FRIEND_TEST(ConfigUpdateE2eAbTest, TestHashStrings);
+    FRIEND_TEST(ConfigUpdateE2eAbTest, TestUidMapVersionStringInstaller);
+    FRIEND_TEST(ConfigUpdateE2eAbTest, TestConfigTtl);
+
     FRIEND_TEST(CountMetricE2eTest, TestInitialConditionChanges);
     FRIEND_TEST(CountMetricE2eTest, TestSlicedState);
     FRIEND_TEST(CountMetricE2eTest, TestSlicedStateWithMap);
diff --git a/bin/src/StatsService.cpp b/bin/src/StatsService.cpp
index ca8326c..94b1736 100644
--- a/bin/src/StatsService.cpp
+++ b/bin/src/StatsService.cpp
@@ -480,7 +480,8 @@
     dprintf(out, "  Clear cached puller data.\n");
     dprintf(out, "\n");
     dprintf(out, "usage: adb shell cmd stats print-logs\n");
-    dprintf(out, "      Only works on eng build\n");
+    dprintf(out, "  Requires root privileges.\n");
+    dprintf(out, "  Can be disabled by calling adb shell cmd stats print-logs 0\n");
 }
 
 status_t StatsService::cmd_trigger_broadcast(int out, Vector<String8>& args) {
@@ -861,18 +862,19 @@
 }
 
 status_t StatsService::cmd_print_logs(int out, const Vector<String8>& args) {
-    VLOG("StatsService::cmd_print_logs with Pid %i, Uid %i", AIBinder_getCallingPid(),
-         AIBinder_getCallingUid());
-    if (checkPermission(kPermissionDump)) {
-        bool enabled = true;
-        if (args.size() >= 2) {
-            enabled = atoi(args[1].c_str()) != 0;
-        }
-        mProcessor->setPrintLogs(enabled);
-        return NO_ERROR;
-    } else {
+    Status status = checkUid(AID_ROOT);
+    if (!status.isOk()) {
         return PERMISSION_DENIED;
     }
+
+    VLOG("StatsService::cmd_print_logs with pid %i, uid %i", AIBinder_getCallingPid(),
+         AIBinder_getCallingUid());
+    bool enabled = true;
+    if (args.size() >= 2) {
+        enabled = atoi(args[1].c_str()) != 0;
+    }
+    mProcessor->setPrintLogs(enabled);
+    return NO_ERROR;
 }
 
 bool StatsService::getUidFromArgs(const Vector<String8>& args, size_t uidArgIndex, int32_t& uid) {
diff --git a/bin/src/anomaly/AlarmTracker.h b/bin/src/anomaly/AlarmTracker.h
index aa37192..23defc9 100644
--- a/bin/src/anomaly/AlarmTracker.h
+++ b/bin/src/anomaly/AlarmTracker.h
@@ -73,6 +73,7 @@
 
     FRIEND_TEST(AlarmTrackerTest, TestTriggerTimestamp);
     FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateAlarms);
 };
 
 }  // namespace statsd
diff --git a/bin/src/anomaly/AnomalyTracker.cpp b/bin/src/anomaly/AnomalyTracker.cpp
index 619752c..6aa410b 100644
--- a/bin/src/anomaly/AnomalyTracker.cpp
+++ b/bin/src/anomaly/AnomalyTracker.cpp
@@ -37,14 +37,6 @@
 AnomalyTracker::AnomalyTracker(const Alert& alert, const ConfigKey& configKey)
         : mAlert(alert), mConfigKey(configKey), mNumOfPastBuckets(mAlert.num_buckets() - 1) {
     VLOG("AnomalyTracker() called");
-    if (mAlert.num_buckets() <= 0) {
-        ALOGE("Cannot create AnomalyTracker with %lld buckets", (long long)mAlert.num_buckets());
-        return;
-    }
-    if (!mAlert.has_trigger_if_sum_gt()) {
-        ALOGE("Cannot create AnomalyTracker without threshold");
-        return;
-    }
     resetStorage();  // initialization
 }
 
@@ -52,6 +44,10 @@
     VLOG("~AnomalyTracker() called");
 }
 
+void AnomalyTracker::onConfigUpdated() {
+    mSubscriptions.clear();
+}
+
 void AnomalyTracker::resetStorage() {
     VLOG("resetStorage() called.");
     mPastBuckets.clear();
@@ -259,6 +255,15 @@
     return false;
 }
 
+std::pair<bool, uint64_t> AnomalyTracker::getProtoHash() const {
+    string serializedAlert;
+    if (!mAlert.SerializeToString(&serializedAlert)) {
+        ALOGW("Unable to serialize alert %lld", (long long)mAlert.id());
+        return {false, 0};
+    }
+    return {true, Hash64(serializedAlert)};
+}
+
 void AnomalyTracker::informSubscribers(const MetricDimensionKey& key, int64_t metric_id,
                                        int64_t metricValue) {
     triggerSubscribers(mAlert.id(), metric_id, key, metricValue, mConfigKey, mSubscriptions);
diff --git a/bin/src/anomaly/AnomalyTracker.h b/bin/src/anomaly/AnomalyTracker.h
index e5893a4..7839e06 100644
--- a/bin/src/anomaly/AnomalyTracker.h
+++ b/bin/src/anomaly/AnomalyTracker.h
@@ -16,15 +16,15 @@
 
 #pragma once
 
-#include <stdlib.h>
-
 #include <gtest/gtest_prod.h>
+#include <stdlib.h>
 #include <utils/RefBase.h>
 
 #include "AlarmMonitor.h"
 #include "config/ConfigKey.h"
-#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"  // Alert
+#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"    // Alert
 #include "packages/modules/StatsD/bin/src/statsd_metadata.pb.h"  // AlertMetadata
+#include "hash.h"
 #include "stats_util.h"  // HashableDimensionKey and DimToValMap
 
 namespace android {
@@ -41,6 +41,9 @@
 
     virtual ~AnomalyTracker();
 
+    // Reset appropriate state on a config update. Clear subscriptions so they can be reset.
+    void onConfigUpdated();
+
     // Add subscriptions that depend on this alert.
     void addSubscription(const Subscription& subscription) {
         mSubscriptions.push_back(subscription);
@@ -106,6 +109,26 @@
         return mNumOfPastBuckets;
     }
 
+    std::pair<bool, uint64_t> getProtoHash() const;
+
+    // Sets an alarm for the given timestamp.
+    // Replaces previous alarm if one already exists.
+    virtual void startAlarm(const MetricDimensionKey& dimensionKey, const int64_t& eventTime) {
+        return;  // The base AnomalyTracker class doesn't have alarms.
+    }
+
+    // Stops the alarm.
+    // If it should have already fired, but hasn't yet (e.g. because the AlarmManager is delayed),
+    // declare the anomaly now.
+    virtual void stopAlarm(const MetricDimensionKey& dimensionKey, const int64_t& timestampNs) {
+        return;  // The base AnomalyTracker class doesn't have alarms.
+    }
+
+    // Stop all the alarms owned by this tracker. Does not declare any anomalies.
+    virtual void cancelAllAlarms() {
+        return;  // The base AnomalyTracker class doesn't have alarms.
+    }
+
     // Declares an anomaly for each alarm in firedAlarms that belongs to this AnomalyTracker,
     // and removes it from firedAlarms. Does NOT remove the alarm from the AlarmMonitor.
     virtual void informAlarmsFired(const int64_t& timestampNs,
@@ -197,6 +220,8 @@
     FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_single_bucket);
     FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_multiple_buckets);
     FRIEND_TEST(AnomalyDetectionE2eTest, TestDurationMetric_SUM_long_refractory_period);
+
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts);
 };
 
 }  // namespace statsd
diff --git a/bin/src/anomaly/DurationAnomalyTracker.h b/bin/src/anomaly/DurationAnomalyTracker.h
index 686d8f9..4641914 100644
--- a/bin/src/anomaly/DurationAnomalyTracker.h
+++ b/bin/src/anomaly/DurationAnomalyTracker.h
@@ -34,15 +34,15 @@
 
     // Sets an alarm for the given timestamp.
     // Replaces previous alarm if one already exists.
-    void startAlarm(const MetricDimensionKey& dimensionKey, const int64_t& eventTime);
+    void startAlarm(const MetricDimensionKey& dimensionKey, const int64_t& eventTime) override;
 
     // Stops the alarm.
     // If it should have already fired, but hasn't yet (e.g. because the AlarmManager is delayed),
     // declare the anomaly now.
-    void stopAlarm(const MetricDimensionKey& dimensionKey, const int64_t& timestampNs);
+    void stopAlarm(const MetricDimensionKey& dimensionKey, const int64_t& timestampNs) override;
 
     // Stop all the alarms owned by this tracker. Does not declare any anomalies.
-    void cancelAllAlarms();
+    void cancelAllAlarms() override;
 
     // Declares an anomaly for each alarm in firedAlarms that belongs to this DurationAnomalyTracker
     // and removes it from firedAlarms. The AlarmMonitor is not informed.
diff --git a/bin/src/condition/CombinationConditionTracker.cpp b/bin/src/condition/CombinationConditionTracker.cpp
index e9875ba..4574b2e 100644
--- a/bin/src/condition/CombinationConditionTracker.cpp
+++ b/bin/src/condition/CombinationConditionTracker.cpp
@@ -25,8 +25,9 @@
 using std::unordered_map;
 using std::vector;
 
-CombinationConditionTracker::CombinationConditionTracker(const int64_t& id, const int index)
-    : ConditionTracker(id, index) {
+CombinationConditionTracker::CombinationConditionTracker(const int64_t& id, const int index,
+                                                         const uint64_t protoHash)
+    : ConditionTracker(id, index, protoHash) {
     VLOG("creating CombinationConditionTracker %lld", (long long)mConditionId);
 }
 
@@ -38,9 +39,23 @@
                                        const vector<sp<ConditionTracker>>& allConditionTrackers,
                                        const unordered_map<int64_t, int>& conditionIdIndexMap,
                                        vector<bool>& stack,
-                                       vector<ConditionState>& initialConditionCache) {
+                                       vector<ConditionState>& conditionCache) {
     VLOG("Combination predicate init() %lld", (long long)mConditionId);
     if (mInitialized) {
+        // All the children are guaranteed to be initialized, but the recursion is needed to
+        // fill the conditionCache properly, since another combination condition or metric
+        // might rely on this. The recursion is needed to compute the current condition.
+
+        // Init is called instead of isConditionMet so that the ConditionKey can be filled with the
+        // default key for sliced conditions, since we do not know all indirect descendants here.
+        for (const int childIndex : mChildren) {
+            if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
+                allConditionTrackers[childIndex]->init(allConditionConfig, allConditionTrackers,
+                                                       conditionIdIndexMap, stack, conditionCache);
+            }
+        }
+        conditionCache[mIndex] =
+                evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
         return true;
     }
 
@@ -74,9 +89,8 @@
             return false;
         }
 
-        bool initChildSucceeded =
-                childTracker->init(allConditionConfig, allConditionTrackers, conditionIdIndexMap,
-                                   stack, initialConditionCache);
+        bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers,
+                                                     conditionIdIndexMap, stack, conditionCache);
 
         if (!initChildSucceeded) {
             ALOGW("Child initialization failed %lld ", (long long)child);
@@ -92,14 +106,14 @@
             mUnSlicedChildren.push_back(childIndex);
         }
         mChildren.push_back(childIndex);
-        mTrackerIndex.insert(childTracker->getLogTrackerIndex().begin(),
-                             childTracker->getLogTrackerIndex().end());
+        mTrackerIndex.insert(childTracker->getAtomMatchingTrackerIndex().begin(),
+                             childTracker->getAtomMatchingTrackerIndex().end());
     }
 
-    mUnSlicedPartCondition = evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation,
-                                                          initialConditionCache);
-    initialConditionCache[mIndex] =
-            evaluateCombinationCondition(mChildren, mLogicalOperation, initialConditionCache);
+    mUnSlicedPartCondition =
+            evaluateCombinationCondition(mUnSlicedChildren, mLogicalOperation, conditionCache);
+    conditionCache[mIndex] =
+            evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
 
     // unmark this node in the recursion stack.
     stack[mIndex] = false;
@@ -109,6 +123,49 @@
     return true;
 }
 
+bool CombinationConditionTracker::onConfigUpdated(
+        const vector<Predicate>& allConditionProtos, const int index,
+        const vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& conditionTrackerMap) {
+    ConditionTracker::onConfigUpdated(allConditionProtos, index, allConditionTrackers,
+                                      atomMatchingTrackerMap, conditionTrackerMap);
+    mTrackerIndex.clear();
+    mChildren.clear();
+    mUnSlicedChildren.clear();
+    mSlicedChildren.clear();
+    Predicate_Combination combinationCondition = allConditionProtos[mIndex].combination();
+
+    for (const int64_t child : combinationCondition.predicate()) {
+        const auto& it = conditionTrackerMap.find(child);
+
+        if (it == conditionTrackerMap.end()) {
+            ALOGW("Predicate %lld not found in the config", (long long)child);
+            return false;
+        }
+
+        int childIndex = it->second;
+        const sp<ConditionTracker>& childTracker = allConditionTrackers[childIndex];
+
+        // Ensures that the child's tracker indices are updated.
+        if (!childTracker->onConfigUpdated(allConditionProtos, childIndex, allConditionTrackers,
+                                           atomMatchingTrackerMap, conditionTrackerMap)) {
+            ALOGW("Child update failed %lld ", (long long)child);
+            return false;
+        }
+
+        if (allConditionTrackers[childIndex]->isSliced()) {
+            mSlicedChildren.push_back(childIndex);
+        } else {
+            mUnSlicedChildren.push_back(childIndex);
+        }
+        mChildren.push_back(childIndex);
+        mTrackerIndex.insert(childTracker->getAtomMatchingTrackerIndex().begin(),
+                             childTracker->getAtomMatchingTrackerIndex().end());
+    }
+    return true;
+}
+
 void CombinationConditionTracker::isConditionMet(
         const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
         const bool isPartialLink,
diff --git a/bin/src/condition/CombinationConditionTracker.h b/bin/src/condition/CombinationConditionTracker.h
index 0c598c2..329f707 100644
--- a/bin/src/condition/CombinationConditionTracker.h
+++ b/bin/src/condition/CombinationConditionTracker.h
@@ -24,16 +24,21 @@
 namespace os {
 namespace statsd {
 
-class CombinationConditionTracker : public virtual ConditionTracker {
+class CombinationConditionTracker : public ConditionTracker {
 public:
-    CombinationConditionTracker(const int64_t& id, const int index);
+    CombinationConditionTracker(const int64_t& id, const int index, const uint64_t protoHash);
 
     ~CombinationConditionTracker();
 
     bool init(const std::vector<Predicate>& allConditionConfig,
               const std::vector<sp<ConditionTracker>>& allConditionTrackers,
               const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack,
-              std::vector<ConditionState>& initialConditionCache) override;
+              std::vector<ConditionState>& conditionCache) override;
+
+    bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index,
+                         const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+                         const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                         const std::unordered_map<int64_t, int>& conditionTrackerMap) override;
 
     void evaluateCondition(const LogEvent& event,
                            const std::vector<MatchingState>& eventMatcherValues,
@@ -80,16 +85,14 @@
         const std::vector<sp<ConditionTracker>>& allConditions,
         const vector<Matcher>& dimensions) const override;
 
-    void getTrueSlicedDimensions(
-            const std::vector<sp<ConditionTracker>>& allConditions,
-            std::set<HashableDimensionKey>* dimensions) const override {
+    const std::map<HashableDimensionKey, int>* getSlicedDimensionMap(
+            const std::vector<sp<ConditionTracker>>& allConditions) const override {
         if (mSlicedChildren.size() == 1) {
-            return allConditions[mSlicedChildren.front()]->getTrueSlicedDimensions(
-                allConditions, dimensions);
+            return allConditions[mSlicedChildren.front()]->getSlicedDimensionMap(allConditions);
         }
+        return nullptr;
     }
 
-
 private:
     LogicalOperation mLogicalOperation;
 
@@ -102,6 +105,7 @@
     std::vector<int> mSlicedChildren;
     std::vector<int> mUnSlicedChildren;
 
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions);
 };
 
 }  // namespace statsd
diff --git a/bin/src/condition/ConditionTimer.h b/bin/src/condition/ConditionTimer.h
index 442bc11..1fbe252 100644
--- a/bin/src/condition/ConditionTimer.h
+++ b/bin/src/condition/ConditionTimer.h
@@ -36,7 +36,7 @@
 public:
     explicit ConditionTimer(bool initCondition, int64_t bucketStartNs) : mCondition(initCondition) {
         if (initCondition) {
-            mLastConditionTrueTimestampNs = bucketStartNs;
+            mLastConditionChangeTimestampNs = bucketStartNs;
         }
     };
 
@@ -44,21 +44,46 @@
     // When a new bucket is created, this value will be reset to 0.
     int64_t mTimerNs = 0;
 
-    // Last elapsed real timestamp when condition turned to true
-    // When a new bucket is created and the condition is true, then the timestamp is set
-    // to be the bucket start timestamp.
-    int64_t mLastConditionTrueTimestampNs = 0;
+    // Last elapsed real timestamp when condition changed.
+    int64_t mLastConditionChangeTimestampNs = 0;
 
     bool mCondition = false;
 
     int64_t newBucketStart(int64_t nextBucketStartNs) {
         if (mCondition) {
-            mTimerNs += (nextBucketStartNs - mLastConditionTrueTimestampNs);
-            mLastConditionTrueTimestampNs = nextBucketStartNs;
+            // Normally, the next bucket happens after the last condition
+            // change. In this case, add the time between the condition becoming
+            // true to the next bucket start time.
+            // Otherwise, the next bucket start time is before the last
+            // condition change time, this means that the condition was false at
+            // the bucket boundary before the condition became true, so the
+            // timer should not get updated and the last condition change time
+            // remains as is.
+            if (nextBucketStartNs >= mLastConditionChangeTimestampNs) {
+                mTimerNs += (nextBucketStartNs - mLastConditionChangeTimestampNs);
+                mLastConditionChangeTimestampNs = nextBucketStartNs;
+            }
+        } else if (mLastConditionChangeTimestampNs > nextBucketStartNs) {
+            // The next bucket start time is before the last condition change
+            // time, this means that the condition was true at the bucket
+            // boundary before the condition became false, so adjust the timer
+            // to match how long the condition was true to the bucket boundary.
+            // This means remove the amount the condition stayed true in the
+            // next bucket from the current bucket.
+            mTimerNs -= (mLastConditionChangeTimestampNs - nextBucketStartNs);
         }
 
         int64_t temp = mTimerNs;
         mTimerNs = 0;
+
+        if (!mCondition && (mLastConditionChangeTimestampNs > nextBucketStartNs)) {
+            // The next bucket start time is before the last condition change
+            // time, this means that the condition was true at the bucket
+            // boundary and remained true in the next bucket up to the condition
+            // change to false, so adjust the timer to match how long the
+            // condition stayed true in the next bucket (now the current bucket).
+            mTimerNs = mLastConditionChangeTimestampNs - nextBucketStartNs;
+        }
         return temp;
     }
 
@@ -67,11 +92,10 @@
             return;
         }
         mCondition = newCondition;
-        if (newCondition) {
-            mLastConditionTrueTimestampNs = timestampNs;
-        } else {
-            mTimerNs += (timestampNs - mLastConditionTrueTimestampNs);
+        if (newCondition == false) {
+            mTimerNs += (timestampNs - mLastConditionChangeTimestampNs);
         }
+        mLastConditionChangeTimestampNs = timestampNs;
     }
 
     FRIEND_TEST(ConditionTimerTest, TestTimer_Inital_False);
@@ -80,4 +104,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/bin/src/condition/ConditionTracker.h b/bin/src/condition/ConditionTracker.h
index b8448f7..84095f0 100644
--- a/bin/src/condition/ConditionTracker.h
+++ b/bin/src/condition/ConditionTracker.h
@@ -18,7 +18,7 @@
 
 #include "condition/condition_util.h"
 #include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
-#include "matchers/LogMatchingTracker.h"
+#include "matchers/AtomMatchingTracker.h"
 #include "matchers/matcher_util.h"
 
 #include <utils/RefBase.h>
@@ -31,38 +31,59 @@
 
 class ConditionTracker : public virtual RefBase {
 public:
-    ConditionTracker(const int64_t& id, const int index)
+    ConditionTracker(const int64_t& id, const int index, const uint64_t protoHash)
         : mConditionId(id),
           mIndex(index),
           mInitialized(false),
           mTrackerIndex(),
           mUnSlicedPartCondition(ConditionState::kUnknown),
-          mSliced(false){};
+          mSliced(false),
+          mProtoHash(protoHash){};
 
     virtual ~ConditionTracker(){};
 
-    inline const int64_t& getId() { return mConditionId; }
-
     // Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also
     // be done in the constructor, but we do it separately because (1) easy to return a bool to
     // indicate whether the initialization is successful. (2) makes unit test easier.
+    // This function can also be called on config updates, in which case it does nothing other than
+    // fill the condition cache with the current condition.
     // allConditionConfig: the list of all Predicate config from statsd_config.
     // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also
-    //                       need to call init() on children conditions)
+    //                       need to call init() on child conditions)
     // conditionIdIndexMap: the mapping from condition id to its index.
     // stack: a bit map to keep track which nodes have been visited on the stack in the recursion.
-    // initialConditionCache: tracks initial conditions of all ConditionTrackers.
+    // conditionCache: tracks initial conditions of all ConditionTrackers. returns the
+    //                        current condition if called on a config update.
     virtual bool init(const std::vector<Predicate>& allConditionConfig,
                       const std::vector<sp<ConditionTracker>>& allConditionTrackers,
                       const std::unordered_map<int64_t, int>& conditionIdIndexMap,
-                      std::vector<bool>& stack,
-                      std::vector<ConditionState>& initialConditionCache) = 0;
+                      std::vector<bool>& stack, std::vector<ConditionState>& conditionCache) = 0;
+
+    // Update appropriate state on config updates. Primarily, all indices need to be updated.
+    // This predicate and all of its children are guaranteed to be preserved across the update.
+    // This function is recursive and will call onConfigUpdated on child conditions. It does not
+    // manage cycle detection since all preserved conditions should not have any cycles.
+    //
+    // allConditionProtos: the new predicates.
+    // index: the new index of this tracker in allConditionProtos and allConditionTrackers.
+    // allConditionTrackers: the list of all ConditionTrackers (this is needed because we may also
+    //                       need to call onConfigUpdated() on child conditions)
+    // atomMatchingTrackerMap: map of atom matcher id to index after the config update.
+    // conditionTrackerMap: map of condition tracker id to index after the config update.
+    // returns whether or not the update is successful.
+    virtual bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index,
+                                 const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+                                 const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                                 const std::unordered_map<int64_t, int>& conditionTrackerMap) {
+        mIndex = index;
+        return true;
+    }
 
     // evaluate current condition given the new event.
     // event: the new log event
-    // eventMatcherValues: the results of the LogMatcherTrackers. LogMatcherTrackers always process
-    //                     event before ConditionTrackers, because ConditionTracker depends on
-    //                     LogMatchingTrackers.
+    // eventMatcherValues: the results of the AtomMatchingTrackers. AtomMatchingTrackers always
+    //                     process event before ConditionTrackers, because ConditionTracker depends
+    //                     on AtomMatchingTrackers.
     // mAllConditions: the list of all ConditionTracker
     // conditionCache: the cached non-sliced condition of the ConditionTrackers for this new event.
     // conditionChanged: the bit map to record whether the condition has changed.
@@ -88,8 +109,8 @@
             const bool isPartialLink,
             std::vector<ConditionState>& conditionCache) const = 0;
 
-    // return the list of LogMatchingTracker index that this ConditionTracker uses.
-    virtual const std::set<int>& getLogTrackerIndex() const {
+    // return the list of AtomMatchingTracker index that this ConditionTracker uses.
+    virtual const std::set<int>& getAtomMatchingTrackerIndex() const {
         return mTrackerIndex;
     }
 
@@ -110,9 +131,12 @@
         return mConditionId;
     }
 
-    virtual void getTrueSlicedDimensions(
-        const std::vector<sp<ConditionTracker>>& allConditions,
-        std::set<HashableDimensionKey>* dimensions) const = 0;
+    inline uint64_t getProtoHash() const {
+        return mProtoHash;
+    }
+
+    virtual const std::map<HashableDimensionKey, int>* getSlicedDimensionMap(
+            const std::vector<sp<ConditionTracker>>& allConditions) const = 0;
 
     virtual bool IsChangedDimensionTrackable() const = 0;
 
@@ -131,12 +155,12 @@
     const int64_t mConditionId;
 
     // the index of this condition in the manager's condition list.
-    const int mIndex;
+    int mIndex;
 
     // if it's properly initialized.
     bool mInitialized;
 
-    // the list of LogMatchingTracker index that this ConditionTracker uses.
+    // the list of AtomMatchingTracker index that this ConditionTracker uses.
     std::set<int> mTrackerIndex;
 
     // This variable is only used for CombinationConditionTrackers.
@@ -149,6 +173,12 @@
     ConditionState mUnSlicedPartCondition;
 
     bool mSliced;
+
+    // Hash of the Predicate's proto bytes from StatsdConfig.
+    // Used to determine if the definition of this condition has changed across a config update.
+    const uint64_t mProtoHash;
+
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions);
 };
 
 }  // namespace statsd
diff --git a/bin/src/condition/ConditionWizard.h b/bin/src/condition/ConditionWizard.h
index 8926479..43db94c 100644
--- a/bin/src/condition/ConditionWizard.h
+++ b/bin/src/condition/ConditionWizard.h
@@ -53,9 +53,9 @@
     ConditionState getUnSlicedPartConditionState(const int index) {
         return mAllConditions[index]->getUnSlicedPartConditionState();
     }
-    void getTrueSlicedDimensions(const int index,
-        std::set<HashableDimensionKey>* trueDimensions) const {
-        return mAllConditions[index]->getTrueSlicedDimensions(mAllConditions, trueDimensions);
+
+    const std::map<HashableDimensionKey, int>* getSlicedDimensionMap(const int index) const {
+        return mAllConditions[index]->getSlicedDimensionMap(mAllConditions);
     }
 
 private:
diff --git a/bin/src/condition/SimpleConditionTracker.cpp b/bin/src/condition/SimpleConditionTracker.cpp
index efb4d49..1dcc8f9 100644
--- a/bin/src/condition/SimpleConditionTracker.cpp
+++ b/bin/src/condition/SimpleConditionTracker.cpp
@@ -27,54 +27,21 @@
 using std::unordered_map;
 
 SimpleConditionTracker::SimpleConditionTracker(
-        const ConfigKey& key, const int64_t& id, const int index,
+        const ConfigKey& key, const int64_t& id, const uint64_t protoHash, const int index,
         const SimplePredicate& simplePredicate,
-        const unordered_map<int64_t, int>& trackerNameIndexMap)
-    : ConditionTracker(id, index), mConfigKey(key), mContainANYPositionInInternalDimensions(false) {
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap)
+    : ConditionTracker(id, index, protoHash),
+      mConfigKey(key),
+      mContainANYPositionInInternalDimensions(false) {
     VLOG("creating SimpleConditionTracker %lld", (long long)mConditionId);
     mCountNesting = simplePredicate.count_nesting();
 
-    if (simplePredicate.has_start()) {
-        auto pair = trackerNameIndexMap.find(simplePredicate.start());
-        if (pair == trackerNameIndexMap.end()) {
-            ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start());
-            return;
-        }
-        mStartLogMatcherIndex = pair->second;
-        mTrackerIndex.insert(mStartLogMatcherIndex);
-    } else {
-        mStartLogMatcherIndex = -1;
-    }
-
-    if (simplePredicate.has_stop()) {
-        auto pair = trackerNameIndexMap.find(simplePredicate.stop());
-        if (pair == trackerNameIndexMap.end()) {
-            ALOGW("Stop matcher %lld not found in the config", (long long)simplePredicate.stop());
-            return;
-        }
-        mStopLogMatcherIndex = pair->second;
-        mTrackerIndex.insert(mStopLogMatcherIndex);
-    } else {
-        mStopLogMatcherIndex = -1;
-    }
-
-    if (simplePredicate.has_stop_all()) {
-        auto pair = trackerNameIndexMap.find(simplePredicate.stop_all());
-        if (pair == trackerNameIndexMap.end()) {
-            ALOGW("Stop all matcher %lld found in the config", (long long)simplePredicate.stop_all());
-            return;
-        }
-        mStopAllLogMatcherIndex = pair->second;
-        mTrackerIndex.insert(mStopAllLogMatcherIndex);
-    } else {
-        mStopAllLogMatcherIndex = -1;
-    }
+    setMatcherIndices(simplePredicate, atomMatchingTrackerMap);
 
     if (simplePredicate.has_dimensions()) {
         translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions);
         if (mOutputDimensions.size() > 0) {
             mSliced = true;
-            mDimensionTag = mOutputDimensions[0].mMatcher.getTag();
         }
         mContainANYPositionInInternalDimensions = HasPositionANY(simplePredicate.dimensions());
     }
@@ -95,14 +62,70 @@
 bool SimpleConditionTracker::init(const vector<Predicate>& allConditionConfig,
                                   const vector<sp<ConditionTracker>>& allConditionTrackers,
                                   const unordered_map<int64_t, int>& conditionIdIndexMap,
-                                  vector<bool>& stack,
-                                  vector<ConditionState>& initialConditionCache) {
+                                  vector<bool>& stack, vector<ConditionState>& conditionCache) {
     // SimpleConditionTracker does not have dependency on other conditions, thus we just return
     // if the initialization was successful.
-    initialConditionCache[mIndex] = mInitialValue;
+    ConditionKey conditionKey;
+    if (mSliced) {
+        conditionKey[mConditionId] = DEFAULT_DIMENSION_KEY;
+    }
+    isConditionMet(conditionKey, allConditionTrackers, mSliced, conditionCache);
     return mInitialized;
 }
 
+bool SimpleConditionTracker::onConfigUpdated(
+        const vector<Predicate>& allConditionProtos, const int index,
+        const vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& conditionTrackerMap) {
+    ConditionTracker::onConfigUpdated(allConditionProtos, index, allConditionTrackers,
+                                      atomMatchingTrackerMap, conditionTrackerMap);
+    setMatcherIndices(allConditionProtos[index].simple_predicate(), atomMatchingTrackerMap);
+    return true;
+}
+
+void SimpleConditionTracker::setMatcherIndices(
+        const SimplePredicate& simplePredicate,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
+    mTrackerIndex.clear();
+    if (simplePredicate.has_start()) {
+        auto pair = atomMatchingTrackerMap.find(simplePredicate.start());
+        if (pair == atomMatchingTrackerMap.end()) {
+            ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start());
+            return;
+        }
+        mStartLogMatcherIndex = pair->second;
+        mTrackerIndex.insert(mStartLogMatcherIndex);
+    } else {
+        mStartLogMatcherIndex = -1;
+    }
+
+    if (simplePredicate.has_stop()) {
+        auto pair = atomMatchingTrackerMap.find(simplePredicate.stop());
+        if (pair == atomMatchingTrackerMap.end()) {
+            ALOGW("Stop matcher %lld not found in the config", (long long)simplePredicate.stop());
+            return;
+        }
+        mStopLogMatcherIndex = pair->second;
+        mTrackerIndex.insert(mStopLogMatcherIndex);
+    } else {
+        mStopLogMatcherIndex = -1;
+    }
+
+    if (simplePredicate.has_stop_all()) {
+        auto pair = atomMatchingTrackerMap.find(simplePredicate.stop_all());
+        if (pair == atomMatchingTrackerMap.end()) {
+            ALOGW("Stop all matcher %lld found in the config",
+                  (long long)simplePredicate.stop_all());
+            return;
+        }
+        mStopAllLogMatcherIndex = pair->second;
+        mTrackerIndex.insert(mStopAllLogMatcherIndex);
+    } else {
+        mStopAllLogMatcherIndex = -1;
+    }
+}
+
 void SimpleConditionTracker::dumpState() {
     VLOG("%lld DUMP:", (long long)mConditionId);
     for (const auto& pair : mSlicedConditionState) {
diff --git a/bin/src/condition/SimpleConditionTracker.h b/bin/src/condition/SimpleConditionTracker.h
index d576b21..4daec06 100644
--- a/bin/src/condition/SimpleConditionTracker.h
+++ b/bin/src/condition/SimpleConditionTracker.h
@@ -27,18 +27,23 @@
 namespace os {
 namespace statsd {
 
-class SimpleConditionTracker : public virtual ConditionTracker {
+class SimpleConditionTracker : public ConditionTracker {
 public:
-    SimpleConditionTracker(const ConfigKey& key, const int64_t& id, const int index,
-                           const SimplePredicate& simplePredicate,
-                           const std::unordered_map<int64_t, int>& trackerNameIndexMap);
+    SimpleConditionTracker(const ConfigKey& key, const int64_t& id, const uint64_t protoHash,
+                           const int index, const SimplePredicate& simplePredicate,
+                           const std::unordered_map<int64_t, int>& atomMatchingTrackerMap);
 
     ~SimpleConditionTracker();
 
     bool init(const std::vector<Predicate>& allConditionConfig,
               const std::vector<sp<ConditionTracker>>& allConditionTrackers,
               const std::unordered_map<int64_t, int>& conditionIdIndexMap, std::vector<bool>& stack,
-              std::vector<ConditionState>& initialConditionCache) override;
+              std::vector<ConditionState>& conditionCache) override;
+
+    bool onConfigUpdated(const std::vector<Predicate>& allConditionProtos, const int index,
+                         const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+                         const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                         const std::unordered_map<int64_t, int>& conditionTrackerMap) override;
 
     void evaluateCondition(const LogEvent& event,
                            const std::vector<MatchingState>& eventMatcherValues,
@@ -69,14 +74,9 @@
         }
     }
 
-    void getTrueSlicedDimensions(
-            const std::vector<sp<ConditionTracker>>& allConditions,
-            std::set<HashableDimensionKey>* dimensions) const override {
-        for (const auto& itr : mSlicedConditionState) {
-            if (itr.second > 0) {
-                dimensions->insert(itr.first);
-            }
-        }
+    const std::map<HashableDimensionKey, int>* getSlicedDimensionMap(
+            const std::vector<sp<ConditionTracker>>& allConditions) const override {
+        return &mSlicedConditionState;
     }
 
     bool IsChangedDimensionTrackable() const  override { return true; }
@@ -112,10 +112,11 @@
     std::set<HashableDimensionKey> mLastChangedToTrueDimensions;
     std::set<HashableDimensionKey> mLastChangedToFalseDimensions;
 
-    int mDimensionTag;
-
     std::map<HashableDimensionKey, int> mSlicedConditionState;
 
+    void setMatcherIndices(const SimplePredicate& predicate,
+                           const std::unordered_map<int64_t, int>& logTrackerMap);
+
     void handleStopAll(std::vector<ConditionState>& conditionCache,
                        std::vector<bool>& changedCache);
 
@@ -129,6 +130,7 @@
     FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition);
     FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim);
     FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateConditions);
 };
 
 }  // namespace statsd
diff --git a/bin/src/external/StatsPullerManager.cpp b/bin/src/external/StatsPullerManager.cpp
index 8a9ec74..8334b6b 100644
--- a/bin/src/external/StatsPullerManager.cpp
+++ b/bin/src/external/StatsPullerManager.cpp
@@ -92,63 +92,43 @@
 }
 
 bool StatsPullerManager::Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
-                              vector<shared_ptr<LogEvent>>* data, bool useUids) {
+                              vector<shared_ptr<LogEvent>>* data) {
     std::lock_guard<std::mutex> _l(mLock);
-    return PullLocked(tagId, configKey, eventTimeNs, data, useUids);
+    return PullLocked(tagId, configKey, eventTimeNs, data);
 }
 
 bool StatsPullerManager::Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
-                              vector<std::shared_ptr<LogEvent>>* data, bool useUids) {
+                              vector<std::shared_ptr<LogEvent>>* data) {
     std::lock_guard<std::mutex> _l(mLock);
-    return PullLocked(tagId, uids, eventTimeNs, data, useUids);
+    return PullLocked(tagId, uids, eventTimeNs, data);
 }
 
 bool StatsPullerManager::PullLocked(int tagId, const ConfigKey& configKey,
-                                    const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data,
-                                    bool useUids) {
+                                    const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data) {
     vector<int32_t> uids;
-    if (useUids) {
-        auto uidProviderIt = mPullUidProviders.find(configKey);
-        if (uidProviderIt == mPullUidProviders.end()) {
-            ALOGE("Error pulling tag %d. No pull uid provider for config key %s", tagId,
-                  configKey.ToString().c_str());
-            StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
-            return false;
-        }
-        sp<PullUidProvider> pullUidProvider = uidProviderIt->second.promote();
-        if (pullUidProvider == nullptr) {
-            ALOGE("Error pulling tag %d, pull uid provider for config %s is gone.", tagId,
-                  configKey.ToString().c_str());
-            StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
-            return false;
-        }
-        uids = pullUidProvider->getPullAtomUids(tagId);
+    const auto& uidProviderIt = mPullUidProviders.find(configKey);
+    if (uidProviderIt == mPullUidProviders.end()) {
+        ALOGE("Error pulling tag %d. No pull uid provider for config key %s", tagId,
+              configKey.ToString().c_str());
+        StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
+        return false;
     }
-    return PullLocked(tagId, uids, eventTimeNs, data, useUids);
+    sp<PullUidProvider> pullUidProvider = uidProviderIt->second.promote();
+    if (pullUidProvider == nullptr) {
+        ALOGE("Error pulling tag %d, pull uid provider for config %s is gone.", tagId,
+              configKey.ToString().c_str());
+        StatsdStats::getInstance().notePullUidProviderNotFound(tagId);
+        return false;
+    }
+    uids = pullUidProvider->getPullAtomUids(tagId);
+    return PullLocked(tagId, uids, eventTimeNs, data);
 }
 
 bool StatsPullerManager::PullLocked(int tagId, const vector<int32_t>& uids,
-                                    const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data,
-                                    bool useUids) {
+                                    const int64_t eventTimeNs, vector<shared_ptr<LogEvent>>* data) {
     VLOG("Initiating pulling %d", tagId);
-    if (useUids) {
-        for (int32_t uid : uids) {
-            PullerKey key = {.atomTag = tagId, .uid = uid};
-            auto pullerIt = kAllPullAtomInfo.find(key);
-            if (pullerIt != kAllPullAtomInfo.end()) {
-                bool ret = pullerIt->second->Pull(eventTimeNs, data);
-                VLOG("pulled %zu items", data->size());
-                if (!ret) {
-                    StatsdStats::getInstance().notePullFailed(tagId);
-                }
-                return ret;
-            }
-        }
-        StatsdStats::getInstance().notePullerNotFound(tagId);
-        ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
-        return false;  // Return early since we don't know what to pull.
-    } else {
-        PullerKey key = {.atomTag = tagId, .uid = -1};
+    for (int32_t uid : uids) {
+        PullerKey key = {.atomTag = tagId, .uid = uid};
         auto pullerIt = kAllPullAtomInfo.find(key);
         if (pullerIt != kAllPullAtomInfo.end()) {
             bool ret = pullerIt->second->Pull(eventTimeNs, data);
@@ -158,9 +138,10 @@
             }
             return ret;
         }
-        ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
-        return false;  // Return early since we don't know what to pull.
     }
+    StatsdStats::getInstance().notePullerNotFound(tagId);
+    ALOGW("StatsPullerManager: Unknown tagId %d", tagId);
+    return false;  // Return early since we don't know what to pull.
 }
 
 bool StatsPullerManager::PullerForMatcherExists(int tagId) const {
@@ -334,6 +315,7 @@
 }
 
 int StatsPullerManager::ForceClearPullerCache() {
+    std::lock_guard<std::mutex> _l(mLock);
     int totalCleared = 0;
     for (const auto& pulledAtom : kAllPullAtomInfo) {
         totalCleared += pulledAtom.second->ForceClearCache();
@@ -342,6 +324,7 @@
 }
 
 int StatsPullerManager::ClearPullerCacheIfNecessary(int64_t timestampNs) {
+    std::lock_guard<std::mutex> _l(mLock);
     int totalCleared = 0;
     for (const auto& pulledAtom : kAllPullAtomInfo) {
         totalCleared += pulledAtom.second->ClearCacheIfNecessary(timestampNs);
@@ -352,8 +335,7 @@
 void StatsPullerManager::RegisterPullAtomCallback(const int uid, const int32_t atomTag,
                                                   const int64_t coolDownNs, const int64_t timeoutNs,
                                                   const vector<int32_t>& additiveFields,
-                                                  const shared_ptr<IPullAtomCallback>& callback,
-                                                  bool useUid) {
+                                                  const shared_ptr<IPullAtomCallback>& callback) {
     std::lock_guard<std::mutex> _l(mLock);
     VLOG("RegisterPullerCallback: adding puller for tag %d", atomTag);
 
@@ -368,16 +350,15 @@
 
     sp<StatsCallbackPuller> puller = new StatsCallbackPuller(atomTag, callback, actualCoolDownNs,
                                                              actualTimeoutNs, additiveFields);
-    PullerKey key = {.atomTag = atomTag, .uid = useUid ? uid : -1};
+    PullerKey key = {.atomTag = atomTag, .uid = uid};
     AIBinder_linkToDeath(callback->asBinder().get(), mPullAtomCallbackDeathRecipient.get(),
                          new PullAtomCallbackDeathCookie(this, key, puller));
     kAllPullAtomInfo[key] = puller;
 }
 
-void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag,
-                                                    bool useUids) {
+void StatsPullerManager::UnregisterPullAtomCallback(const int uid, const int32_t atomTag) {
     std::lock_guard<std::mutex> _l(mLock);
-    PullerKey key = {.atomTag = atomTag, .uid = useUids ? uid : -1};
+    PullerKey key = {.atomTag = atomTag, .uid = uid};
     if (kAllPullAtomInfo.find(key) != kAllPullAtomInfo.end()) {
         StatsdStats::getInstance().notePullerCallbackRegistrationChanged(atomTag,
                                                                          /*registered=*/false);
diff --git a/bin/src/external/StatsPullerManager.h b/bin/src/external/StatsPullerManager.h
index 194a0f5..489cbdb 100644
--- a/bin/src/external/StatsPullerManager.h
+++ b/bin/src/external/StatsPullerManager.h
@@ -102,11 +102,11 @@
     // If the metric wants to make any change to the data, like timestamps, they
     // should make a copy as this data may be shared with multiple metrics.
     virtual bool Pull(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
-                      vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
+                      vector<std::shared_ptr<LogEvent>>* data);
 
     // Same as above, but directly specify the allowed uids to pull from.
     virtual bool Pull(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
-                      vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
+                      vector<std::shared_ptr<LogEvent>>* data);
 
     // Clear pull data cache immediately.
     int ForceClearPullerCache();
@@ -118,10 +118,9 @@
 
     void RegisterPullAtomCallback(const int uid, const int32_t atomTag, const int64_t coolDownNs,
                                   const int64_t timeoutNs, const vector<int32_t>& additiveFields,
-                                  const shared_ptr<IPullAtomCallback>& callback,
-                                  bool useUid = true);
+                                  const shared_ptr<IPullAtomCallback>& callback);
 
-    void UnregisterPullAtomCallback(const int uid, const int32_t atomTag, bool useUids = true);
+    void UnregisterPullAtomCallback(const int uid, const int32_t atomTag);
 
     std::map<const PullerKey, sp<StatsPuller>> kAllPullAtomInfo;
 
@@ -153,10 +152,10 @@
     std::map<ConfigKey, wp<PullUidProvider>> mPullUidProviders;
 
     bool PullLocked(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
-                    vector<std::shared_ptr<LogEvent>>* data, bool useUids = true);
+                    vector<std::shared_ptr<LogEvent>>* data);
 
     bool PullLocked(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
-                    vector<std::shared_ptr<LogEvent>>* data, bool useUids);
+                    vector<std::shared_ptr<LogEvent>>* data);
 
     // locks for data receiver and StatsCompanionService changes
     std::mutex mLock;
diff --git a/bin/src/flags/flags.cpp b/bin/src/flags/flags.cpp
new file mode 100644
index 0000000..e9fceda
--- /dev/null
+++ b/bin/src/flags/flags.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "flags.h"
+
+#include <server_configurable_flags/get_flags.h>
+
+using server_configurable_flags::GetServerConfigurableFlag;
+using std::string;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+string getFlagString(const string& flagName, const string& defaultValue) {
+    return GetServerConfigurableFlag(STATSD_NATIVE_NAMESPACE, flagName, defaultValue);
+}
+
+bool getFlagBool(const string& flagName, const string& defaultValue) {
+    return getFlagString(flagName, defaultValue) == "true";
+}
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/bin/src/flags/flags.h b/bin/src/flags/flags.h
new file mode 100644
index 0000000..213e1a4
--- /dev/null
+++ b/bin/src/flags/flags.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <string>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+const std::string STATSD_NATIVE_NAMESPACE = "statsd_native";
+
+const std::string PARTIAL_CONFIG_UPDATE_FLAG = "partial_config_update";
+
+std::string getFlagString(const std::string& flagName, const std::string& defaultValue);
+
+// Returns true IFF flagName has a value of "true".
+bool getFlagBool(const std::string& flagName, const std::string& defaultValue);
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/bin/src/guardrail/StatsdStats.cpp b/bin/src/guardrail/StatsdStats.cpp
index 6e89038..f4e01ce 100644
--- a/bin/src/guardrail/StatsdStats.cpp
+++ b/bin/src/guardrail/StatsdStats.cpp
@@ -521,6 +521,15 @@
     getAtomMetricStats(metricId).lateLogEventSkipped++;
 }
 
+void StatsdStats::noteLateLogEvent(int64_t metricId, int64_t extraDurationNs) {
+    lock_guard<std::mutex> lock(mLock);
+    AtomMetricStats& metricStats = getAtomMetricStats(metricId);
+    metricStats.lateLogEvent++;
+    metricStats.sumLateLogEventExtraDurationNs += extraDurationNs;
+    metricStats.maxLateLogEventExtraDurationNs =
+            std::max(metricStats.maxLateLogEventExtraDurationNs, extraDurationNs);
+}
+
 void StatsdStats::noteSkippedForwardBuckets(int64_t metricId) {
     lock_guard<std::mutex> lock(mLock);
     getAtomMetricStats(metricId).skippedForwardBuckets++;
@@ -558,11 +567,11 @@
 
 void StatsdStats::noteBucketBoundaryDelayNs(int64_t metricId, int64_t timeDelayNs) {
     lock_guard<std::mutex> lock(mLock);
-    AtomMetricStats& pullStats = getAtomMetricStats(metricId);
-    pullStats.maxBucketBoundaryDelayNs =
-            std::max(pullStats.maxBucketBoundaryDelayNs, timeDelayNs);
-    pullStats.minBucketBoundaryDelayNs =
-            std::min(pullStats.minBucketBoundaryDelayNs, timeDelayNs);
+    AtomMetricStats& metricStats = getAtomMetricStats(metricId);
+    metricStats.maxBucketBoundaryDelayNs =
+            std::max(metricStats.maxBucketBoundaryDelayNs, timeDelayNs);
+    metricStats.minBucketBoundaryDelayNs =
+            std::min(metricStats.minBucketBoundaryDelayNs, timeDelayNs);
 }
 
 void StatsdStats::noteAtomError(int atomTag, bool pull) {
diff --git a/bin/src/guardrail/StatsdStats.h b/bin/src/guardrail/StatsdStats.h
index 0050484..89a51f5 100644
--- a/bin/src/guardrail/StatsdStats.h
+++ b/bin/src/guardrail/StatsdStats.h
@@ -419,6 +419,11 @@
     void noteLateLogEventSkipped(int64_t metricId);
 
     /**
+     * A log event was too late, arrived in the wrong bucket.
+     */
+    void noteLateLogEvent(int64_t metricId, int64_t extraDurationNs);
+
+    /**
      * Buckets were skipped as time elapsed without any data for them
      */
     void noteSkippedForwardBuckets(int64_t metricId);
@@ -542,6 +547,9 @@
         int64_t maxBucketBoundaryDelayNs = 0;
         long bucketUnknownCondition = 0;
         long bucketCount = 0;
+        long lateLogEvent = 0;
+        int64_t sumLateLogEventExtraDurationNs = 0;
+        int64_t maxLateLogEventExtraDurationNs = 0;
     } AtomMetricStats;
 
 private:
diff --git a/bin/src/hash.h b/bin/src/hash.h
index cfe869f..bd6b0cd 100644
--- a/bin/src/hash.h
+++ b/bin/src/hash.h
@@ -22,6 +22,7 @@
 namespace os {
 namespace statsd {
 
+// Uses murmur2 hashing algorithm.
 extern uint32_t Hash32(const char *data, size_t n, uint32_t seed);
 extern uint64_t Hash64(const char* data, size_t n, uint64_t seed);
 
diff --git a/bin/src/main.cpp b/bin/src/main.cpp
index cd9c4e5..03b178a 100644
--- a/bin/src/main.cpp
+++ b/bin/src/main.cpp
@@ -76,7 +76,7 @@
     ABinderProcess_startThreadPool();
 
     std::shared_ptr<LogEventQueue> eventQueue =
-            std::make_shared<LogEventQueue>(2000 /*buffer limit. Buffer is NOT pre-allocated*/);
+            std::make_shared<LogEventQueue>(4000 /*buffer limit. Buffer is NOT pre-allocated*/);
 
     // Create the service
     gStatsService = SharedRefBase::make<StatsService>(looper, eventQueue);
diff --git a/bin/src/matchers/AtomMatchingTracker.h b/bin/src/matchers/AtomMatchingTracker.h
new file mode 100644
index 0000000..0155195
--- /dev/null
+++ b/bin/src/matchers/AtomMatchingTracker.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ATOM_MATCHING_TRACKER_H
+#define ATOM_MATCHING_TRACKER_H
+
+#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
+#include "logd/LogEvent.h"
+#include "matchers/matcher_util.h"
+
+#include <utils/RefBase.h>
+
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class AtomMatchingTracker : public virtual RefBase {
+public:
+    AtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash)
+        : mId(id), mIndex(index), mInitialized(false), mProtoHash(protoHash){};
+
+    virtual ~AtomMatchingTracker(){};
+
+    // Initialize this AtomMatchingTracker.
+    // allAtomMatchers: the list of the AtomMatcher proto config. This is needed because we don't
+    //                  store the proto object in memory. We only need it during initilization.
+    // allAtomMatchingTrackers: the list of the AtomMatchingTracker objects. It's a one-to-one
+    //                          mapping with allAtomMatchers. This is needed because the
+    //                          initialization is done recursively for
+    //                          CombinationAtomMatchingTrackers using DFS.
+    // stack: a bit map to record which matcher has been visited on the stack. This is for detecting
+    //        circle dependency.
+    virtual bool init(const std::vector<AtomMatcher>& allAtomMatchers,
+                      const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+                      const std::unordered_map<int64_t, int>& matcherMap,
+                      std::vector<bool>& stack) = 0;
+
+    // Update appropriate state on config updates. Primarily, all indices need to be updated.
+    // This matcher and all of its children are guaranteed to be preserved across the update.
+    // matcher: the AtomMatcher proto from the config.
+    // index: the index of this matcher in mAllAtomMatchingTrackers.
+    // atomMatchingTrackerMap: map from matcher id to index in mAllAtomMatchingTrackers
+    virtual bool onConfigUpdated(
+            const AtomMatcher& matcher, const int index,
+            const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) = 0;
+
+    // Called when a log event comes.
+    // event: the log event.
+    // allAtomMatchingTrackers: the list of all AtomMatchingTrackers. This is needed because the log
+    //                          processing is done recursively.
+    // matcherResults: The cached results for all matchers for this event. Parent matchers can
+    //                 directly access the children's matching results if they have been evaluated.
+    //                 Otherwise, call children matchers' onLogEvent.
+    virtual void onLogEvent(const LogEvent& event,
+                            const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+                            std::vector<MatchingState>& matcherResults) = 0;
+
+    // Get the tagIds that this matcher cares about. The combined collection is stored
+    // in MetricMananger, so that we can pass any LogEvents that are not interest of us. It uses
+    // some memory but hopefully it can save us much CPU time when there is flood of events.
+    virtual const std::set<int>& getAtomIds() const {
+        return mAtomIds;
+    }
+
+    int64_t getId() const {
+        return mId;
+    }
+
+    uint64_t getProtoHash() const {
+        return mProtoHash;
+    }
+
+protected:
+    // Name of this matching. We don't really need the name, but it makes log message easy to debug.
+    const int64_t mId;
+
+    // Index of this AtomMatchingTracker in MetricsManager's container.
+    int mIndex;
+
+    // Whether this AtomMatchingTracker has been properly initialized.
+    bool mInitialized;
+
+    // The collection of the event tag ids that this AtomMatchingTracker cares. So we can quickly
+    // return kNotMatched when we receive an event with an id not in the list. This is especially
+    // useful when we have a complex CombinationAtomMatchingTracker.
+    std::set<int> mAtomIds;
+
+    // Hash of the AtomMatcher's proto bytes from StatsdConfig.
+    // Used to determine if the definition of this matcher has changed across a config update.
+    const uint64_t mProtoHash;
+
+    FRIEND_TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerSimple);
+    FRIEND_TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerCombination);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateMatchers);
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+
+#endif  // ATOM_MATCHING_TRACKER_H
diff --git a/bin/src/matchers/CombinationAtomMatchingTracker.cpp b/bin/src/matchers/CombinationAtomMatchingTracker.cpp
new file mode 100644
index 0000000..45685ce
--- /dev/null
+++ b/bin/src/matchers/CombinationAtomMatchingTracker.cpp
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Log.h"
+
+#include "CombinationAtomMatchingTracker.h"
+
+#include "matchers/matcher_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+CombinationAtomMatchingTracker::CombinationAtomMatchingTracker(const int64_t& id, const int index,
+                                                               const uint64_t protoHash)
+    : AtomMatchingTracker(id, index, protoHash) {
+}
+
+CombinationAtomMatchingTracker::~CombinationAtomMatchingTracker() {
+}
+
+bool CombinationAtomMatchingTracker::init(
+        const vector<AtomMatcher>& allAtomMatchers,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& matcherMap, vector<bool>& stack) {
+    if (mInitialized) {
+        return true;
+    }
+
+    // mark this node as visited in the recursion stack.
+    stack[mIndex] = true;
+
+    AtomMatcher_Combination matcher = allAtomMatchers[mIndex].combination();
+
+    // LogicalOperation is missing in the config
+    if (!matcher.has_operation()) {
+        return false;
+    }
+
+    mLogicalOperation = matcher.operation();
+
+    if (mLogicalOperation == LogicalOperation::NOT && matcher.matcher_size() != 1) {
+        return false;
+    }
+
+    for (const auto& child : matcher.matcher()) {
+        auto pair = matcherMap.find(child);
+        if (pair == matcherMap.end()) {
+            ALOGW("Matcher %lld not found in the config", (long long)child);
+            return false;
+        }
+
+        int childIndex = pair->second;
+
+        // if the child is a visited node in the recursion -> circle detected.
+        if (stack[childIndex]) {
+            ALOGE("Circle detected in matcher config");
+            return false;
+        }
+
+        if (!allAtomMatchingTrackers[childIndex]->init(allAtomMatchers, allAtomMatchingTrackers,
+                                                       matcherMap, stack)) {
+            ALOGW("child matcher init failed %lld", (long long)child);
+            return false;
+        }
+
+        mChildren.push_back(childIndex);
+
+        const set<int>& childTagIds = allAtomMatchingTrackers[childIndex]->getAtomIds();
+        mAtomIds.insert(childTagIds.begin(), childTagIds.end());
+    }
+
+    mInitialized = true;
+    // unmark this node in the recursion stack.
+    stack[mIndex] = false;
+    return true;
+}
+
+bool CombinationAtomMatchingTracker::onConfigUpdated(
+        const AtomMatcher& matcher, const int index,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
+    mIndex = index;
+    mChildren.clear();
+    AtomMatcher_Combination combinationMatcher = matcher.combination();
+    for (const int64_t child : combinationMatcher.matcher()) {
+        const auto& pair = atomMatchingTrackerMap.find(child);
+        if (pair == atomMatchingTrackerMap.end()) {
+            ALOGW("Matcher %lld not found in the config", (long long)child);
+            return false;
+        }
+        mChildren.push_back(pair->second);
+    }
+    return true;
+}
+
+void CombinationAtomMatchingTracker::onLogEvent(
+        const LogEvent& event, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        vector<MatchingState>& matcherResults) {
+    // this event has been processed.
+    if (matcherResults[mIndex] != MatchingState::kNotComputed) {
+        return;
+    }
+
+    if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) {
+        matcherResults[mIndex] = MatchingState::kNotMatched;
+        return;
+    }
+
+    // evaluate children matchers if they haven't been evaluated.
+    for (const int childIndex : mChildren) {
+        if (matcherResults[childIndex] == MatchingState::kNotComputed) {
+            const sp<AtomMatchingTracker>& child = allAtomMatchingTrackers[childIndex];
+            child->onLogEvent(event, allAtomMatchingTrackers, matcherResults);
+        }
+    }
+
+    bool matched = combinationMatch(mChildren, mLogicalOperation, matcherResults);
+    matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/bin/src/matchers/CombinationAtomMatchingTracker.h b/bin/src/matchers/CombinationAtomMatchingTracker.h
new file mode 100644
index 0000000..d6e8f2c
--- /dev/null
+++ b/bin/src/matchers/CombinationAtomMatchingTracker.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef COMBINATION_ATOM_MATCHING_TRACKER_H
+#define COMBINATION_ATOM_MATCHING_TRACKER_H
+
+#include <unordered_map>
+#include <vector>
+
+#include "AtomMatchingTracker.h"
+#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Represents a AtomMatcher_Combination in the StatsdConfig.
+class CombinationAtomMatchingTracker : public AtomMatchingTracker {
+public:
+    CombinationAtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash);
+
+    bool init(const std::vector<AtomMatcher>& allAtomMatchers,
+              const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+              const std::unordered_map<int64_t, int>& matcherMap, std::vector<bool>& stack);
+
+    bool onConfigUpdated(const AtomMatcher& matcher, const int index,
+                         const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) override;
+
+    ~CombinationAtomMatchingTracker();
+
+    void onLogEvent(const LogEvent& event,
+                    const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+                    std::vector<MatchingState>& matcherResults) override;
+
+private:
+    LogicalOperation mLogicalOperation;
+
+    std::vector<int> mChildren;
+
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateMatchers);
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#endif  // COMBINATION_ATOM_MATCHING_TRACKER_H
diff --git a/bin/src/matchers/CombinationLogMatchingTracker.cpp b/bin/src/matchers/CombinationLogMatchingTracker.cpp
deleted file mode 100644
index b94a957..0000000
--- a/bin/src/matchers/CombinationLogMatchingTracker.cpp
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "Log.h"
-
-#include "CombinationLogMatchingTracker.h"
-#include "matchers/matcher_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::set;
-using std::unordered_map;
-using std::vector;
-
-CombinationLogMatchingTracker::CombinationLogMatchingTracker(const int64_t& id, const int index)
-    : LogMatchingTracker(id, index) {
-}
-
-CombinationLogMatchingTracker::~CombinationLogMatchingTracker() {
-}
-
-bool CombinationLogMatchingTracker::init(const vector<AtomMatcher>& allLogMatchers,
-                                         const vector<sp<LogMatchingTracker>>& allTrackers,
-                                         const unordered_map<int64_t, int>& matcherMap,
-                                         vector<bool>& stack) {
-    if (mInitialized) {
-        return true;
-    }
-
-    // mark this node as visited in the recursion stack.
-    stack[mIndex] = true;
-
-    AtomMatcher_Combination matcher = allLogMatchers[mIndex].combination();
-
-    // LogicalOperation is missing in the config
-    if (!matcher.has_operation()) {
-        return false;
-    }
-
-    mLogicalOperation = matcher.operation();
-
-    if (mLogicalOperation == LogicalOperation::NOT && matcher.matcher_size() != 1) {
-        return false;
-    }
-
-    for (const auto& child : matcher.matcher()) {
-        auto pair = matcherMap.find(child);
-        if (pair == matcherMap.end()) {
-            ALOGW("Matcher %lld not found in the config", (long long)child);
-            return false;
-        }
-
-        int childIndex = pair->second;
-
-        // if the child is a visited node in the recursion -> circle detected.
-        if (stack[childIndex]) {
-            ALOGE("Circle detected in matcher config");
-            return false;
-        }
-
-        if (!allTrackers[childIndex]->init(allLogMatchers, allTrackers, matcherMap, stack)) {
-            ALOGW("child matcher init failed %lld", (long long)child);
-            return false;
-        }
-
-        mChildren.push_back(childIndex);
-
-        const set<int>& childTagIds = allTrackers[childIndex]->getAtomIds();
-        mAtomIds.insert(childTagIds.begin(), childTagIds.end());
-    }
-
-    mInitialized = true;
-    // unmark this node in the recursion stack.
-    stack[mIndex] = false;
-    return true;
-}
-
-void CombinationLogMatchingTracker::onLogEvent(const LogEvent& event,
-                                               const vector<sp<LogMatchingTracker>>& allTrackers,
-                                               vector<MatchingState>& matcherResults) {
-    // this event has been processed.
-    if (matcherResults[mIndex] != MatchingState::kNotComputed) {
-        return;
-    }
-
-    if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) {
-        matcherResults[mIndex] = MatchingState::kNotMatched;
-        return;
-    }
-
-    // evaluate children matchers if they haven't been evaluated.
-    for (const int childIndex : mChildren) {
-        if (matcherResults[childIndex] == MatchingState::kNotComputed) {
-            const sp<LogMatchingTracker>& child = allTrackers[childIndex];
-            child->onLogEvent(event, allTrackers, matcherResults);
-        }
-    }
-
-    bool matched = combinationMatch(mChildren, mLogicalOperation, matcherResults);
-    matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
-}
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/bin/src/matchers/CombinationLogMatchingTracker.h b/bin/src/matchers/CombinationLogMatchingTracker.h
deleted file mode 100644
index e949215..0000000
--- a/bin/src/matchers/CombinationLogMatchingTracker.h
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-#ifndef COMBINATION_LOG_MATCHING_TRACKER_H
-#define COMBINATION_LOG_MATCHING_TRACKER_H
-
-#include <unordered_map>
-#include <vector>
-#include "LogMatchingTracker.h"
-#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Represents a AtomMatcher_Combination in the StatsdConfig.
-class CombinationLogMatchingTracker : public virtual LogMatchingTracker {
-public:
-    CombinationLogMatchingTracker(const int64_t& id, const int index);
-
-    bool init(const std::vector<AtomMatcher>& allLogMatchers,
-              const std::vector<sp<LogMatchingTracker>>& allTrackers,
-              const std::unordered_map<int64_t, int>& matcherMap,
-              std::vector<bool>& stack);
-
-    ~CombinationLogMatchingTracker();
-
-    void onLogEvent(const LogEvent& event,
-                    const std::vector<sp<LogMatchingTracker>>& allTrackers,
-                    std::vector<MatchingState>& matcherResults) override;
-
-private:
-    LogicalOperation mLogicalOperation;
-
-    std::vector<int> mChildren;
-};
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
-#endif  // COMBINATION_LOG_MATCHING_TRACKER_H
diff --git a/bin/src/matchers/EventMatcherWizard.h b/bin/src/matchers/EventMatcherWizard.h
index 57ec2b3..5d780f2 100644
--- a/bin/src/matchers/EventMatcherWizard.h
+++ b/bin/src/matchers/EventMatcherWizard.h
@@ -16,7 +16,7 @@
 
 #pragma once
 
-#include "LogMatchingTracker.h"
+#include "AtomMatchingTracker.h"
 
 namespace android {
 namespace os {
@@ -25,7 +25,7 @@
 class EventMatcherWizard : public virtual android::RefBase {
 public:
     EventMatcherWizard(){};  // for testing
-    EventMatcherWizard(const std::vector<sp<LogMatchingTracker>>& eventTrackers)
+    EventMatcherWizard(const std::vector<sp<AtomMatchingTracker>>& eventTrackers)
         : mAllEventMatchers(eventTrackers){};
 
     virtual ~EventMatcherWizard(){};
@@ -33,7 +33,7 @@
     MatchingState matchLogEvent(const LogEvent& event, int matcher_index);
 
 private:
-    std::vector<sp<LogMatchingTracker>> mAllEventMatchers;
+    std::vector<sp<AtomMatchingTracker>> mAllEventMatchers;
 };
 
 }  // namespace statsd
diff --git a/bin/src/matchers/LogMatchingTracker.h b/bin/src/matchers/LogMatchingTracker.h
deleted file mode 100644
index 77f0b99..0000000
--- a/bin/src/matchers/LogMatchingTracker.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef LOG_MATCHING_TRACKER_H
-#define LOG_MATCHING_TRACKER_H
-
-#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
-#include "logd/LogEvent.h"
-#include "matchers/matcher_util.h"
-
-#include <utils/RefBase.h>
-
-#include <set>
-#include <unordered_map>
-#include <vector>
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class LogMatchingTracker : public virtual RefBase {
-public:
-    LogMatchingTracker(const int64_t& id, const int index)
-        : mId(id), mIndex(index), mInitialized(false){};
-
-    virtual ~LogMatchingTracker(){};
-
-    // Initialize this LogMatchingTracker.
-    // allLogMatchers: the list of the AtomMatcher proto config. This is needed because we don't
-    //                 store the proto object in memory. We only need it during initilization.
-    // allTrackers: the list of the LogMatchingTracker objects. It's a one-to-one mapping with
-    //              allLogMatchers. This is needed because the initialization is done recursively
-    //              for CombinationLogMatchingTrackers using DFS.
-    // stack: a bit map to record which matcher has been visited on the stack. This is for detecting
-    //        circle dependency.
-    virtual bool init(const std::vector<AtomMatcher>& allLogMatchers,
-                      const std::vector<sp<LogMatchingTracker>>& allTrackers,
-                      const std::unordered_map<int64_t, int>& matcherMap,
-                      std::vector<bool>& stack) = 0;
-
-    // Called when a log event comes.
-    // event: the log event.
-    // allTrackers: the list of all LogMatchingTrackers. This is needed because the log processing
-    //              is done recursively.
-    // matcherResults: The cached results for all matchers for this event. Parent matchers can
-    //                 directly access the children's matching results if they have been evaluated.
-    //                 Otherwise, call children matchers' onLogEvent.
-    virtual void onLogEvent(const LogEvent& event,
-                            const std::vector<sp<LogMatchingTracker>>& allTrackers,
-                            std::vector<MatchingState>& matcherResults) = 0;
-
-    // Get the tagIds that this matcher cares about. The combined collection is stored
-    // in MetricMananger, so that we can pass any LogEvents that are not interest of us. It uses
-    // some memory but hopefully it can save us much CPU time when there is flood of events.
-    virtual const std::set<int>& getAtomIds() const {
-        return mAtomIds;
-    }
-
-    const int64_t& getId() const {
-        return mId;
-    }
-
-protected:
-    // Name of this matching. We don't really need the name, but it makes log message easy to debug.
-    const int64_t mId;
-
-    // Index of this LogMatchingTracker in MetricsManager's container.
-    const int mIndex;
-
-    // Whether this LogMatchingTracker has been properly initialized.
-    bool mInitialized;
-
-    // The collection of the event tag ids that this LogMatchingTracker cares. So we can quickly
-    // return kNotMatched when we receive an event with an id not in the list. This is especially
-    // useful when we have a complex CombinationLogMatcherTracker.
-    std::set<int> mAtomIds;
-};
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
-
-#endif  // LOG_MATCHING_TRACKER_H
diff --git a/bin/src/matchers/SimpleAtomMatchingTracker.cpp b/bin/src/matchers/SimpleAtomMatchingTracker.cpp
new file mode 100644
index 0000000..423da5b
--- /dev/null
+++ b/bin/src/matchers/SimpleAtomMatchingTracker.cpp
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG false  // STOPSHIP if true
+#include "Log.h"
+
+#include "SimpleAtomMatchingTracker.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::unordered_map;
+using std::vector;
+
+SimpleAtomMatchingTracker::SimpleAtomMatchingTracker(const int64_t& id, const int index,
+                                                     const uint64_t protoHash,
+                                                     const SimpleAtomMatcher& matcher,
+                                                     const sp<UidMap>& uidMap)
+    : AtomMatchingTracker(id, index, protoHash), mMatcher(matcher), mUidMap(uidMap) {
+    if (!matcher.has_atom_id()) {
+        mInitialized = false;
+    } else {
+        mAtomIds.insert(matcher.atom_id());
+        mInitialized = true;
+    }
+}
+
+SimpleAtomMatchingTracker::~SimpleAtomMatchingTracker() {
+}
+
+bool SimpleAtomMatchingTracker::init(const vector<AtomMatcher>& allAtomMatchers,
+                                     const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+                                     const unordered_map<int64_t, int>& matcherMap,
+                                     vector<bool>& stack) {
+    // no need to do anything.
+    return mInitialized;
+}
+
+bool SimpleAtomMatchingTracker::onConfigUpdated(
+        const AtomMatcher& matcher, const int index,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
+    mIndex = index;
+    // Do not need to update mMatcher since the matcher must be identical across the update.
+    return mInitialized;
+}
+
+void SimpleAtomMatchingTracker::onLogEvent(
+        const LogEvent& event, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        vector<MatchingState>& matcherResults) {
+    if (matcherResults[mIndex] != MatchingState::kNotComputed) {
+        VLOG("Matcher %lld already evaluated ", (long long)mId);
+        return;
+    }
+
+    if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) {
+        matcherResults[mIndex] = MatchingState::kNotMatched;
+        return;
+    }
+
+    bool matched = matchesSimple(mUidMap, mMatcher, event);
+    matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
+    VLOG("Stats SimpleAtomMatcher %lld matched? %d", (long long)mId, matched);
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/bin/src/matchers/SimpleAtomMatchingTracker.h b/bin/src/matchers/SimpleAtomMatchingTracker.h
new file mode 100644
index 0000000..a76a467
--- /dev/null
+++ b/bin/src/matchers/SimpleAtomMatchingTracker.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SIMPLE_ATOM_MATCHING_TRACKER_H
+#define SIMPLE_ATOM_MATCHING_TRACKER_H
+
+#include <unordered_map>
+#include <vector>
+
+#include "AtomMatchingTracker.h"
+#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
+#include "packages/UidMap.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class SimpleAtomMatchingTracker : public AtomMatchingTracker {
+public:
+    SimpleAtomMatchingTracker(const int64_t& id, const int index, const uint64_t protoHash,
+                              const SimpleAtomMatcher& matcher, const sp<UidMap>& uidMap);
+
+    ~SimpleAtomMatchingTracker();
+
+    bool init(const std::vector<AtomMatcher>& allAtomMatchers,
+              const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+              const std::unordered_map<int64_t, int>& matcherMap,
+              std::vector<bool>& stack) override;
+
+    bool onConfigUpdated(const AtomMatcher& matcher, const int index,
+                         const std::unordered_map<int64_t, int>& atomMatchingTrackerMap) override;
+
+    void onLogEvent(const LogEvent& event,
+                    const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+                    std::vector<MatchingState>& matcherResults) override;
+
+private:
+    const SimpleAtomMatcher mMatcher;
+    const sp<UidMap> mUidMap;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#endif  // SIMPLE_ATOM_MATCHING_TRACKER_H
diff --git a/bin/src/matchers/SimpleLogMatchingTracker.cpp b/bin/src/matchers/SimpleLogMatchingTracker.cpp
deleted file mode 100644
index 082daf5..0000000
--- a/bin/src/matchers/SimpleLogMatchingTracker.cpp
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG false  // STOPSHIP if true
-#include "Log.h"
-
-#include "SimpleLogMatchingTracker.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-using std::unordered_map;
-using std::vector;
-
-
-SimpleLogMatchingTracker::SimpleLogMatchingTracker(const int64_t& id, const int index,
-                                                   const SimpleAtomMatcher& matcher,
-                                                   const UidMap& uidMap)
-    : LogMatchingTracker(id, index), mMatcher(matcher), mUidMap(uidMap) {
-    if (!matcher.has_atom_id()) {
-        mInitialized = false;
-    } else {
-        mAtomIds.insert(matcher.atom_id());
-        mInitialized = true;
-    }
-}
-
-SimpleLogMatchingTracker::~SimpleLogMatchingTracker() {
-}
-
-bool SimpleLogMatchingTracker::init(const vector<AtomMatcher>& allLogMatchers,
-                                    const vector<sp<LogMatchingTracker>>& allTrackers,
-                                    const unordered_map<int64_t, int>& matcherMap,
-                                    vector<bool>& stack) {
-    // no need to do anything.
-    return mInitialized;
-}
-
-void SimpleLogMatchingTracker::onLogEvent(const LogEvent& event,
-                                          const vector<sp<LogMatchingTracker>>& allTrackers,
-                                          vector<MatchingState>& matcherResults) {
-    if (matcherResults[mIndex] != MatchingState::kNotComputed) {
-        VLOG("Matcher %lld already evaluated ", (long long)mId);
-        return;
-    }
-
-    if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) {
-        matcherResults[mIndex] = MatchingState::kNotMatched;
-        return;
-    }
-
-    bool matched = matchesSimple(mUidMap, mMatcher, event);
-    matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
-    VLOG("Stats SimpleLogMatcher %lld matched? %d", (long long)mId, matched);
-}
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/bin/src/matchers/SimpleLogMatchingTracker.h b/bin/src/matchers/SimpleLogMatchingTracker.h
deleted file mode 100644
index ca9200b..0000000
--- a/bin/src/matchers/SimpleLogMatchingTracker.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef SIMPLE_LOG_MATCHING_TRACKER_H
-#define SIMPLE_LOG_MATCHING_TRACKER_H
-
-#include <unordered_map>
-#include <vector>
-#include "LogMatchingTracker.h"
-#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
-#include "packages/UidMap.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-class SimpleLogMatchingTracker : public virtual LogMatchingTracker {
-public:
-    SimpleLogMatchingTracker(const int64_t& id, const int index,
-                             const SimpleAtomMatcher& matcher,
-                             const UidMap& uidMap);
-
-    ~SimpleLogMatchingTracker();
-
-    bool init(const std::vector<AtomMatcher>& allLogMatchers,
-              const std::vector<sp<LogMatchingTracker>>& allTrackers,
-              const std::unordered_map<int64_t, int>& matcherMap,
-              std::vector<bool>& stack) override;
-
-    void onLogEvent(const LogEvent& event,
-                    const std::vector<sp<LogMatchingTracker>>& allTrackers,
-                    std::vector<MatchingState>& matcherResults) override;
-
-private:
-    const SimpleAtomMatcher mMatcher;
-    const UidMap& mUidMap;
-};
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
-#endif  // SIMPLE_LOG_MATCHING_TRACKER_H
diff --git a/bin/src/matchers/matcher_util.cpp b/bin/src/matchers/matcher_util.cpp
index 6b2fdfd..625eef9 100644
--- a/bin/src/matchers/matcher_util.cpp
+++ b/bin/src/matchers/matcher_util.cpp
@@ -17,7 +17,7 @@
 #include "Log.h"
 
 #include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
-#include "matchers/LogMatchingTracker.h"
+#include "matchers/AtomMatchingTracker.h"
 #include "matchers/matcher_util.h"
 #include "stats_util.h"
 
@@ -81,14 +81,15 @@
     return matched;
 }
 
-bool tryMatchString(const UidMap& uidMap, const FieldValue& fieldValue, const string& str_match) {
+bool tryMatchString(const sp<UidMap>& uidMap, const FieldValue& fieldValue,
+                    const string& str_match) {
     if (isAttributionUidField(fieldValue) || isUidField(fieldValue)) {
         int uid = fieldValue.mValue.int_value;
         auto aidIt = UidMap::sAidToUidMapping.find(str_match);
         if (aidIt != UidMap::sAidToUidMapping.end()) {
             return ((int)aidIt->second) == uid;
         }
-        std::set<string> packageNames = uidMap.getAppNamesFromUid(uid, true /* normalize*/);
+        std::set<string> packageNames = uidMap->getAppNamesFromUid(uid, true /* normalize*/);
         return packageNames.find(str_match) != packageNames.end();
     } else if (fieldValue.mValue.getType() == STRING) {
         return fieldValue.mValue.str_value == str_match;
@@ -96,7 +97,7 @@
     return false;
 }
 
-bool matchesSimple(const UidMap& uidMap, const FieldValueMatcher& matcher,
+bool matchesSimple(const sp<UidMap>& uidMap, const FieldValueMatcher& matcher,
                    const vector<FieldValue>& values, int start, int end, int depth) {
     if (depth > 2) {
         ALOGE("Depth > 3 not supported");
@@ -353,7 +354,7 @@
     }
 }
 
-bool matchesSimple(const UidMap& uidMap, const SimpleAtomMatcher& simpleMatcher,
+bool matchesSimple(const sp<UidMap>& uidMap, const SimpleAtomMatcher& simpleMatcher,
                    const LogEvent& event) {
     if (event.GetTagId() != simpleMatcher.atom_id()) {
         return false;
diff --git a/bin/src/matchers/matcher_util.h b/bin/src/matchers/matcher_util.h
index 7493d5d..fc526da 100644
--- a/bin/src/matchers/matcher_util.h
+++ b/bin/src/matchers/matcher_util.h
@@ -36,8 +36,8 @@
 bool combinationMatch(const std::vector<int>& children, const LogicalOperation& operation,
                       const std::vector<MatchingState>& matcherResults);
 
-bool matchesSimple(const UidMap& uidMap,
-    const SimpleAtomMatcher& simpleMatcher, const LogEvent& wrapper);
+bool matchesSimple(const sp<UidMap>& uidMap, const SimpleAtomMatcher& simpleMatcher,
+                   const LogEvent& wrapper);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/bin/src/metrics/CountMetricProducer.cpp b/bin/src/metrics/CountMetricProducer.cpp
index 5739612..a8ef54a 100644
--- a/bin/src/metrics/CountMetricProducer.cpp
+++ b/bin/src/metrics/CountMetricProducer.cpp
@@ -24,6 +24,7 @@
 #include <stdlib.h>
 
 #include "guardrail/StatsdStats.h"
+#include "metrics/parsing_utils/metrics_manager_util.h"
 #include "stats_log_util.h"
 #include "stats_util.h"
 
@@ -69,13 +70,14 @@
 CountMetricProducer::CountMetricProducer(
         const ConfigKey& key, const CountMetric& metric, const int conditionIndex,
         const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
-        const int64_t timeBaseNs, const int64_t startTimeNs,
+        const uint64_t protoHash, const int64_t timeBaseNs, const int64_t startTimeNs,
         const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
         const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
         const vector<int>& slicedStateAtoms,
         const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
-                     eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap) {
+                     protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
+                     stateGroupMap) {
     if (metric.has_bucket()) {
         mBucketSizeNs =
                 TimeUnitToBucketSizeInMillisGuardrailed(key.GetUid(), metric.bucket()) * 1000000;
@@ -121,6 +123,47 @@
     VLOG("~CountMetricProducer() called");
 }
 
+bool CountMetricProducer::onConfigUpdatedLocked(
+        const StatsdConfig& config, const int configIndex, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    if (!MetricProducer::onConfigUpdatedLocked(
+                config, configIndex, metricIndex, allAtomMatchingTrackers,
+                oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
+                trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+                deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+        return false;
+    }
+
+    const CountMetric& metric = config.count_metric(configIndex);
+    int trackerIndex;
+    // Update appropriate indices, specifically mConditionIndex and MetricsManager maps.
+    if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false,
+                                              allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                              trackerToMetricMap, trackerIndex)) {
+        return false;
+    }
+
+    if (metric.has_condition() &&
+        !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                    metric.links(), allConditionTrackers, mConditionTrackerIndex,
+                                    conditionToMetricMap)) {
+        return false;
+    }
+    return true;
+}
+
 void CountMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
                                          const HashableDimensionKey& primaryKey,
                                          const FieldValue& oldState, const FieldValue& newState) {
diff --git a/bin/src/metrics/CountMetricProducer.h b/bin/src/metrics/CountMetricProducer.h
index 5399c34..4b1f1c8 100644
--- a/bin/src/metrics/CountMetricProducer.h
+++ b/bin/src/metrics/CountMetricProducer.h
@@ -44,7 +44,7 @@
     CountMetricProducer(
             const ConfigKey& key, const CountMetric& countMetric, const int conditionIndex,
             const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
-            const int64_t timeBaseNs, const int64_t startTimeNs,
+            const uint64_t protoHash, const int64_t timeBaseNs, const int64_t startTimeNs,
             const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
             const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
                     eventDeactivationMap = {},
@@ -57,6 +57,10 @@
                         const HashableDimensionKey& primaryKey, const FieldValue& oldState,
                         const FieldValue& newState) override;
 
+    MetricType getMetricType() const override {
+        return METRIC_TYPE_COUNT;
+    }
+
 protected:
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const MetricDimensionKey& eventKey,
@@ -93,6 +97,22 @@
     void flushCurrentBucketLocked(const int64_t& eventTimeNs,
                                   const int64_t& nextBucketStartTimeNs) override;
 
+    bool onConfigUpdatedLocked(
+            const StatsdConfig& config, const int configIndex, const int metricIndex,
+            const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+            const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+            const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+            const sp<EventMatcherWizard>& matcherWizard,
+            const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+            const std::unordered_map<int64_t, int>& conditionTrackerMap,
+            const sp<ConditionWizard>& wizard,
+            const std::unordered_map<int64_t, int>& metricToActivationMap,
+            std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+            std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+            std::vector<int>& metricsWithActivation) override;
+
     std::unordered_map<MetricDimensionKey, std::vector<CountBucket>> mPastBuckets;
 
     // The current bucket (may be a partial bucket).
diff --git a/bin/src/metrics/DurationMetricProducer.cpp b/bin/src/metrics/DurationMetricProducer.cpp
index e9b0438..fe92b08 100644
--- a/bin/src/metrics/DurationMetricProducer.cpp
+++ b/bin/src/metrics/DurationMetricProducer.cpp
@@ -17,14 +17,17 @@
 #define DEBUG false
 
 #include "Log.h"
+
 #include "DurationMetricProducer.h"
-#include "guardrail/StatsdStats.h"
-#include "stats_util.h"
-#include "stats_log_util.h"
 
 #include <limits.h>
 #include <stdlib.h>
 
+#include "guardrail/StatsdStats.h"
+#include "metrics/parsing_utils/metrics_manager_util.h"
+#include "stats_log_util.h"
+#include "stats_util.h"
+
 using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_BOOL;
 using android::util::FIELD_TYPE_FLOAT;
@@ -64,16 +67,17 @@
 
 DurationMetricProducer::DurationMetricProducer(
         const ConfigKey& key, const DurationMetric& metric, const int conditionIndex,
-        const vector<ConditionState>& initialConditionCache, const size_t startIndex,
-        const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
-        const sp<ConditionWizard>& wizard, const FieldMatcher& internalDimensions,
-        const int64_t timeBaseNs, const int64_t startTimeNs,
+        const vector<ConditionState>& initialConditionCache, const int whatIndex,
+        const int startIndex, const int stopIndex, const int stopAllIndex, const bool nesting,
+        const sp<ConditionWizard>& wizard, const uint64_t protoHash,
+        const FieldMatcher& internalDimensions, const int64_t timeBaseNs, const int64_t startTimeNs,
         const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
         const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
         const vector<int>& slicedStateAtoms,
         const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
-                     eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap),
+                     protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
+                     stateGroupMap),
       mAggregationType(metric.aggregation_type()),
       mStartIndex(startIndex),
       mStopIndex(stopIndex),
@@ -103,6 +107,12 @@
         ALOGE("Position ANY in dimension_in_what not supported.");
     }
 
+    // Dimensions in what must be subset of internal dimensions
+    if (!subsetDimensions(mDimensionsInWhat, mInternalDimensions)) {
+        ALOGE("Dimensions in what must be a subset of the internal dimensions");
+        mValid = false;
+    }
+
     mSliceByPositionALL = HasPositionALL(metric.dimensions_in_what());
 
     if (metric.links().size() > 0) {
@@ -111,6 +121,10 @@
             mc.conditionId = link.condition();
             translateFieldMatcher(link.fields_in_what(), &mc.metricFields);
             translateFieldMatcher(link.fields_in_condition(), &mc.conditionFields);
+            if (!subsetDimensions(mc.metricFields, mInternalDimensions)) {
+                ALOGE(("Condition links must be a subset of the internal dimensions"));
+                mValid = false;
+            }
             mMetric2ConditionLinks.push_back(mc);
         }
         mConditionSliced = true;
@@ -122,6 +136,10 @@
         ms.stateAtomId = stateLink.state_atom_id();
         translateFieldMatcher(stateLink.fields_in_what(), &ms.metricFields);
         translateFieldMatcher(stateLink.fields_in_state(), &ms.stateFields);
+        if (!subsetDimensions(ms.metricFields, mInternalDimensions)) {
+            ALOGE(("State links must be a subset of the dimensions in what  internal dimensions"));
+            mValid = false;
+        }
         mMetric2StateLinks.push_back(ms);
     }
 
@@ -136,12 +154,109 @@
     mCurrentBucketStartTimeNs = startTimeNs;
     VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
          (long long)mBucketSizeNs, (long long)mTimeBaseNs);
+
+    initTrueDimensions(whatIndex, startTimeNs);
 }
 
 DurationMetricProducer::~DurationMetricProducer() {
     VLOG("~DurationMetric() called");
 }
 
+bool DurationMetricProducer::onConfigUpdatedLocked(
+        const StatsdConfig& config, const int configIndex, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    if (!MetricProducer::onConfigUpdatedLocked(
+                config, configIndex, metricIndex, allAtomMatchingTrackers,
+                oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
+                trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+                deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+        return false;
+    }
+
+    const DurationMetric& metric = config.duration_metric(configIndex);
+    const auto& what_it = conditionTrackerMap.find(metric.what());
+    if (what_it == conditionTrackerMap.end()) {
+        ALOGE("DurationMetric's \"what\" is not present in the config");
+        return false;
+    }
+
+    const Predicate& durationWhat = config.predicate(what_it->second);
+    if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) {
+        ALOGE("DurationMetric's \"what\" must be a simple condition");
+        return false;
+    }
+
+    const SimplePredicate& simplePredicate = durationWhat.simple_predicate();
+
+    // Update indices: mStartIndex, mStopIndex, mStopAllIndex, mConditionIndex and MetricsManager
+    // maps.
+    if (!handleMetricWithAtomMatchingTrackers(simplePredicate.start(), metricIndex,
+                                              metric.has_dimensions_in_what(),
+                                              allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                              trackerToMetricMap, mStartIndex)) {
+        ALOGE("Duration metrics must specify a valid start event matcher");
+        return false;
+    }
+
+    if (simplePredicate.has_stop() &&
+        !handleMetricWithAtomMatchingTrackers(simplePredicate.stop(), metricIndex,
+                                              metric.has_dimensions_in_what(),
+                                              allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                              trackerToMetricMap, mStopIndex)) {
+        return false;
+    }
+
+    if (simplePredicate.has_stop_all() &&
+        !handleMetricWithAtomMatchingTrackers(simplePredicate.stop_all(), metricIndex,
+                                              metric.has_dimensions_in_what(),
+                                              allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                              trackerToMetricMap, mStopAllIndex)) {
+        return false;
+    }
+
+    if (metric.has_condition() &&
+        !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                    metric.links(), allConditionTrackers, mConditionTrackerIndex,
+                                    conditionToMetricMap)) {
+        return false;
+    }
+
+    for (const auto& it : mCurrentSlicedDurationTrackerMap) {
+        it.second->onConfigUpdated(wizard, mConditionTrackerIndex);
+    }
+
+    return true;
+}
+
+void DurationMetricProducer::initTrueDimensions(const int whatIndex, const int64_t startTimeNs) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    // Currently whatIndex will only be -1 in tests. In the future, we might want to avoid creating
+    // a ConditionTracker if the condition is only used in the "what" of a duration metric. In that
+    // scenario, -1 can also be passed.
+    if (whatIndex == -1) {
+        return;
+    }
+    const map<HashableDimensionKey, int>* slicedWhatMap = mWizard->getSlicedDimensionMap(whatIndex);
+    for (const auto& [internalDimKey, count] : *slicedWhatMap) {
+        for (int i = 0; i < count; i++) {
+            // Fake start events.
+            handleMatchedLogEventValuesLocked(mStartIndex, internalDimKey.getValues(), startTimeNs);
+        }
+    }
+}
+
 sp<AnomalyTracker> DurationMetricProducer::addAnomalyTracker(
         const Alert &alert, const sp<AlarmMonitor>& anomalyAlarmMonitor) {
     std::lock_guard<std::mutex> lock(mMutex);
@@ -152,14 +267,26 @@
             return nullptr;
         }
     }
-    sp<DurationAnomalyTracker> anomalyTracker =
-        new DurationAnomalyTracker(alert, mConfigKey, anomalyAlarmMonitor);
-    if (anomalyTracker != nullptr) {
-        mAnomalyTrackers.push_back(anomalyTracker);
-    }
+    sp<AnomalyTracker> anomalyTracker =
+            new DurationAnomalyTracker(alert, mConfigKey, anomalyAlarmMonitor);
+    addAnomalyTrackerLocked(anomalyTracker);
     return anomalyTracker;
 }
 
+// Adds an AnomalyTracker that has already been created.
+// Note: this gets called on config updates, and will only get called if the metric and the
+// associated alert are preserved, which means the AnomalyTracker must be a DurationAnomalyTracker.
+void DurationMetricProducer::addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker) {
+    std::lock_guard<std::mutex> lock(mMutex);
+    addAnomalyTrackerLocked(anomalyTracker);
+}
+
+void DurationMetricProducer::addAnomalyTrackerLocked(sp<AnomalyTracker>& anomalyTracker) {
+    mAnomalyTrackers.push_back(anomalyTracker);
+    for (const auto& [_, durationTracker] : mCurrentSlicedDurationTrackerMap) {
+        durationTracker->addAnomalyTracker(anomalyTracker);
+    }
+}
 void DurationMetricProducer::onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
                                             const HashableDimensionKey& primaryKey,
                                             const FieldValue& oldState,
@@ -171,6 +298,12 @@
 
     flushIfNeededLocked(eventTimeNs);
 
+    // Log late event and extra duration.
+    if (eventTimeNs < mCurrentBucketStartTimeNs) {
+        StatsdStats::getInstance().noteLateLogEvent(mMetricId,
+                                                    mCurrentBucketStartTimeNs - eventTimeNs);
+    }
+
     // Each duration tracker is mapped to a different whatKey (a set of values from the
     // dimensionsInWhat fields). We notify all trackers iff the primaryKey field values from the
     // state change event are a subset of the tracker's whatKey field values.
@@ -236,14 +369,14 @@
     // state based on the new unsliced condition state.
     if (dimensionsChangedToTrue == nullptr || dimensionsChangedToFalse == nullptr ||
         (dimensionsChangedToTrue->empty() && dimensionsChangedToFalse->empty())) {
-        std::set<HashableDimensionKey> trueConditionDimensions;
-        mWizard->getTrueSlicedDimensions(mConditionTrackerIndex, &trueConditionDimensions);
+        const map<HashableDimensionKey, int>* slicedConditionMap =
+                mWizard->getSlicedDimensionMap(mConditionTrackerIndex);
         for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
             HashableDimensionKey linkedConditionDimensionKey;
             getDimensionForCondition(whatIt.first.getValues(), mMetric2ConditionLinks[0],
                                      &linkedConditionDimensionKey);
-            if (trueConditionDimensions.find(linkedConditionDimensionKey) !=
-                    trueConditionDimensions.end()) {
+            const auto& slicedConditionIt = slicedConditionMap->find(linkedConditionDimensionKey);
+            if (slicedConditionIt != slicedConditionMap->end() && slicedConditionIt->second > 0) {
                 whatIt.second->onConditionChanged(currentUnSlicedPartCondition, eventTime);
             }
         }
@@ -289,6 +422,12 @@
         return;
     }
 
+    // Log late event and extra duration.
+    if (eventTime < mCurrentBucketStartTimeNs) {
+        StatsdStats::getInstance().noteLateLogEvent(mMetricId,
+                                                    mCurrentBucketStartTimeNs - eventTime);
+    }
+
     flushIfNeededLocked(eventTime);
 
     if (!mConditionSliced) {
@@ -306,6 +445,12 @@
             return;
         }
 
+        // Log late event and extra duration.
+        if (eventTimeNs < mCurrentBucketStartTimeNs) {
+            StatsdStats::getInstance().noteLateLogEvent(mMetricId,
+                                                        mCurrentBucketStartTimeNs - eventTimeNs);
+        }
+
         if (mIsActive) {
             flushIfNeededLocked(eventTimeNs);
         }
@@ -332,6 +477,12 @@
         return;
     }
 
+    // Log late event and extra duration.
+    if (eventTime < mCurrentBucketStartTimeNs) {
+        StatsdStats::getInstance().noteLateLogEvent(mMetricId,
+                                                    mCurrentBucketStartTimeNs - eventTime);
+    }
+
     flushIfNeededLocked(eventTime);
     for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
         whatIt.second->onConditionChanged(conditionMet, eventTime);
@@ -501,8 +652,9 @@
 }
 
 void DurationMetricProducer::handleStartEvent(const MetricDimensionKey& eventKey,
-                                              const ConditionKey& conditionKeys,
-                                              bool condition, const LogEvent& event) {
+                                              const ConditionKey& conditionKeys, bool condition,
+                                              const int64_t eventTimeNs,
+                                              const vector<FieldValue>& eventValues) {
     const auto& whatKey = eventKey.getDimensionKeyInWhat();
     auto whatIt = mCurrentSlicedDurationTrackerMap.find(whatKey);
     if (whatIt == mCurrentSlicedDurationTrackerMap.end()) {
@@ -514,20 +666,17 @@
 
     auto it = mCurrentSlicedDurationTrackerMap.find(whatKey);
     if (mUseWhatDimensionAsInternalDimension) {
-        it->second->noteStart(whatKey, condition, event.GetElapsedTimestampNs(), conditionKeys);
+        it->second->noteStart(whatKey, condition, eventTimeNs, conditionKeys);
         return;
     }
 
     if (mInternalDimensions.empty()) {
-        it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, event.GetElapsedTimestampNs(),
-                              conditionKeys);
+        it->second->noteStart(DEFAULT_DIMENSION_KEY, condition, eventTimeNs, conditionKeys);
     } else {
         HashableDimensionKey dimensionKey = DEFAULT_DIMENSION_KEY;
-        filterValues(mInternalDimensions, event.getValues(), &dimensionKey);
-        it->second->noteStart(dimensionKey, condition, event.GetElapsedTimestampNs(),
-                              conditionKeys);
+        filterValues(mInternalDimensions, eventValues, &dimensionKey);
+        it->second->noteStart(dimensionKey, condition, eventTimeNs, conditionKeys);
     }
-
 }
 
 void DurationMetricProducer::onMatchedLogEventInternalLocked(
@@ -539,26 +688,38 @@
 
 void DurationMetricProducer::onMatchedLogEventLocked(const size_t matcherIndex,
                                                      const LogEvent& event) {
-    int64_t eventTimeNs = event.GetElapsedTimestampNs();
+    handleMatchedLogEventValuesLocked(matcherIndex, event.getValues(),
+                                      event.GetElapsedTimestampNs());
+}
+
+void DurationMetricProducer::handleMatchedLogEventValuesLocked(const size_t matcherIndex,
+                                                               const vector<FieldValue>& values,
+                                                               const int64_t eventTimeNs) {
     if (eventTimeNs < mTimeBaseNs) {
         return;
     }
 
+    // Log late event and extra duration.
+    if (eventTimeNs < mCurrentBucketStartTimeNs) {
+        StatsdStats::getInstance().noteLateLogEvent(mMetricId,
+                                                    mCurrentBucketStartTimeNs - eventTimeNs);
+    }
+
     if (mIsActive) {
-        flushIfNeededLocked(event.GetElapsedTimestampNs());
+        flushIfNeededLocked(eventTimeNs);
     }
 
     // Handles Stopall events.
-    if (matcherIndex == mStopAllIndex) {
+    if ((int)matcherIndex == mStopAllIndex) {
         for (auto& whatIt : mCurrentSlicedDurationTrackerMap) {
-            whatIt.second->noteStopAll(event.GetElapsedTimestampNs());
+            whatIt.second->noteStopAll(eventTimeNs);
         }
         return;
     }
 
     HashableDimensionKey dimensionInWhat = DEFAULT_DIMENSION_KEY;
     if (!mDimensionsInWhat.empty()) {
-        filterValues(mDimensionsInWhat, event.getValues(), &dimensionInWhat);
+        filterValues(mDimensionsInWhat, values, &dimensionInWhat);
     }
 
     // Stores atom id to primary key pairs for each state atom that the metric is
@@ -569,8 +730,7 @@
     // field values from the log event. These values will form a primary key
     // that will be used to query StateTracker for the correct state value.
     for (const auto& stateLink : mMetric2StateLinks) {
-        getDimensionForState(event.getValues(), stateLink,
-                             &statePrimaryKeys[stateLink.stateAtomId]);
+        getDimensionForState(values, stateLink, &statePrimaryKeys[stateLink.stateAtomId]);
     }
 
     // For each sliced state, query StateTracker for the state value using
@@ -597,23 +757,23 @@
     }
 
     // Handles Stop events.
-    if (matcherIndex == mStopIndex) {
+    if ((int)matcherIndex == mStopIndex) {
         if (mUseWhatDimensionAsInternalDimension) {
             auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
             if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
-                whatIt->second->noteStop(dimensionInWhat, event.GetElapsedTimestampNs(), false);
+                whatIt->second->noteStop(dimensionInWhat, eventTimeNs, false);
             }
             return;
         }
 
         HashableDimensionKey internalDimensionKey = DEFAULT_DIMENSION_KEY;
         if (!mInternalDimensions.empty()) {
-            filterValues(mInternalDimensions, event.getValues(), &internalDimensionKey);
+            filterValues(mInternalDimensions, values, &internalDimensionKey);
         }
 
         auto whatIt = mCurrentSlicedDurationTrackerMap.find(dimensionInWhat);
         if (whatIt != mCurrentSlicedDurationTrackerMap.end()) {
-            whatIt->second->noteStop(internalDimensionKey, event.GetElapsedTimestampNs(), false);
+            whatIt->second->noteStop(internalDimensionKey, eventTimeNs, false);
         }
         return;
     }
@@ -622,7 +782,7 @@
     ConditionKey conditionKey;
     if (mConditionSliced) {
         for (const auto& link : mMetric2ConditionLinks) {
-            getDimensionForCondition(event.getValues(), link, &conditionKey[link.conditionId]);
+            getDimensionForCondition(values, link, &conditionKey[link.conditionId]);
         }
 
         auto conditionState =
@@ -637,7 +797,7 @@
     condition = condition && mIsActive;
 
     handleStartEvent(MetricDimensionKey(dimensionInWhat, stateValuesKey), conditionKey, condition,
-                     event);
+                     eventTimeNs, values);
 }
 
 size_t DurationMetricProducer::byteSizeLocked() const {
diff --git a/bin/src/metrics/DurationMetricProducer.h b/bin/src/metrics/DurationMetricProducer.h
index b46fb1f..5d3cd11 100644
--- a/bin/src/metrics/DurationMetricProducer.h
+++ b/bin/src/metrics/DurationMetricProducer.h
@@ -40,10 +40,11 @@
 public:
     DurationMetricProducer(
             const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex,
-            const vector<ConditionState>& initialConditionCache, const size_t startIndex,
-            const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
-            const sp<ConditionWizard>& wizard, const FieldMatcher& internalDimensions,
-            const int64_t timeBaseNs, const int64_t startTimeNs,
+            const vector<ConditionState>& initialConditionCache, const int whatIndex,
+            const int startIndex, const int stopIndex, const int stopAllIndex, const bool nesting,
+            const sp<ConditionWizard>& wizard, const uint64_t protoHash,
+            const FieldMatcher& internalDimensions, const int64_t timeBaseNs,
+            const int64_t startTimeNs,
             const unordered_map<int, shared_ptr<Activation>>& eventActivationMap = {},
             const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap = {},
             const vector<int>& slicedStateAtoms = {},
@@ -54,10 +55,16 @@
     sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
                                          const sp<AlarmMonitor>& anomalyAlarmMonitor) override;
 
+    void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker) override;
+
     void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
                         const HashableDimensionKey& primaryKey, const FieldValue& oldState,
                         const FieldValue& newState) override;
 
+    MetricType getMetricType() const override {
+        return METRIC_TYPE_DURATION;
+    }
+
 protected:
     void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) override;
 
@@ -67,8 +74,15 @@
             const std::map<int, HashableDimensionKey>& statePrimaryKeys) override;
 
 private:
+    // Initializes true dimensions of the 'what' predicate. Only to be called during initialization.
+    void initTrueDimensions(const int whatIndex, const int64_t startTimeNs);
+
+    void handleMatchedLogEventValuesLocked(const size_t matcherIndex,
+                                           const std::vector<FieldValue>& values,
+                                           const int64_t eventTimeNs);
     void handleStartEvent(const MetricDimensionKey& eventKey, const ConditionKey& conditionKeys,
-                          bool condition, const LogEvent& event);
+                          bool condition, const int64_t eventTimeNs,
+                          const vector<FieldValue>& eventValues);
 
     void onDumpReportLocked(const int64_t dumpTimeNs,
                             const bool include_current_partial_bucket,
@@ -107,16 +121,34 @@
     void flushCurrentBucketLocked(const int64_t& eventTimeNs,
                                   const int64_t& nextBucketStartTimeNs) override;
 
+    bool onConfigUpdatedLocked(
+            const StatsdConfig& config, const int configIndex, const int metricIndex,
+            const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+            const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+            const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+            const sp<EventMatcherWizard>& matcherWizard,
+            const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+            const std::unordered_map<int64_t, int>& conditionTrackerMap,
+            const sp<ConditionWizard>& wizard,
+            const std::unordered_map<int64_t, int>& metricToActivationMap,
+            std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+            std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+            std::vector<int>& metricsWithActivation) override;
+
+    void addAnomalyTrackerLocked(sp<AnomalyTracker>& anomalyTracker);
+
     const DurationMetric_AggregationType mAggregationType;
 
     // Index of the SimpleAtomMatcher which defines the start.
-    const size_t mStartIndex;
+    int mStartIndex;
 
     // Index of the SimpleAtomMatcher which defines the stop.
-    const size_t mStopIndex;
+    int mStopIndex;
 
     // Index of the SimpleAtomMatcher which defines the stop all for all dimensions.
-    const size_t mStopAllIndex;
+    int mStopAllIndex;
 
     // nest counting -- for the same key, stops must match the number of starts to make real stop
     const bool mNested;
@@ -143,9 +175,6 @@
     std::unique_ptr<DurationTracker> createDurationTracker(
             const MetricDimensionKey& eventKey) const;
 
-    // This hides the base class's std::vector<sp<AnomalyTracker>> mAnomalyTrackers
-    std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
-
     // Util function to check whether the specified dimension hits the guardrail.
     bool hitGuardRailLocked(const MetricDimensionKey& newKey);
 
@@ -162,6 +191,9 @@
                 TestSumDurationWithSplitInFollowingBucket);
     FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDuration);
     FRIEND_TEST(DurationMetricProducerTest_PartialBucket, TestMaxDurationWithSplitInNextBucket);
+
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts);
 };
 
 }  // namespace statsd
diff --git a/bin/src/metrics/EventMetricProducer.cpp b/bin/src/metrics/EventMetricProducer.cpp
index dc0036a..ca302c0 100644
--- a/bin/src/metrics/EventMetricProducer.cpp
+++ b/bin/src/metrics/EventMetricProducer.cpp
@@ -18,12 +18,14 @@
 #include "Log.h"
 
 #include "EventMetricProducer.h"
-#include "stats_util.h"
-#include "stats_log_util.h"
 
 #include <limits.h>
 #include <stdlib.h>
 
+#include "metrics/parsing_utils/metrics_manager_util.h"
+#include "stats_log_util.h"
+#include "stats_util.h"
+
 using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_BOOL;
 using android::util::FIELD_TYPE_FLOAT;
@@ -55,13 +57,14 @@
 EventMetricProducer::EventMetricProducer(
         const ConfigKey& key, const EventMetric& metric, const int conditionIndex,
         const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
-        const int64_t startTimeNs,
+        const uint64_t protoHash, const int64_t startTimeNs,
         const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
         const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
         const vector<int>& slicedStateAtoms,
         const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
     : MetricProducer(metric.id(), key, startTimeNs, conditionIndex, initialConditionCache, wizard,
-                     eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap) {
+                     protoHash, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
+                     stateGroupMap) {
     if (metric.links().size() > 0) {
         for (const auto& link : metric.links()) {
             Metric2Condition mc;
@@ -81,6 +84,47 @@
     VLOG("~EventMetricProducer() called");
 }
 
+bool EventMetricProducer::onConfigUpdatedLocked(
+        const StatsdConfig& config, const int configIndex, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    if (!MetricProducer::onConfigUpdatedLocked(
+                config, configIndex, metricIndex, allAtomMatchingTrackers,
+                oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
+                trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+                deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+        return false;
+    }
+
+    const EventMetric& metric = config.event_metric(configIndex);
+    int trackerIndex;
+    // Update appropriate indices, specifically mConditionIndex and MetricsManager maps.
+    if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false,
+                                              allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                              trackerToMetricMap, trackerIndex)) {
+        return false;
+    }
+
+    if (metric.has_condition() &&
+        !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                    metric.links(), allConditionTrackers, mConditionTrackerIndex,
+                                    conditionToMetricMap)) {
+        return false;
+    }
+    return true;
+}
+
 void EventMetricProducer::dropDataLocked(const int64_t dropTimeNs) {
     mProto->clear();
     StatsdStats::getInstance().noteBucketDropped(mMetricId);
diff --git a/bin/src/metrics/EventMetricProducer.h b/bin/src/metrics/EventMetricProducer.h
index f6fbadc..5236ebe 100644
--- a/bin/src/metrics/EventMetricProducer.h
+++ b/bin/src/metrics/EventMetricProducer.h
@@ -36,7 +36,7 @@
     EventMetricProducer(
             const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex,
             const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
-            const int64_t startTimeNs,
+            const uint64_t protoHash, const int64_t startTimeNs,
             const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
             const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
                     eventDeactivationMap = {},
@@ -45,6 +45,10 @@
 
     virtual ~EventMetricProducer();
 
+    MetricType getMetricType() const override {
+        return METRIC_TYPE_EVENT;
+    }
+
 private:
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const MetricDimensionKey& eventKey,
@@ -65,6 +69,22 @@
     // Internal interface to handle sliced condition change.
     void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
 
+    bool onConfigUpdatedLocked(
+            const StatsdConfig& config, const int configIndex, const int metricIndex,
+            const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+            const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+            const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+            const sp<EventMatcherWizard>& matcherWizard,
+            const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+            const std::unordered_map<int64_t, int>& conditionTrackerMap,
+            const sp<ConditionWizard>& wizard,
+            const std::unordered_map<int64_t, int>& metricToActivationMap,
+            std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+            std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+            std::vector<int>& metricsWithActivation) override;
+
     void dropDataLocked(const int64_t dropTimeNs) override;
 
     // Internal function to calculate the current used bytes.
diff --git a/bin/src/metrics/GaugeMetricProducer.cpp b/bin/src/metrics/GaugeMetricProducer.cpp
index 020f4b6..2a37b58 100644
--- a/bin/src/metrics/GaugeMetricProducer.cpp
+++ b/bin/src/metrics/GaugeMetricProducer.cpp
@@ -17,9 +17,11 @@
 #define DEBUG false  // STOPSHIP if true
 #include "Log.h"
 
-#include "../guardrail/StatsdStats.h"
 #include "GaugeMetricProducer.h"
-#include "../stats_log_util.h"
+
+#include "guardrail/StatsdStats.h"
+#include "metrics/parsing_utils/metrics_manager_util.h"
+#include "stats_log_util.h"
 
 using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_BOOL;
@@ -71,13 +73,14 @@
 GaugeMetricProducer::GaugeMetricProducer(
         const ConfigKey& key, const GaugeMetric& metric, const int conditionIndex,
         const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
-        const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard,
-        const int pullTagId, const int triggerAtomId, const int atomId, const int64_t timeBaseNs,
-        const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
+        const uint64_t protoHash, const int whatMatcherIndex,
+        const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int triggerAtomId,
+        const int atomId, const int64_t timeBaseNs, const int64_t startTimeNs,
+        const sp<StatsPullerManager>& pullerManager,
         const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
         const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache, wizard,
-                     eventActivationMap, eventDeactivationMap, /*slicedStateAtoms=*/{},
+                     protoHash, eventActivationMap, eventDeactivationMap, /*slicedStateAtoms=*/{},
                      /*stateGroupMap=*/{}),
       mWhatMatcherIndex(whatMatcherIndex),
       mEventMatcherWizard(matcherWizard),
@@ -153,6 +156,58 @@
     }
 }
 
+bool GaugeMetricProducer::onConfigUpdatedLocked(
+        const StatsdConfig& config, const int configIndex, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    if (!MetricProducer::onConfigUpdatedLocked(
+                config, configIndex, metricIndex, allAtomMatchingTrackers,
+                oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
+                trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+                deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+        return false;
+    }
+
+    const GaugeMetric& metric = config.gauge_metric(configIndex);
+    // Update appropriate indices: mWhatMatcherIndex, mConditionIndex and MetricsManager maps.
+    if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, /*enforceOneAtom=*/false,
+                                              allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                              trackerToMetricMap, mWhatMatcherIndex)) {
+        return false;
+    }
+
+    // Need to update maps since the index changed, but mTriggerAtomId will not change.
+    int triggerTrackerIndex;
+    if (metric.has_trigger_event() &&
+        !handleMetricWithAtomMatchingTrackers(metric.trigger_event(), metricIndex,
+                                              /*enforceOneAtom=*/true, allAtomMatchingTrackers,
+                                              newAtomMatchingTrackerMap, trackerToMetricMap,
+                                              triggerTrackerIndex)) {
+        return false;
+    }
+
+    if (metric.has_condition() &&
+        !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                    metric.links(), allConditionTrackers, mConditionTrackerIndex,
+                                    conditionToMetricMap)) {
+        return false;
+    }
+    sp<EventMatcherWizard> tmpEventWizard = mEventMatcherWizard;
+    mEventMatcherWizard = matcherWizard;
+    return true;
+}
+
 void GaugeMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
     if (mCurrentSlicedBucket == nullptr ||
         mCurrentSlicedBucket->size() == 0) {
diff --git a/bin/src/metrics/GaugeMetricProducer.h b/bin/src/metrics/GaugeMetricProducer.h
index 5aa6b87..751b487 100644
--- a/bin/src/metrics/GaugeMetricProducer.h
+++ b/bin/src/metrics/GaugeMetricProducer.h
@@ -53,16 +53,17 @@
 // This gauge metric producer first register the puller to automatically pull the gauge at the
 // beginning of each bucket. If the condition is met, insert it to the bucket info. Otherwise
 // proactively pull the gauge when the condition is changed to be true. Therefore, the gauge metric
-// producer always reports the guage at the earliest time of the bucket when the condition is met.
-class GaugeMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
+// producer always reports the gauge at the earliest time of the bucket when the condition is met.
+class GaugeMetricProducer : public MetricProducer, public virtual PullDataReceiver {
 public:
     GaugeMetricProducer(
             const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex,
             const vector<ConditionState>& initialConditionCache,
-            const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
-            const sp<EventMatcherWizard>& matcherWizard, const int pullTagId,
-            const int triggerAtomId, const int atomId, const int64_t timeBaseNs,
-            const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
+            const sp<ConditionWizard>& conditionWizard, const uint64_t protoHash,
+            const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard,
+            const int pullTagId, const int triggerAtomId, const int atomId,
+            const int64_t timeBaseNs, const int64_t startTimeNs,
+            const sp<StatsPullerManager>& pullerManager,
             const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
             const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
                     eventDeactivationMap = {});
@@ -96,6 +97,10 @@
         }
     };
 
+    MetricType getMetricType() const override {
+        return METRIC_TYPE_GAUGE;
+    }
+
 protected:
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const MetricDimensionKey& eventKey,
@@ -137,7 +142,23 @@
 
     void pullAndMatchEventsLocked(const int64_t timestampNs);
 
-    const int mWhatMatcherIndex;
+    bool onConfigUpdatedLocked(
+            const StatsdConfig& config, const int configIndex, const int metricIndex,
+            const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+            const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+            const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+            const sp<EventMatcherWizard>& matcherWizard,
+            const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+            const std::unordered_map<int64_t, int>& conditionTrackerMap,
+            const sp<ConditionWizard>& wizard,
+            const std::unordered_map<int64_t, int>& metricToActivationMap,
+            std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+            std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+            std::vector<int>& metricsWithActivation) override;
+
+    int mWhatMatcherIndex;
 
     sp<EventMatcherWizard> mEventMatcherWizard;
 
@@ -204,6 +225,8 @@
 
     FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPushedEvents);
     FRIEND_TEST(GaugeMetricProducerTest_PartialBucket, TestPulled);
+
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateGaugeMetrics);
 };
 
 }  // namespace statsd
diff --git a/bin/src/metrics/MetricProducer.cpp b/bin/src/metrics/MetricProducer.cpp
index fe143e4..c68e61e 100644
--- a/bin/src/metrics/MetricProducer.cpp
+++ b/bin/src/metrics/MetricProducer.cpp
@@ -20,6 +20,7 @@
 #include "MetricProducer.h"
 
 #include "../guardrail/StatsdStats.h"
+#include "metrics/parsing_utils/metrics_manager_util.h"
 #include "state/StateTracker.h"
 
 using android::util::FIELD_COUNT_REPEATED;
@@ -46,14 +47,16 @@
 MetricProducer::MetricProducer(
         const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
         const int conditionIndex, const vector<ConditionState>& initialConditionCache,
-        const sp<ConditionWizard>& wizard,
+        const sp<ConditionWizard>& wizard, const uint64_t protoHash,
         const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
         const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
                 eventDeactivationMap,
         const vector<int>& slicedStateAtoms,
         const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
     : mMetricId(metricId),
+      mProtoHash(protoHash),
       mConfigKey(key),
+      mValid(true),
       mTimeBaseNs(timeBaseNs),
       mCurrentBucketStartTimeNs(timeBaseNs),
       mCurrentBucketNum(0),
@@ -71,6 +74,38 @@
       mStateGroupMap(stateGroupMap) {
 }
 
+bool MetricProducer::onConfigUpdatedLocked(
+        const StatsdConfig& config, const int configIndex, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    sp<ConditionWizard> tmpWizard = mWizard;
+    mWizard = wizard;
+
+    unordered_map<int, shared_ptr<Activation>> newEventActivationMap;
+    unordered_map<int, vector<shared_ptr<Activation>>> newEventDeactivationMap;
+    if (!handleMetricActivationOnConfigUpdate(
+                config, mMetricId, metricIndex, metricToActivationMap, oldAtomMatchingTrackerMap,
+                newAtomMatchingTrackerMap, mEventActivationMap, activationAtomTrackerToMetricMap,
+                deactivationAtomTrackerToMetricMap, metricsWithActivation, newEventActivationMap,
+                newEventDeactivationMap)) {
+        return false;
+    }
+    mEventActivationMap = newEventActivationMap;
+    mEventDeactivationMap = newEventDeactivationMap;
+    mAnomalyTrackers.clear();
+    return true;
+}
+
 void MetricProducer::onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event) {
     if (!mIsActive) {
         return;
diff --git a/bin/src/metrics/MetricProducer.h b/bin/src/metrics/MetricProducer.h
index d1ea0a4..9a69028 100644
--- a/bin/src/metrics/MetricProducer.h
+++ b/bin/src/metrics/MetricProducer.h
@@ -26,6 +26,7 @@
 #include "anomaly/AnomalyTracker.h"
 #include "condition/ConditionWizard.h"
 #include "config/ConfigKey.h"
+#include "matchers/EventMatcherWizard.h"
 #include "matchers/matcher_util.h"
 #include "packages/PackageInfoListener.h"
 #include "state/StateListener.h"
@@ -87,6 +88,13 @@
     NO_DATA = 9
 };
 
+enum MetricType {
+    METRIC_TYPE_EVENT = 1,
+    METRIC_TYPE_COUNT = 2,
+    METRIC_TYPE_DURATION = 3,
+    METRIC_TYPE_GAUGE = 4,
+    METRIC_TYPE_VALUE = 5,
+};
 struct Activation {
     Activation(const ActivationType& activationType, const int64_t ttlNs)
         : ttl_ns(ttlNs),
@@ -130,7 +138,7 @@
 public:
     MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
                    const int conditionIndex, const vector<ConditionState>& initialConditionCache,
-                   const sp<ConditionWizard>& wizard,
+                   const sp<ConditionWizard>& wizard, const uint64_t protoHash,
                    const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
                    const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
                            eventDeactivationMap,
@@ -144,6 +152,34 @@
         return conditionIndex >= 0 ? initialConditionCache[conditionIndex] : ConditionState::kTrue;
     }
 
+    // Update appropriate state on config updates. Primarily, all indices need to be updated.
+    // This metric and all of its dependencies are guaranteed to be preserved across the update.
+    // This function also updates several maps used by metricsManager.
+    // This function clears all anomaly trackers. All anomaly trackers need to be added again.
+    bool onConfigUpdated(
+            const StatsdConfig& config, const int configIndex, const int metricIndex,
+            const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+            const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+            const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+            const sp<EventMatcherWizard>& matcherWizard,
+            const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+            const std::unordered_map<int64_t, int>& conditionTrackerMap,
+            const sp<ConditionWizard>& wizard,
+            const std::unordered_map<int64_t, int>& metricToActivationMap,
+            std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+            std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+            std::vector<int>& metricsWithActivation) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        return onConfigUpdatedLocked(config, configIndex, metricIndex, allAtomMatchingTrackers,
+                                     oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap,
+                                     matcherWizard, allConditionTrackers, conditionTrackerMap,
+                                     wizard, metricToActivationMap, trackerToMetricMap,
+                                     conditionToMetricMap, activationAtomTrackerToMetricMap,
+                                     deactivationAtomTrackerToMetricMap, metricsWithActivation);
+    };
+
     /**
      * Force a partial bucket split on app upgrade
      */
@@ -202,6 +238,22 @@
                 dumpLatency, str_set, protoOutput);
     }
 
+    virtual bool onConfigUpdatedLocked(
+            const StatsdConfig& config, const int configIndex, const int metricIndex,
+            const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+            const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+            const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+            const sp<EventMatcherWizard>& matcherWizard,
+            const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+            const std::unordered_map<int64_t, int>& conditionTrackerMap,
+            const sp<ConditionWizard>& wizard,
+            const std::unordered_map<int64_t, int>& metricToActivationMap,
+            std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+            std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+            std::vector<int>& metricsWithActivation);
+
     void clearPastBuckets(const int64_t dumpTimeNs) {
         std::lock_guard<std::mutex> lock(mMutex);
         return clearPastBucketsLocked(dumpTimeNs);
@@ -259,10 +311,16 @@
             int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
 
     // Start: getters/setters
-    inline const int64_t& getMetricId() const {
+    inline int64_t getMetricId() const {
         return mMetricId;
     }
 
+    inline uint64_t getProtoHash() const {
+        return mProtoHash;
+    }
+
+    virtual MetricType getMetricType() const = 0;
+
     // For test only.
     inline int64_t getCurrentBucketNum() const {
         return mCurrentBucketNum;
@@ -278,16 +336,24 @@
         return mSlicedStateAtoms;
     }
 
-    /* If alert is valid, adds an AnomalyTracker and returns it. If invalid, returns nullptr. */
+    inline bool isValid() const {
+        return mValid;
+    }
+
+    /* Adds an AnomalyTracker and returns it. */
     virtual sp<AnomalyTracker> addAnomalyTracker(const Alert &alert,
                                                  const sp<AlarmMonitor>& anomalyAlarmMonitor) {
         std::lock_guard<std::mutex> lock(mMutex);
         sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, mConfigKey);
-        if (anomalyTracker != nullptr) {
-            mAnomalyTrackers.push_back(anomalyTracker);
-        }
+        mAnomalyTrackers.push_back(anomalyTracker);
         return anomalyTracker;
     }
+
+    /* Adds an AnomalyTracker that has already been created */
+    virtual void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        mAnomalyTrackers.push_back(anomalyTracker);
+    }
     // End: getters/setters
 protected:
     /**
@@ -400,8 +466,14 @@
 
     const int64_t mMetricId;
 
+    // Hash of the Metric's proto bytes from StatsdConfig, including any activations.
+    // Used to determine if the definition of this metric has changed across a config update.
+    const uint64_t mProtoHash;
+
     const ConfigKey mConfigKey;
 
+    bool mValid;
+
     // The time when this metric producer was first created. The end time for the current bucket
     // can be computed from this based on mCurrentBucketNum.
     int64_t mTimeBaseNs;
@@ -500,6 +572,14 @@
     FRIEND_TEST(ValueMetricE2eTest, TestInitialConditionChanges);
 
     FRIEND_TEST(MetricsManagerTest, TestInitialConditions);
+
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricActivations);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateCountMetrics);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateEventMetrics);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateGaugeMetrics);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateMetricsMultipleTypes);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts);
 };
 
 }  // namespace statsd
diff --git a/bin/src/metrics/MetricsManager.cpp b/bin/src/metrics/MetricsManager.cpp
index 2d5aa56..f9b0a10 100644
--- a/bin/src/metrics/MetricsManager.cpp
+++ b/bin/src/metrics/MetricsManager.cpp
@@ -24,9 +24,10 @@
 #include "condition/CombinationConditionTracker.h"
 #include "condition/SimpleConditionTracker.h"
 #include "guardrail/StatsdStats.h"
-#include "matchers/CombinationLogMatchingTracker.h"
-#include "matchers/SimpleLogMatchingTracker.h"
-#include "metrics_manager_util.h"
+#include "matchers/CombinationAtomMatchingTracker.h"
+#include "matchers/SimpleAtomMatchingTracker.h"
+#include "parsing_utils/config_update_utils.h"
+#include "parsing_utils/metrics_manager_util.h"
 #include "state/StateManager.h"
 #include "stats_log_util.h"
 #include "stats_util.h"
@@ -77,22 +78,119 @@
     refreshTtl(timeBaseNs);
 
     mConfigValid = initStatsdConfig(
-            key, config, *uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchers, mAllConditionTrackers,
-            mAllMetricProducers, mAllAnomalyTrackers, mAllPeriodicAlarmTrackers,
-            mConditionToMetricMap, mTrackerToMetricMap, mTrackerToConditionMap,
-            mActivationAtomTrackerToMetricMap, mDeactivationAtomTrackerToMetricMap,
-            mAlertTrackerMap, mMetricIndexesWithActivation, mNoReportMetricIds);
+            key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+            timeBaseNs, currentTimeNs, mTagIds, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap,
+            mAllConditionTrackers, mConditionTrackerMap, mAllMetricProducers, mMetricProducerMap,
+            mAllAnomalyTrackers, mAllPeriodicAlarmTrackers, mConditionToMetricMap,
+            mTrackerToMetricMap, mTrackerToConditionMap, mActivationAtomTrackerToMetricMap,
+            mDeactivationAtomTrackerToMetricMap, mAlertTrackerMap, mMetricIndexesWithActivation,
+            mStateProtoHashes, mNoReportMetricIds);
 
     mHashStringsInReport = config.hash_strings_in_metric_report();
     mVersionStringsInReport = config.version_strings_in_metric_report();
     mInstallerInReport = config.installer_in_metric_report();
 
+    createAllLogSourcesFromConfig(config);
+    mPullerManager->RegisterPullUidProvider(mConfigKey, this);
+
+    // Store the sub-configs used.
+    for (const auto& annotation : config.annotation()) {
+        mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32());
+    }
+    verifyGuardrailsAndUpdateStatsdStats();
+    initializeConfigActiveStatus();
+}
+
+MetricsManager::~MetricsManager() {
+    for (auto it : mAllMetricProducers) {
+        for (int atomId : it->getSlicedStateAtoms()) {
+            StateManager::getInstance().unregisterListener(atomId, it);
+        }
+    }
+    mPullerManager->UnregisterPullUidProvider(mConfigKey, this);
+
+    VLOG("~MetricsManager()");
+}
+
+bool MetricsManager::updateConfig(const StatsdConfig& config, const int64_t timeBaseNs,
+                                  const int64_t currentTimeNs,
+                                  const sp<AlarmMonitor>& anomalyAlarmMonitor,
+                                  const sp<AlarmMonitor>& periodicAlarmMonitor) {
+    vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers;
+    unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    vector<sp<ConditionTracker>> newConditionTrackers;
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    map<int64_t, uint64_t> newStateProtoHashes;
+    vector<sp<MetricProducer>> newMetricProducers;
+    unordered_map<int64_t, int> newMetricProducerMap;
+    vector<sp<AnomalyTracker>> newAnomalyTrackers;
+    unordered_map<int64_t, int> newAlertTrackerMap;
+    vector<sp<AlarmTracker>> newPeriodicAlarmTrackers;
+    mTagIds.clear();
+    mConditionToMetricMap.clear();
+    mTrackerToMetricMap.clear();
+    mTrackerToConditionMap.clear();
+    mActivationAtomTrackerToMetricMap.clear();
+    mDeactivationAtomTrackerToMetricMap.clear();
+    mMetricIndexesWithActivation.clear();
+    mNoReportMetricIds.clear();
+    mConfigValid = updateStatsdConfig(
+            mConfigKey, config, mUidMap, mPullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+            timeBaseNs, currentTimeNs, mAllAtomMatchingTrackers, mAtomMatchingTrackerMap,
+            mAllConditionTrackers, mConditionTrackerMap, mAllMetricProducers, mMetricProducerMap,
+            mAllAnomalyTrackers, mAlertTrackerMap, mStateProtoHashes, mTagIds,
+            newAtomMatchingTrackers, newAtomMatchingTrackerMap, newConditionTrackers,
+            newConditionTrackerMap, newMetricProducers, newMetricProducerMap, newAnomalyTrackers,
+            newAlertTrackerMap, newPeriodicAlarmTrackers, mConditionToMetricMap,
+            mTrackerToMetricMap, mTrackerToConditionMap, mActivationAtomTrackerToMetricMap,
+            mDeactivationAtomTrackerToMetricMap, mMetricIndexesWithActivation, newStateProtoHashes,
+            mNoReportMetricIds);
+    mAllAtomMatchingTrackers = newAtomMatchingTrackers;
+    mAtomMatchingTrackerMap = newAtomMatchingTrackerMap;
+    mAllConditionTrackers = newConditionTrackers;
+    mConditionTrackerMap = newConditionTrackerMap;
+    mAllMetricProducers = newMetricProducers;
+    mMetricProducerMap = newMetricProducerMap;
+    mStateProtoHashes = newStateProtoHashes;
+    mAllAnomalyTrackers = newAnomalyTrackers;
+    mAlertTrackerMap = newAlertTrackerMap;
+    mAllPeriodicAlarmTrackers = newPeriodicAlarmTrackers;
+
+    mTtlNs = config.has_ttl_in_seconds() ? config.ttl_in_seconds() * NS_PER_SEC : -1;
+    refreshTtl(currentTimeNs);
+
+    mHashStringsInReport = config.hash_strings_in_metric_report();
+    mVersionStringsInReport = config.version_strings_in_metric_report();
+    mInstallerInReport = config.installer_in_metric_report();
+    mWhitelistedAtomIds.clear();
+    mWhitelistedAtomIds.insert(config.whitelisted_atom_ids().begin(),
+                               config.whitelisted_atom_ids().end());
+    mShouldPersistHistory = config.persist_locally();
+
+    // Store the sub-configs used.
+    mAnnotations.clear();
+    for (const auto& annotation : config.annotation()) {
+        mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32());
+    }
+
+    mAllowedUid.clear();
+    mAllowedPkg.clear();
+    mDefaultPullUids.clear();
+    mPullAtomUids.clear();
+    mPullAtomPackages.clear();
+    createAllLogSourcesFromConfig(config);
+
+    verifyGuardrailsAndUpdateStatsdStats();
+    initializeConfigActiveStatus();
+    return mConfigValid;
+}
+
+void MetricsManager::createAllLogSourcesFromConfig(const StatsdConfig& config) {
     // Init allowed pushed atom uids.
     if (config.allowed_log_source_size() == 0) {
         mConfigValid = false;
         ALOGE("Log source allowlist is empty! This config won't get any data. Suggest adding at "
-                      "least AID_SYSTEM and AID_STATSD to the allowed_log_source field.");
+              "least AID_SYSTEM and AID_STATSD to the allowed_log_source field.");
     } else {
         for (const auto& source : config.allowed_log_source()) {
             auto it = UidMap::sAidToUidMapping.find(source);
@@ -107,7 +205,7 @@
             ALOGE("Too many log sources. This is likely to be an error in the config.");
             mConfigValid = false;
         } else {
-            initLogSourceWhiteList();
+            initAllowedLogSources();
         }
     }
 
@@ -143,17 +241,13 @@
     } else {
         initPullAtomSources();
     }
-    mPullerManager->RegisterPullUidProvider(mConfigKey, this);
+}
 
-    // Store the sub-configs used.
-    for (const auto& annotation : config.annotation()) {
-        mAnnotations.emplace_back(annotation.field_int64(), annotation.field_int32());
-    }
-
+void MetricsManager::verifyGuardrailsAndUpdateStatsdStats() {
     // Guardrail. Reject the config if it's too big.
     if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig ||
         mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig ||
-        mAllAtomMatchers.size() > StatsdStats::kMaxMatcherCountPerConfig) {
+        mAllAtomMatchingTrackers.size() > StatsdStats::kMaxMatcherCountPerConfig) {
         ALOGE("This config is too big! Reject!");
         mConfigValid = false;
     }
@@ -161,41 +255,24 @@
         ALOGE("This config has too many alerts! Reject!");
         mConfigValid = false;
     }
-
-    mIsAlwaysActive = (mMetricIndexesWithActivation.size() != mAllMetricProducers.size()) ||
-            (mAllMetricProducers.size() == 0);
-    bool isActive = mIsAlwaysActive;
-    for (int metric : mMetricIndexesWithActivation) {
-        isActive |= mAllMetricProducers[metric]->isActive();
-    }
-    mIsActive = isActive;
-    VLOG("mIsActive is initialized to %d", mIsActive)
-
     // no matter whether this config is valid, log it in the stats.
     StatsdStats::getInstance().noteConfigReceived(
-            key, mAllMetricProducers.size(), mAllConditionTrackers.size(), mAllAtomMatchers.size(),
-            mAllAnomalyTrackers.size(), mAnnotations, mConfigValid);
-    // Check active
-    for (const auto& metric : mAllMetricProducers) {
-        if (metric->isActive()) {
-            mIsActive = true;
-            break;
-        }
-    }
+            mConfigKey, mAllMetricProducers.size(), mAllConditionTrackers.size(),
+            mAllAtomMatchingTrackers.size(), mAllAnomalyTrackers.size(), mAnnotations,
+            mConfigValid);
 }
 
-MetricsManager::~MetricsManager() {
-    for (auto it : mAllMetricProducers) {
-        for (int atomId : it->getSlicedStateAtoms()) {
-            StateManager::getInstance().unregisterListener(atomId, it);
-        }
+void MetricsManager::initializeConfigActiveStatus() {
+    mIsAlwaysActive = (mMetricIndexesWithActivation.size() != mAllMetricProducers.size()) ||
+                      (mAllMetricProducers.size() == 0);
+    mIsActive = mIsAlwaysActive;
+    for (int metric : mMetricIndexesWithActivation) {
+        mIsActive |= mAllMetricProducers[metric]->isActive();
     }
-    mPullerManager->UnregisterPullUidProvider(mConfigKey, this);
-
-    VLOG("~MetricsManager()");
+    VLOG("mIsActive is initialized to %d", mIsActive);
 }
 
-void MetricsManager::initLogSourceWhiteList() {
+void MetricsManager::initAllowedLogSources() {
     std::lock_guard<std::mutex> lock(mAllowedLogSourcesMutex);
     mAllowedLogSources.clear();
     mAllowedLogSources.insert(mAllowedUid.begin(), mAllowedUid.end());
@@ -239,7 +316,7 @@
     if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) {
         // We will re-initialize the whole list because we don't want to keep the multi mapping of
         // UID<->pkg inside MetricsManager to reduce the memory usage.
-        initLogSourceWhiteList();
+        initAllowedLogSources();
     }
 
     for (const auto& it : mPullAtomPackages) {
@@ -260,7 +337,7 @@
     if (std::find(mAllowedPkg.begin(), mAllowedPkg.end(), apk) != mAllowedPkg.end()) {
         // We will re-initialize the whole list because we don't want to keep the multi mapping of
         // UID<->pkg inside MetricsManager to reduce the memory usage.
-        initLogSourceWhiteList();
+        initAllowedLogSources();
     }
 
     for (const auto& it : mPullAtomPackages) {
@@ -280,7 +357,7 @@
     if (mAllowedPkg.size() == 0) {
         return;
     }
-    initLogSourceWhiteList();
+    initAllowedLogSources();
 }
 
 void MetricsManager::onStatsdInitCompleted(const int64_t& eventTimeNs) {
@@ -484,11 +561,12 @@
         return;
     }
 
-    vector<MatchingState> matcherCache(mAllAtomMatchers.size(), MatchingState::kNotComputed);
+    vector<MatchingState> matcherCache(mAllAtomMatchingTrackers.size(),
+                                       MatchingState::kNotComputed);
 
     // Evaluate all atom matchers.
-    for (auto& matcher : mAllAtomMatchers) {
-        matcher->onLogEvent(event, mAllAtomMatchers, matcherCache);
+    for (auto& matcher : mAllAtomMatchingTrackers) {
+        matcher->onLogEvent(event, mAllAtomMatchingTrackers, matcherCache);
     }
 
     // Set of metrics that received an activation cancellation.
@@ -578,10 +656,10 @@
     }
 
     // For matched AtomMatchers, tell relevant metrics that a matched event has come.
-    for (size_t i = 0; i < mAllAtomMatchers.size(); i++) {
+    for (size_t i = 0; i < mAllAtomMatchingTrackers.size(); i++) {
         if (matcherCache[i] == MatchingState::kMatched) {
             StatsdStats::getInstance().noteMatcherMatched(mConfigKey,
-                                                          mAllAtomMatchers[i]->getId());
+                                                          mAllAtomMatchingTrackers[i]->getId());
             auto pair = mTrackerToMetricMap.find(i);
             if (pair != mTrackerToMetricMap.end()) {
                 auto& metricList = pair->second;
diff --git a/bin/src/metrics/MetricsManager.h b/bin/src/metrics/MetricsManager.h
index d17a1a8..a7b6165 100644
--- a/bin/src/metrics/MetricsManager.h
+++ b/bin/src/metrics/MetricsManager.h
@@ -25,7 +25,7 @@
 #include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
 #include "packages/modules/StatsD/bin/src/statsd_metadata.pb.h"
 #include "logd/LogEvent.h"
-#include "matchers/LogMatchingTracker.h"
+#include "matchers/AtomMatchingTracker.h"
 #include "metrics/MetricProducer.h"
 #include "packages/UidMap.h"
 
@@ -46,6 +46,10 @@
 
     virtual ~MetricsManager();
 
+    bool updateConfig(const StatsdConfig& config, const int64_t timeBaseNs,
+                      const int64_t currentTimeNs, const sp<AlarmMonitor>& anomalyAlarmMonitor,
+                      const sp<AlarmMonitor>& periodicAlarmMonitor);
+
     // Return whether the configuration is valid.
     bool isConfigValid() const;
 
@@ -168,7 +172,7 @@
     bool mVersionStringsInReport = false;
     bool mInstallerInReport = false;
 
-    const int64_t mTtlNs;
+    int64_t mTtlNs;
     int64_t mTtlEndNs;
 
     int64_t mLastReportTimeNs;
@@ -189,7 +193,7 @@
     // To guard access to mAllowedLogSources
     mutable std::mutex mAllowedLogSourcesMutex;
 
-    const std::set<int32_t> mWhitelistedAtomIds;
+    std::set<int32_t> mWhitelistedAtomIds;
 
     // We can pull any atom from these uids.
     std::set<int32_t> mDefaultPullUids;
@@ -207,13 +211,12 @@
     // Contains the annotations passed in with StatsdConfig.
     std::list<std::pair<const int64_t, const int32_t>> mAnnotations;
 
-    const bool mShouldPersistHistory;
-
+    bool mShouldPersistHistory;
 
     // All event tags that are interesting to my metrics.
     std::set<int> mTagIds;
 
-    // We only store the sp of LogMatchingTracker, MetricProducer, and ConditionTracker in
+    // We only store the sp of AtomMatchingTracker, MetricProducer, and ConditionTracker in
     // MetricsManager. There are relationships between them, and the relationships are denoted by
     // index instead of pointers. The reasons for this are: (1) the relationship between them are
     // complicated, so storing index instead of pointers reduces the risk that A holds B's sp, and B
@@ -221,7 +224,7 @@
     // the related results from a cache using the index.
 
     // Hold all the atom matchers from the config.
-    std::vector<sp<LogMatchingTracker>> mAllAtomMatchers;
+    std::vector<sp<AtomMatchingTracker>> mAllAtomMatchingTrackers;
 
     // Hold all the conditions from the config.
     std::vector<sp<ConditionTracker>> mAllConditionTrackers;
@@ -235,23 +238,35 @@
     // Hold all periodic alarm trackers.
     std::vector<sp<AlarmTracker>> mAllPeriodicAlarmTrackers;
 
+    // To make updating configs faster, we map the id of a AtomMatchingTracker, MetricProducer, and
+    // ConditionTracker to its index in the corresponding vector.
+
+    // Maps the id of an atom matching tracker to its index in mAllAtomMatchingTrackers.
+    std::unordered_map<int64_t, int> mAtomMatchingTrackerMap;
+
+    // Maps the id of a condition tracker to its index in mAllConditionTrackers.
+    std::unordered_map<int64_t, int> mConditionTrackerMap;
+
+    // Maps the id of a metric producer to its index in mAllMetricProducers.
+    std::unordered_map<int64_t, int> mMetricProducerMap;
+
     // To make the log processing more efficient, we want to do as much filtering as possible
     // before we go into individual trackers and conditions to match.
 
     // 1st filter: check if the event tag id is in mTagIds.
     // 2nd filter: if it is, we parse the event because there is at least one member is interested.
-    //             then pass to all LogMatchingTrackers (itself also filter events by ids).
-    // 3nd filter: for LogMatchingTrackers that matched this event, we pass this event to the
+    //             then pass to all AtomMatchingTrackers (itself also filter events by ids).
+    // 3nd filter: for AtomMatchingTrackers that matched this event, we pass this event to the
     //             ConditionTrackers and MetricProducers that use this matcher.
     // 4th filter: for ConditionTrackers that changed value due to this event, we pass
     //             new conditions to  metrics that use this condition.
 
     // The following map is initialized from the statsd_config.
 
-    // Maps from the index of the LogMatchingTracker to index of MetricProducer.
+    // Maps from the index of the AtomMatchingTracker to index of MetricProducer.
     std::unordered_map<int, std::vector<int>> mTrackerToMetricMap;
 
-    // Maps from LogMatchingTracker to ConditionTracker
+    // Maps from AtomMatchingTracker to ConditionTracker
     std::unordered_map<int, std::vector<int>> mTrackerToConditionMap;
 
     // Maps from ConditionTracker to MetricProducer
@@ -269,10 +284,22 @@
 
     std::vector<int> mMetricIndexesWithActivation;
 
-    void initLogSourceWhiteList();
+    void initAllowedLogSources();
 
     void initPullAtomSources();
 
+    // Only called on config creation/update to initialize log sources from the config.
+    // Calls initAllowedLogSources and initPullAtomSources. Sets mConfigValid to false on error.
+    void createAllLogSourcesFromConfig(const StatsdConfig& config);
+
+    // Verifies the config meets guardrails and updates statsdStats.
+    // Sets mConfigValid to false on error. Should be called on config creation/update
+    void verifyGuardrailsAndUpdateStatsdStats();
+
+    // Initializes mIsAlwaysActive and mIsActive.
+    // Should be called on config creation/update.
+    void initializeConfigActiveStatus();
+
     // The metrics that don't need to be uploaded or even reported.
     std::set<int64_t> mNoReportMetricIds;
 
@@ -282,6 +309,9 @@
     // The config is always active if any metric in the config does not have an activation signal.
     bool mIsAlwaysActive;
 
+    // Hashes of the States used in this config, keyed by the state id, used in config updates.
+    std::map<int64_t, uint64_t> mStateProtoHashes;
+
     FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions);
     FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
     FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSliceByFirstUid);
@@ -304,6 +334,7 @@
 
     FRIEND_TEST(AlarmE2eTest, TestMultipleAlarms);
     FRIEND_TEST(ConfigTtlE2eTest, TestCountMetric);
+    FRIEND_TEST(ConfigUpdateE2eAbTest, TestConfigTtl);
     FRIEND_TEST(MetricActivationE2eTest, TestCountMetric);
     FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithOneDeactivation);
     FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoDeactivations);
@@ -311,6 +342,7 @@
     FRIEND_TEST(MetricActivationE2eTest, TestCountMetricWithTwoMetricsTwoDeactivations);
 
     FRIEND_TEST(MetricsManagerTest, TestLogSources);
+    FRIEND_TEST(MetricsManagerTest, TestLogSourcesOnConfigUpdate);
 
     FRIEND_TEST(StatsLogProcessorTest, TestActiveConfigMetricDiskWriteRead);
     FRIEND_TEST(StatsLogProcessorTest, TestActivationOnBoot);
diff --git a/bin/src/metrics/ValueMetricProducer.cpp b/bin/src/metrics/ValueMetricProducer.cpp
index 5987a72..22fdf16 100644
--- a/bin/src/metrics/ValueMetricProducer.cpp
+++ b/bin/src/metrics/ValueMetricProducer.cpp
@@ -18,12 +18,14 @@
 #include "Log.h"
 
 #include "ValueMetricProducer.h"
-#include "../guardrail/StatsdStats.h"
-#include "../stats_log_util.h"
 
 #include <limits.h>
 #include <stdlib.h>
 
+#include "../guardrail/StatsdStats.h"
+#include "../stats_log_util.h"
+#include "metrics/parsing_utils/metrics_manager_util.h"
+
 using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_BOOL;
 using android::util::FIELD_TYPE_DOUBLE;
@@ -79,16 +81,17 @@
 ValueMetricProducer::ValueMetricProducer(
         const ConfigKey& key, const ValueMetric& metric, const int conditionIndex,
         const vector<ConditionState>& initialConditionCache,
-        const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
-        const sp<EventMatcherWizard>& matcherWizard, const int pullTagId, const int64_t timeBaseNs,
-        const int64_t startTimeNs, const sp<StatsPullerManager>& pullerManager,
+        const sp<ConditionWizard>& conditionWizard, const uint64_t protoHash,
+        const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard,
+        const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
+        const sp<StatsPullerManager>& pullerManager,
         const unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
         const unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap,
         const vector<int>& slicedStateAtoms,
         const unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap)
     : MetricProducer(metric.id(), key, timeBaseNs, conditionIndex, initialConditionCache,
-                     conditionWizard, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
-                     stateGroupMap),
+                     conditionWizard, protoHash, eventActivationMap, eventDeactivationMap,
+                     slicedStateAtoms, stateGroupMap),
       mWhatMatcherIndex(whatMatcherIndex),
       mEventMatcherWizard(matcherWizard),
       mPullerManager(pullerManager),
@@ -183,6 +186,48 @@
     }
 }
 
+bool ValueMetricProducer::onConfigUpdatedLocked(
+        const StatsdConfig& config, const int configIndex, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    if (!MetricProducer::onConfigUpdatedLocked(
+                config, configIndex, metricIndex, allAtomMatchingTrackers,
+                oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                allConditionTrackers, conditionTrackerMap, wizard, metricToActivationMap,
+                trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+                deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+        return false;
+    }
+
+    const ValueMetric& metric = config.value_metric(configIndex);
+    // Update appropriate indices: mWhatMatcherIndex, mConditionIndex and MetricsManager maps.
+    if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, /*enforceOneAtom=*/false,
+                                              allAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                              trackerToMetricMap, mWhatMatcherIndex)) {
+        return false;
+    }
+
+    if (metric.has_condition() &&
+        !handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                    metric.links(), allConditionTrackers, mConditionTrackerIndex,
+                                    conditionToMetricMap)) {
+        return false;
+    }
+    sp<EventMatcherWizard> tmpEventWizard = mEventMatcherWizard;
+    mEventMatcherWizard = matcherWizard;
+    return true;
+}
+
 void ValueMetricProducer::onStateChanged(int64_t eventTimeNs, int32_t atomId,
                                          const HashableDimensionKey& primaryKey,
                                          const FieldValue& oldState, const FieldValue& newState) {
@@ -300,7 +345,6 @@
             protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_BUCKET_DROP_REASON, dropEvent.reason);
             protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_DROP_TIME,
                                (long long)(NanoToMillis(dropEvent.dropTimeNs)));
-            ;
             protoOutput->end(dropEventToken);
         }
         protoOutput->end(wrapperToken);
@@ -345,8 +389,11 @@
                 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_NUM,
                                    (long long)(getBucketNumFromEndTimeNs(bucket.mBucketEndNs)));
             }
-            // only write the condition timer value if the metric has a condition.
-            if (mConditionTrackerIndex >= 0) {
+            // We only write the condition timer value if the metric has a
+            // condition and/or is sliced by state.
+            // If the metric is sliced by state, the condition timer value is
+            // also sliced by state to reflect time spent in that state.
+            if (mConditionTrackerIndex >= 0 || !mSlicedStateAtoms.empty()) {
                 protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_TRUE_NS,
                                    (long long)bucket.mConditionTrueNs);
             }
@@ -411,7 +458,7 @@
 
 void ValueMetricProducer::resetBase() {
     for (auto& slice : mCurrentBaseInfo) {
-        for (auto& baseInfo : slice.second) {
+        for (auto& baseInfo : slice.second.baseInfos) {
             baseInfo.hasBase = false;
         }
     }
@@ -453,6 +500,8 @@
 
     // Let condition timer know of new active state.
     mConditionTimer.onConditionChanged(mIsActive, eventTimeNs);
+
+    updateCurrentSlicedBucketConditionTimers(mIsActive, eventTimeNs);
 }
 
 void ValueMetricProducer::onConditionChangedLocked(const bool condition,
@@ -475,6 +524,8 @@
         invalidateCurrentBucket(eventTimeNs, BucketDropReason::EVENT_IN_WRONG_BUCKET);
         mCondition = ConditionState::kUnknown;
         mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
+
+        updateCurrentSlicedBucketConditionTimers(mCondition, eventTimeNs);
         return;
     }
 
@@ -516,6 +567,29 @@
 
     flushIfNeededLocked(eventTimeNs);
     mConditionTimer.onConditionChanged(mCondition, eventTimeNs);
+
+    updateCurrentSlicedBucketConditionTimers(mCondition, eventTimeNs);
+}
+
+void ValueMetricProducer::updateCurrentSlicedBucketConditionTimers(bool newCondition,
+                                                                   int64_t eventTimeNs) {
+    if (mSlicedStateAtoms.empty()) {
+        return;
+    }
+
+    // Utilize the current state key of each DimensionsInWhat key to determine
+    // which condition timers to update.
+    //
+    // Assumes that the MetricDimensionKey exists in `mCurrentSlicedBucket`.
+    bool inPulledData;
+    for (const auto& [dimensionInWhatKey, dimensionInWhatInfo] : mCurrentBaseInfo) {
+        // If the new condition is true, turn ON the condition timer only if
+        // the DimensionInWhat key was present in the pulled data.
+        inPulledData = dimensionInWhatInfo.hasCurrentState;
+        mCurrentSlicedBucket[MetricDimensionKey(dimensionInWhatKey,
+                                                dimensionInWhatInfo.currentState)]
+                .conditionTimer.onConditionChanged(newCondition && inPulledData, eventTimeNs);
+    }
 }
 
 void ValueMetricProducer::prepareFirstBucketLocked() {
@@ -617,15 +691,21 @@
     // 2. A superset of the current mStateChangePrimaryKey
     // was not found in the new pulled data (i.e. not in mMatchedDimensionInWhatKeys)
     // then we need to reset the base.
-    for (auto& slice : mCurrentSlicedBucket) {
-        const auto& whatKey = slice.first.getDimensionKeyInWhat();
+    for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) {
+        const auto& whatKey = metricDimensionKey.getDimensionKeyInWhat();
         bool presentInPulledData =
                 mMatchedMetricDimensionKeys.find(whatKey) != mMatchedMetricDimensionKeys.end();
         if (!presentInPulledData && whatKey.contains(mStateChangePrimaryKey.second)) {
             auto it = mCurrentBaseInfo.find(whatKey);
-            for (auto& baseInfo : it->second) {
+            for (auto& baseInfo : it->second.baseInfos) {
                 baseInfo.hasBase = false;
             }
+            // Set to false when DimensionInWhat key is not present in a pull.
+            // Used in onMatchedLogEventInternalLocked() to ensure the condition
+            // timer is turned on the next pull when data is present.
+            it->second.hasCurrentState = false;
+            // Turn OFF condition timer for keys not present in pulled data.
+            currentValueBucket.conditionTimer.onConditionChanged(false, eventElapsedTimeNs);
         }
     }
     mMatchedMetricDimensionKeys.clear();
@@ -652,7 +732,7 @@
             (unsigned long)mCurrentSlicedBucket.size());
     if (verbose) {
         for (const auto& it : mCurrentSlicedBucket) {
-          for (const auto& interval : it.second) {
+          for (const auto& interval : it.second.intervals) {
               fprintf(out, "\t(what)%s\t(states)%s  (value)%s\n",
                       it.first.getDimensionKeyInWhat().toString().c_str(),
                       it.first.getStateValuesKey().toString().c_str(),
@@ -733,6 +813,11 @@
     return false;
 }
 
+bool ValueMetricProducer::multipleBucketsSkipped(const int64_t numBucketsForward) {
+    // Skip buckets if this is a pulled metric or a pushed metric that is diffed.
+    return numBucketsForward > 1 && (mIsPulled || mUseDiff);
+}
+
 void ValueMetricProducer::onMatchedLogEventInternalLocked(
         const size_t matcherIndex, const MetricDimensionKey& eventKey,
         const ConditionKey& conditionKey, bool condition, const LogEvent& event,
@@ -783,23 +868,28 @@
         return;
     }
 
-    vector<BaseInfo>& baseInfos = mCurrentBaseInfo[whatKey];
+    const auto& returnVal =
+            mCurrentBaseInfo.emplace(whatKey, DimensionsInWhatInfo(getUnknownStateKey()));
+    DimensionsInWhatInfo& dimensionsInWhatInfo = returnVal.first->second;
+    const HashableDimensionKey oldStateKey = dimensionsInWhatInfo.currentState;
+    vector<BaseInfo>& baseInfos = dimensionsInWhatInfo.baseInfos;
     if (baseInfos.size() < mFieldMatchers.size()) {
         VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
         baseInfos.resize(mFieldMatchers.size());
     }
 
-    for (BaseInfo& baseInfo : baseInfos) {
-        if (!baseInfo.hasCurrentState) {
-            baseInfo.currentState = getUnknownStateKey();
-            baseInfo.hasCurrentState = true;
-        }
+    // Ensure we turn on the condition timer in the case where dimensions
+    // were missing on a previous pull due to a state change.
+    bool stateChange = oldStateKey != stateKey;
+    if (!dimensionsInWhatInfo.hasCurrentState) {
+        stateChange = true;
+        dimensionsInWhatInfo.hasCurrentState = true;
     }
 
     // We need to get the intervals stored with the previous state key so we can
     // close these value intervals.
-    const auto oldStateKey = baseInfos[0].currentState;
-    vector<Interval>& intervals = mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)];
+    vector<Interval>& intervals =
+            mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)].intervals;
     if (intervals.size() < mFieldMatchers.size()) {
         VLOG("Resizing number of intervals to %d", (int)mFieldMatchers.size());
         intervals.resize(mFieldMatchers.size());
@@ -813,14 +903,14 @@
     // Discussion here: http://ag/6124370.
     bool useAnomalyDetection = true;
 
+    dimensionsInWhatInfo.hasCurrentState = true;
+    dimensionsInWhatInfo.currentState = stateKey;
     for (int i = 0; i < (int)mFieldMatchers.size(); i++) {
         const Matcher& matcher = mFieldMatchers[i];
         BaseInfo& baseInfo = baseInfos[i];
         Interval& interval = intervals[i];
         interval.valueIndex = i;
         Value value;
-        baseInfo.hasCurrentState = true;
-        baseInfo.currentState = stateKey;
         if (!getDoubleOrLong(event, matcher, value)) {
             VLOG("Failed to get value %d from event %s", i, event.ToString().c_str());
             StatsdStats::getInstance().noteBadValueType(mMetricId);
@@ -910,8 +1000,20 @@
         interval.sampleSize += 1;
     }
 
-    // Only trigger the tracker if all intervals are correct
-    if (useAnomalyDetection) {
+    // State change.
+    if (!mSlicedStateAtoms.empty() && stateChange) {
+        // Turn OFF the condition timer for the previous state key.
+        mCurrentSlicedBucket[MetricDimensionKey(whatKey, oldStateKey)]
+                .conditionTimer.onConditionChanged(false, eventTimeNs);
+
+        // Turn ON the condition timer for the new state key.
+        mCurrentSlicedBucket[MetricDimensionKey(whatKey, stateKey)]
+                .conditionTimer.onConditionChanged(true, eventTimeNs);
+    }
+
+    // Only trigger the tracker if all intervals are correct and we have not skipped the bucket due
+    // to MULTIPLE_BUCKETS_SKIPPED.
+    if (useAnomalyDetection && !multipleBucketsSkipped(calcBucketsForwardCount(eventTimeNs))) {
         // TODO: propgate proper values down stream when anomaly support doubles
         long wholeBucketVal = intervals[0].value.long_value;
         auto prev = mCurrentFullBucket.find(eventKey);
@@ -961,9 +1063,7 @@
     int64_t bucketEndTime = fullBucketEndTimeNs;
     int64_t numBucketsForward = calcBucketsForwardCount(eventTimeNs);
 
-    // Skip buckets if this is a pulled metric or a pushed metric that is diffed.
-    if (numBucketsForward > 1 && (mIsPulled || mUseDiff)) {
-
+    if (multipleBucketsSkipped(numBucketsForward)) {
         VLOG("Skipping forward %lld buckets", (long long)numBucketsForward);
         StatsdStats::getInstance().noteSkippedForwardBuckets(mMetricId);
         // Something went wrong. Maybe the device was sleeping for a long time. It is better
@@ -985,12 +1085,18 @@
     if (!mCurrentBucketIsSkipped) {
         bool bucketHasData = false;
         // The current bucket is large enough to keep.
-        for (const auto& slice : mCurrentSlicedBucket) {
-            ValueBucket bucket = buildPartialBucket(bucketEndTime, slice.second);
-            bucket.mConditionTrueNs = conditionTrueDuration;
+        for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) {
+            PastValueBucket bucket =
+                    buildPartialBucket(bucketEndTime, currentValueBucket.intervals);
+            if (!mSlicedStateAtoms.empty()) {
+                bucket.mConditionTrueNs =
+                        currentValueBucket.conditionTimer.newBucketStart(bucketEndTime);
+            } else {
+                bucket.mConditionTrueNs = conditionTrueDuration;
+            }
             // it will auto create new vector of ValuebucketInfo if the key is not found.
             if (bucket.valueIndex.size() > 0) {
-                auto& bucketList = mPastBuckets[slice.first];
+                auto& bucketList = mPastBuckets[metricDimensionKey];
                 bucketList.push_back(bucket);
                 bucketHasData = true;
             }
@@ -1018,17 +1124,24 @@
                 buildDropEvent(eventTimeNs, BucketDropReason::NO_DATA));
         mSkippedBuckets.emplace_back(bucketInGap);
     }
-
     appendToFullBucket(eventTimeNs > fullBucketEndTimeNs);
     initCurrentSlicedBucket(nextBucketStartTimeNs);
     // Update the condition timer again, in case we skipped buckets.
     mConditionTimer.newBucketStart(nextBucketStartTimeNs);
+
+    // NOTE: Update the condition timers in `mCurrentSlicedBucket` only when slicing
+    // by state. Otherwise, the "global" condition timer will be used.
+    if (!mSlicedStateAtoms.empty()) {
+        for (auto& [metricDimensionKey, currentValueBucket] : mCurrentSlicedBucket) {
+            currentValueBucket.conditionTimer.newBucketStart(nextBucketStartTimeNs);
+        }
+    }
     mCurrentBucketNum += numBucketsForward;
 }
 
-ValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime,
-                                                    const std::vector<Interval>& intervals) {
-    ValueBucket bucket;
+PastValueBucket ValueMetricProducer::buildPartialBucket(int64_t bucketEndTime,
+                                                        const std::vector<Interval>& intervals) {
+    PastValueBucket bucket;
     bucket.mBucketStartNs = mCurrentBucketStartTimeNs;
     bucket.mBucketEndNs = bucketEndTime;
     for (const auto& interval : intervals) {
@@ -1055,7 +1168,7 @@
     // Cleanup data structure to aggregate values.
     for (auto it = mCurrentSlicedBucket.begin(); it != mCurrentSlicedBucket.end();) {
         bool obsolete = true;
-        for (auto& interval : it->second) {
+        for (auto& interval : it->second.intervals) {
             interval.hasValue = false;
             interval.sampleSize = 0;
             if (interval.seenNewData) {
@@ -1064,6 +1177,17 @@
             interval.seenNewData = false;
         }
 
+        if (obsolete && !mSlicedStateAtoms.empty()) {
+            // When slicing by state, only delete the MetricDimensionKey when the
+            // state key in the MetricDimensionKey is not the current state key.
+            const HashableDimensionKey& dimensionInWhatKey = it->first.getDimensionKeyInWhat();
+            const auto& currentBaseInfoItr = mCurrentBaseInfo.find(dimensionInWhatKey);
+
+            if ((currentBaseInfoItr != mCurrentBaseInfo.end()) &&
+                (it->first.getStateValuesKey() == currentBaseInfoItr->second.currentState)) {
+                obsolete = false;
+            }
+        }
         if (obsolete) {
             it = mCurrentSlicedBucket.erase(it);
         } else {
@@ -1099,11 +1223,11 @@
         // Accumulate partial buckets with current value and then send to anomaly tracker.
         if (mCurrentFullBucket.size() > 0) {
             for (const auto& slice : mCurrentSlicedBucket) {
-                if (hitFullBucketGuardRailLocked(slice.first)) {
+                if (hitFullBucketGuardRailLocked(slice.first) || slice.second.intervals.empty()) {
                     continue;
                 }
                 // TODO: fix this when anomaly can accept double values
-                auto& interval = slice.second[0];
+                auto& interval = slice.second.intervals[0];
                 if (interval.hasValue) {
                     mCurrentFullBucket[slice.first] += interval.value.long_value;
                 }
@@ -1120,9 +1244,9 @@
             // Skip aggregating the partial buckets since there's no previous partial bucket.
             for (const auto& slice : mCurrentSlicedBucket) {
                 for (auto& tracker : mAnomalyTrackers) {
-                    if (tracker != nullptr) {
+                    if (tracker != nullptr && !slice.second.intervals.empty()) {
                         // TODO: fix this when anomaly can accept double values
-                        auto& interval = slice.second[0];
+                        auto& interval = slice.second.intervals[0];
                         if (interval.hasValue) {
                             tracker->addPastBucket(slice.first, interval.value.long_value,
                                                    mCurrentBucketNum);
@@ -1134,10 +1258,12 @@
     } else {
         // Accumulate partial bucket.
         for (const auto& slice : mCurrentSlicedBucket) {
-            // TODO: fix this when anomaly can accept double values
-            auto& interval = slice.second[0];
-            if (interval.hasValue) {
-                mCurrentFullBucket[slice.first] += interval.value.long_value;
+            if (!slice.second.intervals.empty()) {
+                // TODO: fix this when anomaly can accept double values
+                auto& interval = slice.second.intervals[0];
+                if (interval.hasValue) {
+                    mCurrentFullBucket[slice.first] += interval.value.long_value;
+                }
             }
         }
     }
diff --git a/bin/src/metrics/ValueMetricProducer.h b/bin/src/metrics/ValueMetricProducer.h
index a1eb702..1de0524 100644
--- a/bin/src/metrics/ValueMetricProducer.h
+++ b/bin/src/metrics/ValueMetricProducer.h
@@ -31,7 +31,7 @@
 namespace os {
 namespace statsd {
 
-struct ValueBucket {
+struct PastValueBucket {
     int64_t mBucketStartNs;
     int64_t mBucketEndNs;
     std::vector<int> valueIndex;
@@ -41,21 +41,20 @@
     int64_t mConditionTrueNs;
 };
 
-
 // Aggregates values within buckets.
 //
 // There are different events that might complete a bucket
 // - a condition change
 // - an app upgrade
 // - an alarm set to the end of the bucket
-class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
+class ValueMetricProducer : public MetricProducer, public virtual PullDataReceiver {
 public:
     ValueMetricProducer(
             const ConfigKey& key, const ValueMetric& valueMetric, const int conditionIndex,
             const vector<ConditionState>& initialConditionCache,
-            const sp<ConditionWizard>& conditionWizard, const int whatMatcherIndex,
-            const sp<EventMatcherWizard>& matcherWizard, const int pullTagId,
-            const int64_t timeBaseNs, const int64_t startTimeNs,
+            const sp<ConditionWizard>& conditionWizard, const uint64_t protoHash,
+            const int whatMatcherIndex, const sp<EventMatcherWizard>& matcherWizard,
+            const int pullTagId, const int64_t timeBaseNs, const int64_t startTimeNs,
             const sp<StatsPullerManager>& pullerManager,
             const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap = {},
             const std::unordered_map<int, std::vector<std::shared_ptr<Activation>>>&
@@ -93,6 +92,10 @@
     void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey,
                         const FieldValue& oldState, const FieldValue& newState) override;
 
+    MetricType getMetricType() const override {
+        return METRIC_TYPE_VALUE;
+    }
+
 protected:
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const MetricDimensionKey& eventKey,
@@ -152,7 +155,23 @@
     // causes the bucket to be invalidated will not notify StatsdStats.
     void skipCurrentBucket(const int64_t dropTimeNs, const BucketDropReason reason);
 
-    const int mWhatMatcherIndex;
+    bool onConfigUpdatedLocked(
+            const StatsdConfig& config, const int configIndex, const int metricIndex,
+            const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+            const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+            const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+            const sp<EventMatcherWizard>& matcherWizard,
+            const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+            const std::unordered_map<int64_t, int>& conditionTrackerMap,
+            const sp<ConditionWizard>& wizard,
+            const std::unordered_map<int64_t, int>& metricToActivationMap,
+            std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+            std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+            std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+            std::vector<int>& metricsWithActivation) override;
+
+    int mWhatMatcherIndex;
 
     sp<EventMatcherWizard> mEventMatcherWizard;
 
@@ -173,7 +192,7 @@
     // if this is pulled metric
     const bool mIsPulled;
 
-    // internal state of an ongoing aggregation bucket.
+    // Tracks the value information of one value field.
     typedef struct {
         // Index in multi value aggregation.
         int valueIndex;
@@ -188,25 +207,49 @@
         bool seenNewData = false;
     } Interval;
 
+    // Internal state of an ongoing aggregation bucket.
+    typedef struct CurrentValueBucket {
+        // If the `MetricDimensionKey` state key is the current state key, then
+        // the condition timer will be updated later (e.g. condition/state/active
+        // state change) with the correct condition and time.
+        CurrentValueBucket() : intervals(), conditionTimer(ConditionTimer(false, 0)) {}
+        // Value information for each value field of the metric.
+        std::vector<Interval> intervals;
+        // Tracks how long the condition is true.
+        ConditionTimer conditionTimer;
+    } CurrentValueBucket;
+
+    // Holds base information for diffing values from one value field.
     typedef struct {
         // Holds current base value of the dimension. Take diff and update if necessary.
         Value base;
         // Whether there is a base to diff to.
         bool hasBase;
+    } BaseInfo;
+
+    // State key and base information for a specific DimensionsInWhat key.
+    typedef struct DimensionsInWhatInfo {
+        DimensionsInWhatInfo(const HashableDimensionKey& stateKey)
+            : baseInfos(), currentState(stateKey), hasCurrentState(false) {
+        }
+        std::vector<BaseInfo> baseInfos;
         // Last seen state value(s).
         HashableDimensionKey currentState;
         // Whether this dimensions in what key has a current state key.
         bool hasCurrentState;
-    } BaseInfo;
+    } DimensionsInWhatInfo;
 
-    std::unordered_map<MetricDimensionKey, std::vector<Interval>> mCurrentSlicedBucket;
+    // Tracks the internal state in the ongoing aggregation bucket for each DimensionsInWhat
+    // key and StateValuesKey pair.
+    std::unordered_map<MetricDimensionKey, CurrentValueBucket> mCurrentSlicedBucket;
 
-    std::unordered_map<HashableDimensionKey, std::vector<BaseInfo>> mCurrentBaseInfo;
+    // Tracks current state key and base information for each DimensionsInWhat key.
+    std::unordered_map<HashableDimensionKey, DimensionsInWhatInfo> mCurrentBaseInfo;
 
     std::unordered_map<MetricDimensionKey, int64_t> mCurrentFullBucket;
 
     // Save the past buckets and we can clear when the StatsLogReport is dumped.
-    std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>> mPastBuckets;
+    std::unordered_map<MetricDimensionKey, std::vector<PastValueBucket>> mPastBuckets;
 
     const int64_t mMinBucketSizeNs;
 
@@ -219,11 +262,13 @@
 
     void pullAndMatchEventsLocked(const int64_t timestampNs);
 
+    bool multipleBucketsSkipped(const int64_t numBucketsForward);
+
     void accumulateEvents(const std::vector<std::shared_ptr<LogEvent>>& allData,
                           int64_t originalPullTimeNs, int64_t eventElapsedTimeNs);
 
-    ValueBucket buildPartialBucket(int64_t bucketEndTime,
-                                   const std::vector<Interval>& intervals);
+    PastValueBucket buildPartialBucket(int64_t bucketEndTime,
+                                       const std::vector<Interval>& intervals);
 
     void initCurrentSlicedBucket(int64_t nextBucketStartTimeNs);
 
@@ -232,7 +277,11 @@
     // Reset diff base and mHasGlobalBase
     void resetBase();
 
-    static const size_t kBucketSize = sizeof(ValueBucket{});
+    // Updates the condition timers in the current sliced bucket when there is a
+    // condition change or an active state change.
+    void updateCurrentSlicedBucketConditionTimers(bool newCondition, int64_t eventTimeNs);
+
+    static const size_t kBucketSize = sizeof(PastValueBucket{});
 
     const size_t mDimensionSoftLimit;
 
@@ -317,6 +366,11 @@
     FRIEND_TEST(ValueMetricProducerTest, TestTrimUnusedDimensionKey);
     FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBase);
     FRIEND_TEST(ValueMetricProducerTest, TestUseZeroDefaultBaseWithPullFailures);
+    FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMultipleDimensions);
+    FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange);
+    FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionChange);
+    FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataThenFlushBucket);
+    FRIEND_TEST(ValueMetricProducerTest, TestSlicedStateWithNoPullOnBucketBoundary);
 
     FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenOneConditionFailed);
     FRIEND_TEST(ValueMetricProducerTest_BucketDrop, TestInvalidBucketWhenInitialPullFailed);
@@ -332,6 +386,8 @@
     FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPulledValue);
     FRIEND_TEST(ValueMetricProducerTest_PartialBucket, TestPulledValueWhileConditionFalse);
 
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateValueMetrics);
+
     friend class ValueMetricProducerTestHelper;
 };
 
diff --git a/bin/src/metrics/duration_helper/DurationTracker.h b/bin/src/metrics/duration_helper/DurationTracker.h
index 8d59d13..cf1f437 100644
--- a/bin/src/metrics/duration_helper/DurationTracker.h
+++ b/bin/src/metrics/duration_helper/DurationTracker.h
@@ -71,7 +71,7 @@
                     sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
                     int64_t currentBucketStartNs, int64_t currentBucketNum, int64_t startTimeNs,
                     int64_t bucketSizeNs, bool conditionSliced, bool fullLink,
-                    const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
+                    const std::vector<sp<AnomalyTracker>>& anomalyTrackers)
         : mConfigKey(key),
           mTrackerId(id),
           mEventKey(eventKey),
@@ -89,6 +89,13 @@
 
     virtual ~DurationTracker(){};
 
+    void onConfigUpdated(const sp<ConditionWizard>& wizard, const int conditionTrackerIndex) {
+        sp<ConditionWizard> tmpWizard = mWizard;
+        mWizard = wizard;
+        mConditionTrackerIndex = conditionTrackerIndex;
+        mAnomalyTrackers.clear();
+    };
+
     virtual void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
                            const ConditionKey& conditionKey) = 0;
     virtual void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
@@ -114,7 +121,7 @@
             std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0;
 
     // Predict the anomaly timestamp given the current status.
-    virtual int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
+    virtual int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
                                               const int64_t currentTimestamp) const = 0;
     // Dump internal states for debugging
     virtual void dumpStates(FILE* out, bool verbose) const = 0;
@@ -126,6 +133,10 @@
     // Replace old value with new value for the given state atom.
     virtual void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) = 0;
 
+    void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker) {
+        mAnomalyTrackers.push_back(anomalyTracker);
+    }
+
 protected:
     int64_t getCurrentBucketEndTimeNs() const {
         return mStartTimeNs + (mCurrentBucketNum + 1) * mBucketSizeNs;
@@ -191,7 +202,7 @@
 
     sp<ConditionWizard> mWizard;
 
-    const int mConditionTrackerIndex;
+    int mConditionTrackerIndex;
 
     const int64_t mBucketSizeNs;
 
@@ -212,11 +223,14 @@
 
     bool mHasLinksToAllConditionDimensionsInTracker;
 
-    std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
+    std::vector<sp<AnomalyTracker>> mAnomalyTrackers;
 
     FRIEND_TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp);
     FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm);
     FRIEND_TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm);
+
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateDurationMetrics);
+    FRIEND_TEST(ConfigUpdateTest, TestUpdateAlerts);
 };
 
 }  // namespace statsd
diff --git a/bin/src/metrics/duration_helper/MaxDurationTracker.cpp b/bin/src/metrics/duration_helper/MaxDurationTracker.cpp
index ee4e167..62f4982 100644
--- a/bin/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/bin/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -30,7 +30,7 @@
                                        int64_t currentBucketStartNs, int64_t currentBucketNum,
                                        int64_t startTimeNs, int64_t bucketSizeNs,
                                        bool conditionSliced, bool fullLink,
-                                       const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
+                                       const vector<sp<AnomalyTracker>>& anomalyTrackers)
     : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
                       currentBucketNum, startTimeNs, bucketSizeNs, conditionSliced, fullLink,
                       anomalyTrackers) {
@@ -288,7 +288,7 @@
     // Note that we don't update mDuration here since it's only updated during noteStop.
 }
 
-int64_t MaxDurationTracker::predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
+int64_t MaxDurationTracker::predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
                                                       const int64_t currentTimestamp) const {
     // The allowed time we can continue in the current state is the
     // (anomaly threshold) - max(elapsed time of the started mInfos).
diff --git a/bin/src/metrics/duration_helper/MaxDurationTracker.h b/bin/src/metrics/duration_helper/MaxDurationTracker.h
index 2891c6e..be2707c 100644
--- a/bin/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/bin/src/metrics/duration_helper/MaxDurationTracker.h
@@ -29,12 +29,10 @@
 class MaxDurationTracker : public DurationTracker {
 public:
     MaxDurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
-                       sp<ConditionWizard> wizard, int conditionIndex,
-                       bool nesting,
-                       int64_t currentBucketStartNs, int64_t currentBucketNum,
-                       int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced,
-                       bool fullLink,
-                       const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
+                       sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
+                       int64_t currentBucketStartNs, int64_t currentBucketNum, int64_t startTimeNs,
+                       int64_t bucketSizeNs, bool conditionSliced, bool fullLink,
+                       const std::vector<sp<AnomalyTracker>>& anomalyTrackers);
 
     MaxDurationTracker(const MaxDurationTracker& tracker) = default;
 
@@ -57,7 +55,7 @@
     void onStateChanged(const int64_t timestamp, const int32_t atomId,
                         const FieldValue& newState) override;
 
-    int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
+    int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
                                       const int64_t currentTimestamp) const override;
     void dumpStates(FILE* out, bool verbose) const override;
 
diff --git a/bin/src/metrics/duration_helper/OringDurationTracker.cpp b/bin/src/metrics/duration_helper/OringDurationTracker.cpp
index 0d49bbc..247e2e0 100644
--- a/bin/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/bin/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -28,7 +28,7 @@
         const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
         sp<ConditionWizard> wizard, int conditionIndex, bool nesting, int64_t currentBucketStartNs,
         int64_t currentBucketNum, int64_t startTimeNs, int64_t bucketSizeNs, bool conditionSliced,
-        bool fullLink, const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
+        bool fullLink, const vector<sp<AnomalyTracker>>& anomalyTrackers)
     : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
                       currentBucketNum, startTimeNs, bucketSizeNs, conditionSliced, fullLink,
                       anomalyTrackers),
@@ -344,9 +344,8 @@
     updateCurrentStateKey(atomId, newState);
 }
 
-int64_t OringDurationTracker::predictAnomalyTimestampNs(
-        const DurationAnomalyTracker& anomalyTracker, const int64_t eventTimestampNs) const {
-
+int64_t OringDurationTracker::predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
+                                                        const int64_t eventTimestampNs) const {
     // The anomaly threshold.
     const int64_t thresholdNs = anomalyTracker.getAnomalyThreshold();
 
diff --git a/bin/src/metrics/duration_helper/OringDurationTracker.h b/bin/src/metrics/duration_helper/OringDurationTracker.h
index bd8017a..6eddee7 100644
--- a/bin/src/metrics/duration_helper/OringDurationTracker.h
+++ b/bin/src/metrics/duration_helper/OringDurationTracker.h
@@ -31,7 +31,7 @@
                          int conditionIndex, bool nesting, int64_t currentBucketStartNs,
                          int64_t currentBucketNum, int64_t startTimeNs, int64_t bucketSizeNs,
                          bool conditionSliced, bool fullLink,
-                         const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
+                         const std::vector<sp<AnomalyTracker>>& anomalyTrackers);
 
     OringDurationTracker(const OringDurationTracker& tracker) = default;
 
@@ -54,7 +54,7 @@
             int64_t timestampNs,
             std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
 
-    int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
+    int64_t predictAnomalyTimestampNs(const AnomalyTracker& anomalyTracker,
                                       const int64_t currentTimestamp) const override;
     void dumpStates(FILE* out, bool verbose) const override;
 
diff --git a/bin/src/metrics/metrics_manager_util.cpp b/bin/src/metrics/metrics_manager_util.cpp
deleted file mode 100644
index 8917c36..0000000
--- a/bin/src/metrics/metrics_manager_util.cpp
+++ /dev/null
@@ -1,983 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define DEBUG false  // STOPSHIP if true
-#include "Log.h"
-
-#include "metrics_manager_util.h"
-
-#include <inttypes.h>
-
-#include "FieldValue.h"
-#include "MetricProducer.h"
-#include "condition/CombinationConditionTracker.h"
-#include "condition/SimpleConditionTracker.h"
-#include "external/StatsPullerManager.h"
-#include "matchers/CombinationLogMatchingTracker.h"
-#include "matchers/EventMatcherWizard.h"
-#include "matchers/SimpleLogMatchingTracker.h"
-#include "metrics/CountMetricProducer.h"
-#include "metrics/DurationMetricProducer.h"
-#include "metrics/EventMetricProducer.h"
-#include "metrics/GaugeMetricProducer.h"
-#include "metrics/ValueMetricProducer.h"
-#include "state/StateManager.h"
-#include "stats_util.h"
-
-using std::set;
-using std::unordered_map;
-using std::vector;
-
-namespace android {
-namespace os {
-namespace statsd {
-
-namespace {
-
-bool hasLeafNode(const FieldMatcher& matcher) {
-    if (!matcher.has_field()) {
-        return false;
-    }
-    for (int i = 0; i < matcher.child_size(); ++i) {
-        if (hasLeafNode(matcher.child(i))) {
-            return true;
-        }
-    }
-    return true;
-}
-
-}  // namespace
-
-bool handleMetricWithLogTrackers(const int64_t what, const int metricIndex,
-                                 const bool usedForDimension,
-                                 const vector<sp<LogMatchingTracker>>& allAtomMatchers,
-                                 const unordered_map<int64_t, int>& logTrackerMap,
-                                 unordered_map<int, std::vector<int>>& trackerToMetricMap,
-                                 int& logTrackerIndex) {
-    auto logTrackerIt = logTrackerMap.find(what);
-    if (logTrackerIt == logTrackerMap.end()) {
-        ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)what);
-        return false;
-    }
-    if (usedForDimension && allAtomMatchers[logTrackerIt->second]->getAtomIds().size() > 1) {
-        ALOGE("AtomMatcher \"%lld\" has more than one tag ids. When a metric has dimension, "
-              "the \"what\" can only about one atom type.",
-              (long long)what);
-        return false;
-    }
-    logTrackerIndex = logTrackerIt->second;
-    auto& metric_list = trackerToMetricMap[logTrackerIndex];
-    metric_list.push_back(metricIndex);
-    return true;
-}
-
-bool handlePullMetricTriggerWithLogTrackers(
-        const int64_t trigger, const int metricIndex,
-        const vector<sp<LogMatchingTracker>>& allAtomMatchers,
-        const unordered_map<int64_t, int>& logTrackerMap,
-        unordered_map<int, std::vector<int>>& trackerToMetricMap, int& logTrackerIndex) {
-    auto logTrackerIt = logTrackerMap.find(trigger);
-    if (logTrackerIt == logTrackerMap.end()) {
-        ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)trigger);
-        return false;
-    }
-    if (allAtomMatchers[logTrackerIt->second]->getAtomIds().size() > 1) {
-        ALOGE("AtomMatcher \"%lld\" has more than one tag ids."
-              "Trigger can only be one atom type.",
-              (long long)trigger);
-        return false;
-    }
-    logTrackerIndex = logTrackerIt->second;
-    auto& metric_list = trackerToMetricMap[logTrackerIndex];
-    metric_list.push_back(metricIndex);
-    return true;
-}
-
-bool handleMetricWithConditions(
-        const int64_t condition, const int metricIndex,
-        const unordered_map<int64_t, int>& conditionTrackerMap,
-        const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>&
-                links,
-        vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex,
-        unordered_map<int, std::vector<int>>& conditionToMetricMap) {
-    auto condition_it = conditionTrackerMap.find(condition);
-    if (condition_it == conditionTrackerMap.end()) {
-        ALOGW("cannot find Predicate \"%lld\" in the config", (long long)condition);
-        return false;
-    }
-
-    for (const auto& link : links) {
-        auto it = conditionTrackerMap.find(link.condition());
-        if (it == conditionTrackerMap.end()) {
-            ALOGW("cannot find Predicate \"%lld\" in the config", (long long)link.condition());
-            return false;
-        }
-        allConditionTrackers[condition_it->second]->setSliced(true);
-        allConditionTrackers[it->second]->setSliced(true);
-    }
-    conditionIndex = condition_it->second;
-
-    // will create new vector if not exist before.
-    auto& metricList = conditionToMetricMap[condition_it->second];
-    metricList.push_back(metricIndex);
-    return true;
-}
-
-// Initializes state data structures for a metric.
-// input:
-// [config]: the input config
-// [stateIds]: the slice_by_state ids for this metric
-// [stateAtomIdMap]: this map contains the mapping from all state ids to atom ids
-// [allStateGroupMaps]: this map contains the mapping from state ids and state
-//                      values to state group ids for all states
-// output:
-// [slicedStateAtoms]: a vector of atom ids of all the slice_by_states
-// [stateGroupMap]: this map should contain the mapping from states ids and state
-//                      values to state group ids for all states that this metric
-//                      is interested in
-bool handleMetricWithStates(
-        const StatsdConfig& config, const ::google::protobuf::RepeatedField<int64_t>& stateIds,
-        const unordered_map<int64_t, int>& stateAtomIdMap,
-        const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
-        vector<int>& slicedStateAtoms,
-        unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) {
-    for (const auto& stateId : stateIds) {
-        auto it = stateAtomIdMap.find(stateId);
-        if (it == stateAtomIdMap.end()) {
-            ALOGW("cannot find State %" PRId64 " in the config", stateId);
-            return false;
-        }
-        int atomId = it->second;
-        slicedStateAtoms.push_back(atomId);
-
-        auto stateIt = allStateGroupMaps.find(stateId);
-        if (stateIt != allStateGroupMaps.end()) {
-            stateGroupMap[atomId] = stateIt->second;
-        }
-    }
-    return true;
-}
-
-bool handleMetricWithStateLink(const FieldMatcher& stateMatcher,
-                               const vector<Matcher>& dimensionsInWhat) {
-    vector<Matcher> stateMatchers;
-    translateFieldMatcher(stateMatcher, &stateMatchers);
-
-    return subsetDimensions(stateMatchers, dimensionsInWhat);
-}
-
-// Validates a metricActivation and populates state.
-// EventActivationMap and EventDeactivationMap are supplied to a MetricProducer
-//      to provide the producer with state about its activators and deactivators.
-// Returns false if there are errors.
-bool handleMetricActivation(
-        const StatsdConfig& config,
-        const int64_t metricId,
-        const int metricIndex,
-        const unordered_map<int64_t, int>& metricToActivationMap,
-        const unordered_map<int64_t, int>& logTrackerMap,
-        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
-        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
-        vector<int>& metricsWithActivation,
-        unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
-        unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) {
-    // Check if metric has an associated activation
-    auto itr = metricToActivationMap.find(metricId);
-    if (itr == metricToActivationMap.end()) return true;
-
-    int activationIndex = itr->second;
-    const MetricActivation& metricActivation = config.metric_activation(activationIndex);
-
-    for (int i = 0; i < metricActivation.event_activation_size(); i++) {
-        const EventActivation& activation = metricActivation.event_activation(i);
-
-        auto itr = logTrackerMap.find(activation.atom_matcher_id());
-        if (itr == logTrackerMap.end()) {
-            ALOGE("Atom matcher not found for event activation.");
-            return false;
-        }
-
-        ActivationType activationType = (activation.has_activation_type()) ?
-                activation.activation_type() : metricActivation.activation_type();
-        std::shared_ptr<Activation> activationWrapper = std::make_shared<Activation>(
-                activationType, activation.ttl_seconds() * NS_PER_SEC);
-
-        int atomMatcherIndex = itr->second;
-        activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(metricIndex);
-        eventActivationMap.emplace(atomMatcherIndex, activationWrapper);
-
-        if (activation.has_deactivation_atom_matcher_id()) {
-            itr = logTrackerMap.find(activation.deactivation_atom_matcher_id());
-            if (itr == logTrackerMap.end()) {
-                ALOGE("Atom matcher not found for event deactivation.");
-                return false;
-            }
-            int deactivationAtomMatcherIndex = itr->second;
-            deactivationAtomTrackerToMetricMap[deactivationAtomMatcherIndex].push_back(metricIndex);
-            eventDeactivationMap[deactivationAtomMatcherIndex].push_back(activationWrapper);
-        }
-    }
-
-    metricsWithActivation.push_back(metricIndex);
-    return true;
-}
-
-bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap,
-                     unordered_map<int64_t, int>& logTrackerMap,
-                     vector<sp<LogMatchingTracker>>& allAtomMatchers, set<int>& allTagIds) {
-    vector<AtomMatcher> matcherConfigs;
-    const int atomMatcherCount = config.atom_matcher_size();
-    matcherConfigs.reserve(atomMatcherCount);
-    allAtomMatchers.reserve(atomMatcherCount);
-
-    for (int i = 0; i < atomMatcherCount; i++) {
-        const AtomMatcher& logMatcher = config.atom_matcher(i);
-
-        int index = allAtomMatchers.size();
-        switch (logMatcher.contents_case()) {
-            case AtomMatcher::ContentsCase::kSimpleAtomMatcher:
-                allAtomMatchers.push_back(new SimpleLogMatchingTracker(
-                        logMatcher.id(), index, logMatcher.simple_atom_matcher(), uidMap));
-                break;
-            case AtomMatcher::ContentsCase::kCombination:
-                allAtomMatchers.push_back(
-                        new CombinationLogMatchingTracker(logMatcher.id(), index));
-                break;
-            default:
-                ALOGE("Matcher \"%lld\" malformed", (long long)logMatcher.id());
-                return false;
-                // continue;
-        }
-        if (logTrackerMap.find(logMatcher.id()) != logTrackerMap.end()) {
-            ALOGE("Duplicate AtomMatcher found!");
-            return false;
-        }
-        logTrackerMap[logMatcher.id()] = index;
-        matcherConfigs.push_back(logMatcher);
-    }
-
-    vector<bool> stackTracker2(allAtomMatchers.size(), false);
-    for (auto& matcher : allAtomMatchers) {
-        if (!matcher->init(matcherConfigs, allAtomMatchers, logTrackerMap, stackTracker2)) {
-            return false;
-        }
-        // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only.
-        const set<int>& tagIds = matcher->getAtomIds();
-        allTagIds.insert(tagIds.begin(), tagIds.end());
-    }
-    return true;
-}
-
-bool initConditions(const ConfigKey& key, const StatsdConfig& config,
-                    const unordered_map<int64_t, int>& logTrackerMap,
-                    unordered_map<int64_t, int>& conditionTrackerMap,
-                    vector<sp<ConditionTracker>>& allConditionTrackers,
-                    unordered_map<int, std::vector<int>>& trackerToConditionMap,
-                    vector<ConditionState>& initialConditionCache) {
-    vector<Predicate> conditionConfigs;
-    const int conditionTrackerCount = config.predicate_size();
-    conditionConfigs.reserve(conditionTrackerCount);
-    allConditionTrackers.reserve(conditionTrackerCount);
-    initialConditionCache.reserve(conditionTrackerCount);
-    std::fill(initialConditionCache.begin(), initialConditionCache.end(), ConditionState::kUnknown);
-
-    for (int i = 0; i < conditionTrackerCount; i++) {
-        const Predicate& condition = config.predicate(i);
-        int index = allConditionTrackers.size();
-        switch (condition.contents_case()) {
-            case Predicate::ContentsCase::kSimplePredicate: {
-                allConditionTrackers.push_back(new SimpleConditionTracker(
-                        key, condition.id(), index, condition.simple_predicate(), logTrackerMap));
-                break;
-            }
-            case Predicate::ContentsCase::kCombination: {
-                allConditionTrackers.push_back(
-                        new CombinationConditionTracker(condition.id(), index));
-                break;
-            }
-            default:
-                ALOGE("Predicate \"%lld\" malformed", (long long)condition.id());
-                return false;
-        }
-        if (conditionTrackerMap.find(condition.id()) != conditionTrackerMap.end()) {
-            ALOGE("Duplicate Predicate found!");
-            return false;
-        }
-        conditionTrackerMap[condition.id()] = index;
-        conditionConfigs.push_back(condition);
-    }
-
-    vector<bool> stackTracker(allConditionTrackers.size(), false);
-    for (size_t i = 0; i < allConditionTrackers.size(); i++) {
-        auto& conditionTracker = allConditionTrackers[i];
-        if (!conditionTracker->init(conditionConfigs, allConditionTrackers, conditionTrackerMap,
-                                    stackTracker, initialConditionCache)) {
-            return false;
-        }
-        for (const int trackerIndex : conditionTracker->getLogTrackerIndex()) {
-            auto& conditionList = trackerToConditionMap[trackerIndex];
-            conditionList.push_back(i);
-        }
-    }
-    return true;
-}
-
-bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
-                unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps) {
-    for (int i = 0; i < config.state_size(); i++) {
-        const State& state = config.state(i);
-        const int64_t stateId = state.id();
-        stateAtomIdMap[stateId] = state.atom_id();
-
-        const StateMap& stateMap = state.map();
-        for (auto group : stateMap.group()) {
-            for (auto value : group.value()) {
-                allStateGroupMaps[stateId][value] = group.group_id();
-            }
-        }
-    }
-
-    return true;
-}
-
-bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
-                 const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
-                 const unordered_map<int64_t, int>& logTrackerMap,
-                 const unordered_map<int64_t, int>& conditionTrackerMap,
-                 const vector<sp<LogMatchingTracker>>& allAtomMatchers,
-                 const unordered_map<int64_t, int>& stateAtomIdMap,
-                 const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
-                 vector<sp<ConditionTracker>>& allConditionTrackers,
-                 const vector<ConditionState>& initialConditionCache,
-                 vector<sp<MetricProducer>>& allMetricProducers,
-                 unordered_map<int, vector<int>>& conditionToMetricMap,
-                 unordered_map<int, vector<int>>& trackerToMetricMap,
-                 unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds,
-                 unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
-                 unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
-                 vector<int>& metricsWithActivation) {
-    sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
-    sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchers);
-    const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
-                                config.event_metric_size() + config.gauge_metric_size() +
-                                config.value_metric_size();
-    allMetricProducers.reserve(allMetricsCount);
-
-    // Construct map from metric id to metric activation index. The map will be used to determine
-    // the metric activation corresponding to a metric.
-    unordered_map<int64_t, int> metricToActivationMap;
-    for (int i = 0; i < config.metric_activation_size(); i++) {
-        const MetricActivation& metricActivation = config.metric_activation(i);
-        int64_t metricId = metricActivation.metric_id();
-        if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) {
-            ALOGE("Metric %lld has multiple MetricActivations", (long long) metricId);
-            return false;
-        }
-        metricToActivationMap.insert({metricId, i});
-    }
-
-    // Build MetricProducers for each metric defined in config.
-    // build CountMetricProducer
-    for (int i = 0; i < config.count_metric_size(); i++) {
-        const CountMetric& metric = config.count_metric(i);
-        if (!metric.has_what()) {
-            ALOGW("cannot find \"what\" in CountMetric \"%lld\"", (long long)metric.id());
-            return false;
-        }
-
-        int metricIndex = allMetricProducers.size();
-        metricMap.insert({metric.id(), metricIndex});
-        int trackerIndex;
-        if (!handleMetricWithLogTrackers(metric.what(), metricIndex,
-                                         metric.has_dimensions_in_what(),
-                                         allAtomMatchers, logTrackerMap, trackerToMetricMap,
-                                         trackerIndex)) {
-            return false;
-        }
-
-        int conditionIndex = -1;
-        if (metric.has_condition()) {
-            if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
-                                            metric.links(), allConditionTrackers, conditionIndex,
-                                            conditionToMetricMap)) {
-                return false;
-            }
-        } else {
-            if (metric.links_size() > 0) {
-                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
-                return false;
-            }
-        }
-
-        std::vector<int> slicedStateAtoms;
-        unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
-        if (metric.slice_by_state_size() > 0) {
-            if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
-                                        allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
-                return false;
-            }
-        } else {
-            if (metric.state_link_size() > 0) {
-                ALOGW("CountMetric has a MetricStateLink but doesn't have a slice_by_state");
-                return false;
-            }
-        }
-
-        unordered_map<int, shared_ptr<Activation>> eventActivationMap;
-        unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
-        bool success = handleMetricActivation(config, metric.id(), metricIndex,
-                metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
-                deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
-                eventDeactivationMap);
-        if (!success) return false;
-
-        sp<MetricProducer> countProducer =
-                new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
-                                        timeBaseTimeNs, currentTimeNs, eventActivationMap,
-                                        eventDeactivationMap, slicedStateAtoms, stateGroupMap);
-        allMetricProducers.push_back(countProducer);
-    }
-
-    // build DurationMetricProducer
-    for (int i = 0; i < config.duration_metric_size(); i++) {
-        int metricIndex = allMetricProducers.size();
-        const DurationMetric& metric = config.duration_metric(i);
-        metricMap.insert({metric.id(), metricIndex});
-
-        auto what_it = conditionTrackerMap.find(metric.what());
-        if (what_it == conditionTrackerMap.end()) {
-            ALOGE("DurationMetric's \"what\" is invalid");
-            return false;
-        }
-
-        const Predicate& durationWhat = config.predicate(what_it->second);
-
-        if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) {
-            ALOGE("DurationMetric's \"what\" must be a simple condition");
-            return false;
-        }
-
-        const auto& simplePredicate = durationWhat.simple_predicate();
-
-        bool nesting = simplePredicate.count_nesting();
-
-        int trackerIndices[3] = {-1, -1, -1};
-        if (!simplePredicate.has_start() ||
-            !handleMetricWithLogTrackers(simplePredicate.start(), metricIndex,
-                                         metric.has_dimensions_in_what(), allAtomMatchers,
-                                         logTrackerMap, trackerToMetricMap, trackerIndices[0])) {
-            ALOGE("Duration metrics must specify a valid the start event matcher");
-            return false;
-        }
-
-        if (simplePredicate.has_stop() &&
-            !handleMetricWithLogTrackers(simplePredicate.stop(), metricIndex,
-                                         metric.has_dimensions_in_what(), allAtomMatchers,
-                                         logTrackerMap, trackerToMetricMap, trackerIndices[1])) {
-            return false;
-        }
-
-        if (simplePredicate.has_stop_all() &&
-            !handleMetricWithLogTrackers(simplePredicate.stop_all(), metricIndex,
-                                         metric.has_dimensions_in_what(), allAtomMatchers,
-                                         logTrackerMap, trackerToMetricMap, trackerIndices[2])) {
-            return false;
-        }
-
-        FieldMatcher internalDimensions = simplePredicate.dimensions();
-
-        int conditionIndex = -1;
-
-        if (metric.has_condition()) {
-            bool good = handleMetricWithConditions(
-                    metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
-                    allConditionTrackers, conditionIndex, conditionToMetricMap);
-            if (!good) {
-                return false;
-            }
-        } else {
-            if (metric.links_size() > 0) {
-                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
-                return false;
-            }
-        }
-
-        std::vector<int> slicedStateAtoms;
-        unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
-        if (metric.slice_by_state_size() > 0) {
-            if (metric.aggregation_type() == DurationMetric::MAX_SPARSE) {
-                ALOGE("DurationMetric with aggregation type MAX_SPARSE cannot be sliced by state");
-                return false;
-            }
-            if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
-                                        allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
-                return false;
-            }
-        } else {
-            if (metric.state_link_size() > 0) {
-                ALOGW("DurationMetric has a MetricStateLink but doesn't have a sliced state");
-                return false;
-            }
-        }
-
-        // Check that all metric state links are a subset of dimensions_in_what fields.
-        std::vector<Matcher> dimensionsInWhat;
-        translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
-        for (const auto& stateLink : metric.state_link()) {
-            if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
-                return false;
-            }
-        }
-
-        unordered_map<int, shared_ptr<Activation>> eventActivationMap;
-        unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
-        bool success = handleMetricActivation(config, metric.id(), metricIndex,
-                metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
-                deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
-                eventDeactivationMap);
-        if (!success) return false;
-
-        sp<MetricProducer> durationMetric = new DurationMetricProducer(
-                key, metric, conditionIndex, initialConditionCache, trackerIndices[0],
-                trackerIndices[1], trackerIndices[2], nesting, wizard, internalDimensions,
-                timeBaseTimeNs, currentTimeNs, eventActivationMap, eventDeactivationMap,
-                slicedStateAtoms, stateGroupMap);
-
-        allMetricProducers.push_back(durationMetric);
-    }
-
-    // build EventMetricProducer
-    for (int i = 0; i < config.event_metric_size(); i++) {
-        int metricIndex = allMetricProducers.size();
-        const EventMetric& metric = config.event_metric(i);
-        metricMap.insert({metric.id(), metricIndex});
-        if (!metric.has_id() || !metric.has_what()) {
-            ALOGW("cannot find the metric name or what in config");
-            return false;
-        }
-        int trackerIndex;
-        if (!handleMetricWithLogTrackers(metric.what(), metricIndex, false, allAtomMatchers,
-                                         logTrackerMap, trackerToMetricMap, trackerIndex)) {
-            return false;
-        }
-
-        int conditionIndex = -1;
-        if (metric.has_condition()) {
-            bool good = handleMetricWithConditions(
-                    metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
-                    allConditionTrackers, conditionIndex, conditionToMetricMap);
-            if (!good) {
-                return false;
-            }
-        } else {
-            if (metric.links_size() > 0) {
-                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
-                return false;
-            }
-        }
-
-        unordered_map<int, shared_ptr<Activation>> eventActivationMap;
-        unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
-        bool success = handleMetricActivation(config, metric.id(), metricIndex,
-                metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
-                deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
-                eventDeactivationMap);
-        if (!success) return false;
-
-        sp<MetricProducer> eventMetric =
-                new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
-                                        timeBaseTimeNs, eventActivationMap, eventDeactivationMap);
-
-        allMetricProducers.push_back(eventMetric);
-    }
-
-    // build ValueMetricProducer
-    for (int i = 0; i < config.value_metric_size(); i++) {
-        const ValueMetric& metric = config.value_metric(i);
-        if (!metric.has_what()) {
-            ALOGW("cannot find \"what\" in ValueMetric \"%lld\"", (long long)metric.id());
-            return false;
-        }
-        if (!metric.has_value_field()) {
-            ALOGW("cannot find \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
-            return false;
-        }
-        std::vector<Matcher> fieldMatchers;
-        translateFieldMatcher(metric.value_field(), &fieldMatchers);
-        if (fieldMatchers.size() < 1) {
-            ALOGW("incorrect \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
-            return false;
-        }
-
-        int metricIndex = allMetricProducers.size();
-        metricMap.insert({metric.id(), metricIndex});
-        int trackerIndex;
-        if (!handleMetricWithLogTrackers(metric.what(), metricIndex,
-                                         metric.has_dimensions_in_what(),
-                                         allAtomMatchers, logTrackerMap, trackerToMetricMap,
-                                         trackerIndex)) {
-            return false;
-        }
-
-        sp<LogMatchingTracker> atomMatcher = allAtomMatchers.at(trackerIndex);
-        // If it is pulled atom, it should be simple matcher with one tagId.
-        if (atomMatcher->getAtomIds().size() != 1) {
-            return false;
-        }
-        int atomTagId = *(atomMatcher->getAtomIds().begin());
-        int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
-
-        int conditionIndex = -1;
-        if (metric.has_condition()) {
-            bool good = handleMetricWithConditions(
-                    metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
-                    allConditionTrackers, conditionIndex, conditionToMetricMap);
-            if (!good) {
-                return false;
-            }
-        } else {
-            if (metric.links_size() > 0) {
-                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
-                return false;
-            }
-        }
-
-        std::vector<int> slicedStateAtoms;
-        unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
-        if (metric.slice_by_state_size() > 0) {
-            if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
-                                        allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
-                return false;
-            }
-        } else {
-            if (metric.state_link_size() > 0) {
-                ALOGW("ValueMetric has a MetricStateLink but doesn't have a sliced state");
-                return false;
-            }
-        }
-
-        // Check that all metric state links are a subset of dimensions_in_what fields.
-        std::vector<Matcher> dimensionsInWhat;
-        translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
-        for (const auto& stateLink : metric.state_link()) {
-            if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
-                return false;
-            }
-        }
-
-        unordered_map<int, shared_ptr<Activation>> eventActivationMap;
-        unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
-        bool success = handleMetricActivation(
-                config, metric.id(), metricIndex, metricToActivationMap, logTrackerMap,
-                activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                metricsWithActivation, eventActivationMap, eventDeactivationMap);
-        if (!success) return false;
-
-        sp<MetricProducer> valueProducer = new ValueMetricProducer(
-                key, metric, conditionIndex, initialConditionCache, wizard, trackerIndex,
-                matcherWizard, pullTagId, timeBaseTimeNs, currentTimeNs, pullerManager,
-                eventActivationMap, eventDeactivationMap, slicedStateAtoms, stateGroupMap);
-        allMetricProducers.push_back(valueProducer);
-    }
-
-    // Gauge metrics.
-    for (int i = 0; i < config.gauge_metric_size(); i++) {
-        const GaugeMetric& metric = config.gauge_metric(i);
-        if (!metric.has_what()) {
-            ALOGW("cannot find \"what\" in GaugeMetric \"%lld\"", (long long)metric.id());
-            return false;
-        }
-
-        if ((!metric.gauge_fields_filter().has_include_all() ||
-             (metric.gauge_fields_filter().include_all() == false)) &&
-            !hasLeafNode(metric.gauge_fields_filter().fields())) {
-            ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
-            return false;
-        }
-        if ((metric.gauge_fields_filter().has_include_all() &&
-             metric.gauge_fields_filter().include_all() == true) &&
-            hasLeafNode(metric.gauge_fields_filter().fields())) {
-            ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
-            return false;
-        }
-
-        int metricIndex = allMetricProducers.size();
-        metricMap.insert({metric.id(), metricIndex});
-        int trackerIndex;
-        if (!handleMetricWithLogTrackers(metric.what(), metricIndex,
-                                         metric.has_dimensions_in_what(),
-                                         allAtomMatchers, logTrackerMap, trackerToMetricMap,
-                                         trackerIndex)) {
-            return false;
-        }
-
-        sp<LogMatchingTracker> atomMatcher = allAtomMatchers.at(trackerIndex);
-        // For GaugeMetric atom, it should be simple matcher with one tagId.
-        if (atomMatcher->getAtomIds().size() != 1) {
-            return false;
-        }
-        int atomTagId = *(atomMatcher->getAtomIds().begin());
-        int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
-
-        int triggerTrackerIndex;
-        int triggerAtomId = -1;
-        if (metric.has_trigger_event()) {
-            if (pullTagId == -1) {
-                ALOGW("Pull atom not specified for trigger");
-                return false;
-            }
-            // event_trigger should be used with FIRST_N_SAMPLES
-            if (metric.sampling_type() != GaugeMetric::FIRST_N_SAMPLES) {
-                return false;
-            }
-            if (!handlePullMetricTriggerWithLogTrackers(metric.trigger_event(), metricIndex,
-                                                        allAtomMatchers, logTrackerMap,
-                                                        trackerToMetricMap, triggerTrackerIndex)) {
-                return false;
-            }
-            sp<LogMatchingTracker> triggerAtomMatcher = allAtomMatchers.at(triggerTrackerIndex);
-            triggerAtomId = *(triggerAtomMatcher->getAtomIds().begin());
-        }
-
-        if (!metric.has_trigger_event() && pullTagId != -1 &&
-            metric.sampling_type() == GaugeMetric::FIRST_N_SAMPLES) {
-            ALOGW("FIRST_N_SAMPLES is only for pushed event or pull_on_trigger");
-            return false;
-        }
-
-        int conditionIndex = -1;
-        if (metric.has_condition()) {
-            bool good = handleMetricWithConditions(
-                    metric.condition(), metricIndex, conditionTrackerMap, metric.links(),
-                    allConditionTrackers, conditionIndex, conditionToMetricMap);
-            if (!good) {
-                return false;
-            }
-        } else {
-            if (metric.links_size() > 0) {
-                ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
-                return false;
-            }
-        }
-
-        unordered_map<int, shared_ptr<Activation>> eventActivationMap;
-        unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
-        bool success = handleMetricActivation(config, metric.id(), metricIndex,
-                metricToActivationMap, logTrackerMap, activationAtomTrackerToMetricMap,
-                deactivationAtomTrackerToMetricMap, metricsWithActivation, eventActivationMap,
-                eventDeactivationMap);
-        if (!success) return false;
-
-        sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(
-                key, metric, conditionIndex, initialConditionCache, wizard, trackerIndex,
-                matcherWizard, pullTagId, triggerAtomId, atomTagId, timeBaseTimeNs, currentTimeNs,
-                pullerManager, eventActivationMap, eventDeactivationMap);
-        allMetricProducers.push_back(gaugeProducer);
-    }
-    for (int i = 0; i < config.no_report_metric_size(); ++i) {
-        const auto no_report_metric = config.no_report_metric(i);
-        if (metricMap.find(no_report_metric) == metricMap.end()) {
-            ALOGW("no_report_metric %" PRId64 " not exist", no_report_metric);
-            return false;
-        }
-        noReportMetricIds.insert(no_report_metric);
-    }
-
-    const set<int> whitelistedAtomIds(config.whitelisted_atom_ids().begin(),
-                                      config.whitelisted_atom_ids().end());
-    for (const auto& it : allMetricProducers) {
-        // Register metrics to StateTrackers
-        for (int atomId : it->getSlicedStateAtoms()) {
-            // Register listener for non-whitelisted atoms only. Using whitelisted atom as a sliced
-            // state atom is not allowed.
-            if (whitelistedAtomIds.find(atomId) == whitelistedAtomIds.end()) {
-                StateManager::getInstance().registerListener(atomId, it);
-            } else {
-                return false;
-            }
-        }
-    }
-    return true;
-}
-
-bool initAlerts(const StatsdConfig& config,
-                const unordered_map<int64_t, int>& metricProducerMap,
-                unordered_map<int64_t, int>& alertTrackerMap,
-                const sp<AlarmMonitor>& anomalyAlarmMonitor,
-                vector<sp<MetricProducer>>& allMetricProducers,
-                vector<sp<AnomalyTracker>>& allAnomalyTrackers) {
-    for (int i = 0; i < config.alert_size(); i++) {
-        const Alert& alert = config.alert(i);
-        const auto& itr = metricProducerMap.find(alert.metric_id());
-        if (itr == metricProducerMap.end()) {
-            ALOGW("alert \"%lld\" has unknown metric id: \"%lld\"", (long long)alert.id(),
-                  (long long)alert.metric_id());
-            return false;
-        }
-        if (!alert.has_trigger_if_sum_gt()) {
-            ALOGW("invalid alert: missing threshold");
-            return false;
-        }
-        if (alert.trigger_if_sum_gt() < 0 || alert.num_buckets() <= 0) {
-            ALOGW("invalid alert: threshold=%f num_buckets= %d",
-                  alert.trigger_if_sum_gt(), alert.num_buckets());
-            return false;
-        }
-        const int metricIndex = itr->second;
-        sp<MetricProducer> metric = allMetricProducers[metricIndex];
-        sp<AnomalyTracker> anomalyTracker = metric->addAnomalyTracker(alert, anomalyAlarmMonitor);
-        if (anomalyTracker == nullptr) {
-            // The ALOGW for this invalid alert was already displayed in addAnomalyTracker().
-            return false;
-        }
-        alertTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size()));
-        allAnomalyTrackers.push_back(anomalyTracker);
-    }
-    for (int i = 0; i < config.subscription_size(); ++i) {
-        const Subscription& subscription = config.subscription(i);
-        if (subscription.rule_type() != Subscription::ALERT) {
-            continue;
-        }
-        if (subscription.subscriber_information_case() ==
-            Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) {
-            ALOGW("subscription \"%lld\" has no subscriber info.\"",
-                (long long)subscription.id());
-            return false;
-        }
-        const auto& itr = alertTrackerMap.find(subscription.rule_id());
-        if (itr == alertTrackerMap.end()) {
-            ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"",
-                (long long)subscription.id(), (long long)subscription.rule_id());
-            return false;
-        }
-        const int anomalyTrackerIndex = itr->second;
-        allAnomalyTrackers[anomalyTrackerIndex]->addSubscription(subscription);
-    }
-    return true;
-}
-
-bool initAlarms(const StatsdConfig& config, const ConfigKey& key,
-                const sp<AlarmMonitor>& periodicAlarmMonitor,
-                const int64_t timeBaseNs, const int64_t currentTimeNs,
-                vector<sp<AlarmTracker>>& allAlarmTrackers) {
-    unordered_map<int64_t, int> alarmTrackerMap;
-    int64_t startMillis = timeBaseNs / 1000 / 1000;
-    int64_t currentTimeMillis = currentTimeNs / 1000 /1000;
-    for (int i = 0; i < config.alarm_size(); i++) {
-        const Alarm& alarm = config.alarm(i);
-        if (alarm.offset_millis() <= 0) {
-            ALOGW("Alarm offset_millis should be larger than 0.");
-            return false;
-        }
-        if (alarm.period_millis() <= 0) {
-            ALOGW("Alarm period_millis should be larger than 0.");
-            return false;
-        }
-        alarmTrackerMap.insert(std::make_pair(alarm.id(), allAlarmTrackers.size()));
-        allAlarmTrackers.push_back(
-            new AlarmTracker(startMillis, currentTimeMillis,
-                             alarm, key, periodicAlarmMonitor));
-    }
-    for (int i = 0; i < config.subscription_size(); ++i) {
-        const Subscription& subscription = config.subscription(i);
-        if (subscription.rule_type() != Subscription::ALARM) {
-            continue;
-        }
-        if (subscription.subscriber_information_case() ==
-            Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) {
-            ALOGW("subscription \"%lld\" has no subscriber info.\"",
-                (long long)subscription.id());
-            return false;
-        }
-        const auto& itr = alarmTrackerMap.find(subscription.rule_id());
-        if (itr == alarmTrackerMap.end()) {
-            ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"",
-                (long long)subscription.id(), (long long)subscription.rule_id());
-            return false;
-        }
-        const int trackerIndex = itr->second;
-        allAlarmTrackers[trackerIndex]->addSubscription(subscription);
-    }
-    return true;
-}
-
-bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
-                      const sp<StatsPullerManager>& pullerManager,
-                      const sp<AlarmMonitor>& anomalyAlarmMonitor,
-                      const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
-                      const int64_t currentTimeNs, set<int>& allTagIds,
-                      vector<sp<LogMatchingTracker>>& allAtomMatchers,
-                      vector<sp<ConditionTracker>>& allConditionTrackers,
-                      vector<sp<MetricProducer>>& allMetricProducers,
-                      vector<sp<AnomalyTracker>>& allAnomalyTrackers,
-                      vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers,
-                      unordered_map<int, std::vector<int>>& conditionToMetricMap,
-                      unordered_map<int, std::vector<int>>& trackerToMetricMap,
-                      unordered_map<int, std::vector<int>>& trackerToConditionMap,
-                      unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
-                      unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
-                      unordered_map<int64_t, int>& alertTrackerMap,
-                      vector<int>& metricsWithActivation,
-                      std::set<int64_t>& noReportMetricIds) {
-    unordered_map<int64_t, int> logTrackerMap;
-    unordered_map<int64_t, int> conditionTrackerMap;
-    vector<ConditionState> initialConditionCache;
-    unordered_map<int64_t, int> metricProducerMap;
-    unordered_map<int64_t, int> stateAtomIdMap;
-    unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
-
-    if (!initLogTrackers(config, uidMap, logTrackerMap, allAtomMatchers, allTagIds)) {
-        ALOGE("initLogMatchingTrackers failed");
-        return false;
-    }
-    VLOG("initLogMatchingTrackers succeed...");
-
-    if (!initConditions(key, config, logTrackerMap, conditionTrackerMap, allConditionTrackers,
-                        trackerToConditionMap, initialConditionCache)) {
-        ALOGE("initConditionTrackers failed");
-        return false;
-    }
-
-    if (!initStates(config, stateAtomIdMap, allStateGroupMaps)) {
-        ALOGE("initStates failed");
-        return false;
-    }
-    if (!initMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, logTrackerMap,
-                     conditionTrackerMap, allAtomMatchers, stateAtomIdMap, allStateGroupMaps,
-                     allConditionTrackers, initialConditionCache, allMetricProducers,
-                     conditionToMetricMap, trackerToMetricMap, metricProducerMap, noReportMetricIds,
-                     activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                     metricsWithActivation)) {
-        ALOGE("initMetricProducers failed");
-        return false;
-    }
-    if (!initAlerts(config, metricProducerMap, alertTrackerMap, anomalyAlarmMonitor,
-                    allMetricProducers, allAnomalyTrackers)) {
-        ALOGE("initAlerts failed");
-        return false;
-    }
-    if (!initAlarms(config, key, periodicAlarmMonitor,
-                    timeBaseNs, currentTimeNs, allPeriodicAlarmTrackers)) {
-        ALOGE("initAlarms failed");
-        return false;
-    }
-
-    return true;
-}
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/bin/src/metrics/metrics_manager_util.h b/bin/src/metrics/metrics_manager_util.h
deleted file mode 100644
index 96b5c26..0000000
--- a/bin/src/metrics/metrics_manager_util.h
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#pragma once
-
-#include <set>
-#include <unordered_map>
-#include <vector>
-
-#include "../anomaly/AlarmTracker.h"
-#include "../condition/ConditionTracker.h"
-#include "../external/StatsPullerManager.h"
-#include "../matchers/LogMatchingTracker.h"
-#include "../metrics/MetricProducer.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Helper functions for MetricsManager to initialize from StatsdConfig.
-// *Note*: only initStatsdConfig() should be called from outside.
-// All other functions are intermediate
-// steps, created to make unit tests easier. And most of the parameters in these
-// functions are temporary objects in the initialization phase.
-
-// Initialize the LogMatchingTrackers.
-// input:
-// [key]: the config key that this config belongs to
-// [config]: the input StatsdConfig
-// output:
-// [logTrackerMap]: this map should contain matcher name to index mapping
-// [allAtomMatchers]: should store the sp to all the LogMatchingTracker
-// [allTagIds]: contains the set of all interesting tag ids to this config.
-bool initLogTrackers(const StatsdConfig& config,
-                     const UidMap& uidMap,
-                     std::unordered_map<int64_t, int>& logTrackerMap,
-                     std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
-                     std::set<int>& allTagIds);
-
-// Initialize ConditionTrackers
-// input:
-// [key]: the config key that this config belongs to
-// [config]: the input config
-// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
-// output:
-// [conditionTrackerMap]: this map should contain condition name to index mapping
-// [allConditionTrackers]: stores the sp to all the ConditionTrackers
-// [trackerToConditionMap]: contain the mapping from index of
-//                        log tracker to condition trackers that use the log tracker
-// [initialConditionCache]: stores the initial conditions for each ConditionTracker
-bool initConditions(const ConfigKey& key, const StatsdConfig& config,
-                    const std::unordered_map<int64_t, int>& logTrackerMap,
-                    std::unordered_map<int64_t, int>& conditionTrackerMap,
-                    std::vector<sp<ConditionTracker>>& allConditionTrackers,
-                    std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
-                    std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
-                    std::vector<ConditionState>& initialConditionCache);
-
-// Initialize State maps using State protos in the config. These maps will
-// eventually be passed to MetricProducers to initialize their state info.
-// input:
-// [config]: the input config
-// output:
-// [stateAtomIdMap]: this map should contain the mapping from state ids to atom ids
-// [allStateGroupMaps]: this map should contain the mapping from states ids and state
-//                      values to state group ids for all states
-bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
-                unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps);
-
-// Initialize MetricProducers.
-// input:
-// [key]: the config key that this config belongs to
-// [config]: the input config
-// [timeBaseSec]: start time base for all metrics
-// [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
-// [conditionTrackerMap]: condition name to index mapping
-// [stateAtomIdMap]: contains the mapping from state ids to atom ids
-// [allStateGroupMaps]: contains the mapping from atom ids and state values to
-//                      state group ids for all states
-// output:
-// [allMetricProducers]: contains the list of sp to the MetricProducers created.
-// [conditionToMetricMap]: contains the mapping from condition tracker index to
-//                          the list of MetricProducer index
-// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
-bool initMetrics(
-        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
-        const int64_t currentTimeNs, UidMap& uidMap, const sp<StatsPullerManager>& pullerManager,
-        const std::unordered_map<int64_t, int>& logTrackerMap,
-        const std::unordered_map<int64_t, int>& conditionTrackerMap,
-        const std::unordered_map<int, std::vector<MetricConditionLink>>& eventConditionLinks,
-        const vector<sp<LogMatchingTracker>>& allAtomMatchers,
-        const unordered_map<int64_t, int>& stateAtomIdMap,
-        const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
-        vector<sp<ConditionTracker>>& allConditionTrackers,
-        const std::vector<ConditionState>& initialConditionCache,
-        std::vector<sp<MetricProducer>>& allMetricProducers,
-        std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
-        std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
-        std::set<int64_t>& noReportMetricIds,
-        std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
-        std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
-        std::vector<int>& metricsWithActivation);
-
-// Initialize MetricsManager from StatsdConfig.
-// Parameters are the members of MetricsManager. See MetricsManager for declaration.
-bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, UidMap& uidMap,
-                      const sp<StatsPullerManager>& pullerManager,
-                      const sp<AlarmMonitor>& anomalyAlarmMonitor,
-                      const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
-                      const int64_t currentTimeNs, std::set<int>& allTagIds,
-                      std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
-                      std::vector<sp<ConditionTracker>>& allConditionTrackers,
-                      std::vector<sp<MetricProducer>>& allMetricProducers,
-                      vector<sp<AnomalyTracker>>& allAnomalyTrackers,
-                      vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers,
-                      std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
-                      std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
-                      std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
-                      unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
-                      unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
-                      std::unordered_map<int64_t, int>& alertTrackerMap,
-                      vector<int>& metricsWithActivation,
-                      std::set<int64_t>& noReportMetricIds);
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/bin/src/metrics/parsing_utils/config_update_utils.cpp b/bin/src/metrics/parsing_utils/config_update_utils.cpp
new file mode 100644
index 0000000..39789cd
--- /dev/null
+++ b/bin/src/metrics/parsing_utils/config_update_utils.cpp
@@ -0,0 +1,1118 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG false  // STOPSHIP if true
+#include "Log.h"
+
+#include "config_update_utils.h"
+
+#include "external/StatsPullerManager.h"
+#include "hash.h"
+#include "matchers/EventMatcherWizard.h"
+#include "metrics_manager_util.h"
+
+using google::protobuf::MessageLite;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Recursive function to determine if a matcher needs to be updated. Populates matcherToUpdate.
+// Returns whether the function was successful or not.
+bool determineMatcherUpdateStatus(const StatsdConfig& config, const int matcherIdx,
+                                  const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+                                  const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
+                                  const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+                                  vector<UpdateStatus>& matchersToUpdate,
+                                  vector<bool>& cycleTracker) {
+    // Have already examined this matcher.
+    if (matchersToUpdate[matcherIdx] != UPDATE_UNKNOWN) {
+        return true;
+    }
+
+    const AtomMatcher& matcher = config.atom_matcher(matcherIdx);
+    int64_t id = matcher.id();
+    // Check if new matcher.
+    const auto& oldAtomMatchingTrackerIt = oldAtomMatchingTrackerMap.find(id);
+    if (oldAtomMatchingTrackerIt == oldAtomMatchingTrackerMap.end()) {
+        matchersToUpdate[matcherIdx] = UPDATE_NEW;
+        return true;
+    }
+
+    // This is an existing matcher. Check if it has changed.
+    string serializedMatcher;
+    if (!matcher.SerializeToString(&serializedMatcher)) {
+        ALOGE("Unable to serialize matcher %lld", (long long)id);
+        return false;
+    }
+    uint64_t newProtoHash = Hash64(serializedMatcher);
+    if (newProtoHash != oldAtomMatchingTrackers[oldAtomMatchingTrackerIt->second]->getProtoHash()) {
+        matchersToUpdate[matcherIdx] = UPDATE_REPLACE;
+        return true;
+    }
+
+    switch (matcher.contents_case()) {
+        case AtomMatcher::ContentsCase::kSimpleAtomMatcher: {
+            matchersToUpdate[matcherIdx] = UPDATE_PRESERVE;
+            return true;
+        }
+        case AtomMatcher::ContentsCase::kCombination: {
+            // Recurse to check if children have changed.
+            cycleTracker[matcherIdx] = true;
+            UpdateStatus status = UPDATE_PRESERVE;
+            for (const int64_t childMatcherId : matcher.combination().matcher()) {
+                const auto& childIt = newAtomMatchingTrackerMap.find(childMatcherId);
+                if (childIt == newAtomMatchingTrackerMap.end()) {
+                    ALOGW("Matcher %lld not found in the config", (long long)childMatcherId);
+                    return false;
+                }
+                const int childIdx = childIt->second;
+                if (cycleTracker[childIdx]) {
+                    ALOGE("Cycle detected in matcher config");
+                    return false;
+                }
+                if (!determineMatcherUpdateStatus(
+                            config, childIdx, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers,
+                            newAtomMatchingTrackerMap, matchersToUpdate, cycleTracker)) {
+                    return false;
+                }
+
+                if (matchersToUpdate[childIdx] == UPDATE_REPLACE) {
+                    status = UPDATE_REPLACE;
+                    break;
+                }
+            }
+            matchersToUpdate[matcherIdx] = status;
+            cycleTracker[matcherIdx] = false;
+            return true;
+        }
+        default: {
+            ALOGE("Matcher \"%lld\" malformed", (long long)id);
+            return false;
+        }
+    }
+    return true;
+}
+
+bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
+                                const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+                                const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
+                                set<int>& allTagIds,
+                                unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+                                vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
+                                set<int64_t>& replacedMatchers) {
+    const int atomMatcherCount = config.atom_matcher_size();
+    vector<AtomMatcher> matcherProtos;
+    matcherProtos.reserve(atomMatcherCount);
+    newAtomMatchingTrackers.reserve(atomMatcherCount);
+
+    // Maps matcher id to their position in the config. For fast lookup of dependencies.
+    for (int i = 0; i < atomMatcherCount; i++) {
+        const AtomMatcher& matcher = config.atom_matcher(i);
+        if (newAtomMatchingTrackerMap.find(matcher.id()) != newAtomMatchingTrackerMap.end()) {
+            ALOGE("Duplicate atom matcher found for id %lld", (long long)matcher.id());
+            return false;
+        }
+        newAtomMatchingTrackerMap[matcher.id()] = i;
+        matcherProtos.push_back(matcher);
+    }
+
+    // For combination matchers, we need to determine if any children need to be updated.
+    vector<UpdateStatus> matchersToUpdate(atomMatcherCount, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(atomMatcherCount, false);
+    for (int i = 0; i < atomMatcherCount; i++) {
+        if (!determineMatcherUpdateStatus(config, i, oldAtomMatchingTrackerMap,
+                                          oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                          matchersToUpdate, cycleTracker)) {
+            return false;
+        }
+    }
+
+    for (int i = 0; i < atomMatcherCount; i++) {
+        const AtomMatcher& matcher = config.atom_matcher(i);
+        const int64_t id = matcher.id();
+        switch (matchersToUpdate[i]) {
+            case UPDATE_PRESERVE: {
+                const auto& oldAtomMatchingTrackerIt = oldAtomMatchingTrackerMap.find(id);
+                if (oldAtomMatchingTrackerIt == oldAtomMatchingTrackerMap.end()) {
+                    ALOGE("Could not find AtomMatcher %lld in the previous config, but expected it "
+                          "to be there",
+                          (long long)id);
+                    return false;
+                }
+                const sp<AtomMatchingTracker>& tracker =
+                        oldAtomMatchingTrackers[oldAtomMatchingTrackerIt->second];
+                if (!tracker->onConfigUpdated(matcherProtos[i], i, newAtomMatchingTrackerMap)) {
+                    ALOGW("Config update failed for matcher %lld", (long long)id);
+                    return false;
+                }
+                newAtomMatchingTrackers.push_back(tracker);
+                break;
+            }
+            case UPDATE_REPLACE:
+                replacedMatchers.insert(id);
+                [[fallthrough]];  // Intentionally fallthrough to create the new matcher.
+            case UPDATE_NEW: {
+                sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, i, uidMap);
+                if (tracker == nullptr) {
+                    return false;
+                }
+                newAtomMatchingTrackers.push_back(tracker);
+                break;
+            }
+            default: {
+                ALOGE("Matcher \"%lld\" update state is unknown. This should never happen",
+                      (long long)id);
+                return false;
+            }
+        }
+    }
+
+    std::fill(cycleTracker.begin(), cycleTracker.end(), false);
+    for (auto& matcher : newAtomMatchingTrackers) {
+        if (!matcher->init(matcherProtos, newAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                           cycleTracker)) {
+            return false;
+        }
+        // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only.
+        const set<int>& tagIds = matcher->getAtomIds();
+        allTagIds.insert(tagIds.begin(), tagIds.end());
+    }
+
+    return true;
+}
+
+// Recursive function to determine if a condition needs to be updated. Populates conditionsToUpdate.
+// Returns whether the function was successful or not.
+bool determineConditionUpdateStatus(const StatsdConfig& config, const int conditionIdx,
+                                    const unordered_map<int64_t, int>& oldConditionTrackerMap,
+                                    const vector<sp<ConditionTracker>>& oldConditionTrackers,
+                                    const unordered_map<int64_t, int>& newConditionTrackerMap,
+                                    const set<int64_t>& replacedMatchers,
+                                    vector<UpdateStatus>& conditionsToUpdate,
+                                    vector<bool>& cycleTracker) {
+    // Have already examined this condition.
+    if (conditionsToUpdate[conditionIdx] != UPDATE_UNKNOWN) {
+        return true;
+    }
+
+    const Predicate& predicate = config.predicate(conditionIdx);
+    int64_t id = predicate.id();
+    // Check if new condition.
+    const auto& oldConditionTrackerIt = oldConditionTrackerMap.find(id);
+    if (oldConditionTrackerIt == oldConditionTrackerMap.end()) {
+        conditionsToUpdate[conditionIdx] = UPDATE_NEW;
+        return true;
+    }
+
+    // This is an existing condition. Check if it has changed.
+    string serializedCondition;
+    if (!predicate.SerializeToString(&serializedCondition)) {
+        ALOGE("Unable to serialize matcher %lld", (long long)id);
+        return false;
+    }
+    uint64_t newProtoHash = Hash64(serializedCondition);
+    if (newProtoHash != oldConditionTrackers[oldConditionTrackerIt->second]->getProtoHash()) {
+        conditionsToUpdate[conditionIdx] = UPDATE_REPLACE;
+        return true;
+    }
+
+    switch (predicate.contents_case()) {
+        case Predicate::ContentsCase::kSimplePredicate: {
+            // Need to check if any of the underlying matchers changed.
+            const SimplePredicate& simplePredicate = predicate.simple_predicate();
+            if (simplePredicate.has_start()) {
+                if (replacedMatchers.find(simplePredicate.start()) != replacedMatchers.end()) {
+                    conditionsToUpdate[conditionIdx] = UPDATE_REPLACE;
+                    return true;
+                }
+            }
+            if (simplePredicate.has_stop()) {
+                if (replacedMatchers.find(simplePredicate.stop()) != replacedMatchers.end()) {
+                    conditionsToUpdate[conditionIdx] = UPDATE_REPLACE;
+                    return true;
+                }
+            }
+            if (simplePredicate.has_stop_all()) {
+                if (replacedMatchers.find(simplePredicate.stop_all()) != replacedMatchers.end()) {
+                    conditionsToUpdate[conditionIdx] = UPDATE_REPLACE;
+                    return true;
+                }
+            }
+            conditionsToUpdate[conditionIdx] = UPDATE_PRESERVE;
+            return true;
+        }
+        case Predicate::ContentsCase::kCombination: {
+            // Need to recurse on the children to see if any of the child predicates changed.
+            cycleTracker[conditionIdx] = true;
+            UpdateStatus status = UPDATE_PRESERVE;
+            for (const int64_t childPredicateId : predicate.combination().predicate()) {
+                const auto& childIt = newConditionTrackerMap.find(childPredicateId);
+                if (childIt == newConditionTrackerMap.end()) {
+                    ALOGW("Predicate %lld not found in the config", (long long)childPredicateId);
+                    return false;
+                }
+                const int childIdx = childIt->second;
+                if (cycleTracker[childIdx]) {
+                    ALOGE("Cycle detected in predicate config");
+                    return false;
+                }
+                if (!determineConditionUpdateStatus(config, childIdx, oldConditionTrackerMap,
+                                                    oldConditionTrackers, newConditionTrackerMap,
+                                                    replacedMatchers, conditionsToUpdate,
+                                                    cycleTracker)) {
+                    return false;
+                }
+
+                if (conditionsToUpdate[childIdx] == UPDATE_REPLACE) {
+                    status = UPDATE_REPLACE;
+                    break;
+                }
+            }
+            conditionsToUpdate[conditionIdx] = status;
+            cycleTracker[conditionIdx] = false;
+            return true;
+        }
+        default: {
+            ALOGE("Predicate \"%lld\" malformed", (long long)id);
+            return false;
+        }
+    }
+
+    return true;
+}
+
+bool updateConditions(const ConfigKey& key, const StatsdConfig& config,
+                      const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                      const set<int64_t>& replacedMatchers,
+                      const unordered_map<int64_t, int>& oldConditionTrackerMap,
+                      const vector<sp<ConditionTracker>>& oldConditionTrackers,
+                      unordered_map<int64_t, int>& newConditionTrackerMap,
+                      vector<sp<ConditionTracker>>& newConditionTrackers,
+                      unordered_map<int, vector<int>>& trackerToConditionMap,
+                      vector<ConditionState>& conditionCache, set<int64_t>& replacedConditions) {
+    vector<Predicate> conditionProtos;
+    const int conditionTrackerCount = config.predicate_size();
+    conditionProtos.reserve(conditionTrackerCount);
+    newConditionTrackers.reserve(conditionTrackerCount);
+    conditionCache.assign(conditionTrackerCount, ConditionState::kNotEvaluated);
+
+    for (int i = 0; i < conditionTrackerCount; i++) {
+        const Predicate& condition = config.predicate(i);
+        if (newConditionTrackerMap.find(condition.id()) != newConditionTrackerMap.end()) {
+            ALOGE("Duplicate Predicate found!");
+            return false;
+        }
+        newConditionTrackerMap[condition.id()] = i;
+        conditionProtos.push_back(condition);
+    }
+
+    vector<UpdateStatus> conditionsToUpdate(conditionTrackerCount, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(conditionTrackerCount, false);
+    for (int i = 0; i < conditionTrackerCount; i++) {
+        if (!determineConditionUpdateStatus(config, i, oldConditionTrackerMap, oldConditionTrackers,
+                                            newConditionTrackerMap, replacedMatchers,
+                                            conditionsToUpdate, cycleTracker)) {
+            return false;
+        }
+    }
+
+    // Update status has been determined for all conditions. Now perform the update.
+    set<int> preservedConditions;
+    for (int i = 0; i < conditionTrackerCount; i++) {
+        const Predicate& predicate = config.predicate(i);
+        const int64_t id = predicate.id();
+        switch (conditionsToUpdate[i]) {
+            case UPDATE_PRESERVE: {
+                preservedConditions.insert(i);
+                const auto& oldConditionTrackerIt = oldConditionTrackerMap.find(id);
+                if (oldConditionTrackerIt == oldConditionTrackerMap.end()) {
+                    ALOGE("Could not find Predicate %lld in the previous config, but expected it "
+                          "to be there",
+                          (long long)id);
+                    return false;
+                }
+                const int oldIndex = oldConditionTrackerIt->second;
+                newConditionTrackers.push_back(oldConditionTrackers[oldIndex]);
+                break;
+            }
+            case UPDATE_REPLACE:
+                replacedConditions.insert(id);
+                [[fallthrough]];  // Intentionally fallthrough to create the new condition tracker.
+            case UPDATE_NEW: {
+                sp<ConditionTracker> tracker =
+                        createConditionTracker(key, predicate, i, atomMatchingTrackerMap);
+                if (tracker == nullptr) {
+                    return false;
+                }
+                newConditionTrackers.push_back(tracker);
+                break;
+            }
+            default: {
+                ALOGE("Condition \"%lld\" update state is unknown. This should never happen",
+                      (long long)id);
+                return false;
+            }
+        }
+    }
+
+    // Update indices of preserved predicates.
+    for (const int conditionIndex : preservedConditions) {
+        if (!newConditionTrackers[conditionIndex]->onConfigUpdated(
+                    conditionProtos, conditionIndex, newConditionTrackers, atomMatchingTrackerMap,
+                    newConditionTrackerMap)) {
+            ALOGE("Failed to update condition %lld",
+                  (long long)newConditionTrackers[conditionIndex]->getConditionId());
+            return false;
+        }
+    }
+
+    std::fill(cycleTracker.begin(), cycleTracker.end(), false);
+    for (int conditionIndex = 0; conditionIndex < conditionTrackerCount; conditionIndex++) {
+        const sp<ConditionTracker>& conditionTracker = newConditionTrackers[conditionIndex];
+        // Calling init on preserved conditions is OK. It is needed to fill the condition cache.
+        if (!conditionTracker->init(conditionProtos, newConditionTrackers, newConditionTrackerMap,
+                                    cycleTracker, conditionCache)) {
+            return false;
+        }
+        for (const int trackerIndex : conditionTracker->getAtomMatchingTrackerIndex()) {
+            vector<int>& conditionList = trackerToConditionMap[trackerIndex];
+            conditionList.push_back(conditionIndex);
+        }
+    }
+    return true;
+}
+
+bool updateStates(const StatsdConfig& config, const map<int64_t, uint64_t>& oldStateProtoHashes,
+                  unordered_map<int64_t, int>& stateAtomIdMap,
+                  unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+                  map<int64_t, uint64_t>& newStateProtoHashes, set<int64_t>& replacedStates) {
+    // Share with metrics_manager_util.
+    if (!initStates(config, stateAtomIdMap, allStateGroupMaps, newStateProtoHashes)) {
+        return false;
+    }
+
+    for (const auto& [stateId, stateHash] : oldStateProtoHashes) {
+        const auto& it = newStateProtoHashes.find(stateId);
+        if (it != newStateProtoHashes.end() && it->second != stateHash) {
+            replacedStates.insert(stateId);
+        }
+    }
+    return true;
+}
+// Returns true if any matchers in the metric activation were replaced.
+bool metricActivationDepsChange(const StatsdConfig& config,
+                                const unordered_map<int64_t, int>& metricToActivationMap,
+                                const int64_t metricId, const set<int64_t>& replacedMatchers) {
+    const auto& metricActivationIt = metricToActivationMap.find(metricId);
+    if (metricActivationIt == metricToActivationMap.end()) {
+        return false;
+    }
+    const MetricActivation& metricActivation = config.metric_activation(metricActivationIt->second);
+    for (int i = 0; i < metricActivation.event_activation_size(); i++) {
+        const EventActivation& activation = metricActivation.event_activation(i);
+        if (replacedMatchers.find(activation.atom_matcher_id()) != replacedMatchers.end()) {
+            return true;
+        }
+        if (activation.has_deactivation_atom_matcher_id()) {
+            if (replacedMatchers.find(activation.deactivation_atom_matcher_id()) !=
+                replacedMatchers.end()) {
+                return true;
+            }
+        }
+    }
+    return false;
+}
+
+bool determineMetricUpdateStatus(
+        const StatsdConfig& config, const MessageLite& metric, const int64_t metricId,
+        const MetricType metricType, const set<int64_t>& matcherDependencies,
+        const set<int64_t>& conditionDependencies,
+        const ::google::protobuf::RepeatedField<int64_t>& stateDependencies,
+        const ::google::protobuf::RepeatedPtrField<MetricConditionLink>& conditionLinks,
+        const unordered_map<int64_t, int>& oldMetricProducerMap,
+        const vector<sp<MetricProducer>>& oldMetricProducers,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        const set<int64_t>& replacedMatchers, const set<int64_t>& replacedConditions,
+        const set<int64_t>& replacedStates, UpdateStatus& updateStatus) {
+    // Check if new metric
+    const auto& oldMetricProducerIt = oldMetricProducerMap.find(metricId);
+    if (oldMetricProducerIt == oldMetricProducerMap.end()) {
+        updateStatus = UPDATE_NEW;
+        return true;
+    }
+
+    // This is an existing metric, check if it has changed.
+    uint64_t metricHash;
+    if (!getMetricProtoHash(config, metric, metricId, metricToActivationMap, metricHash)) {
+        return false;
+    }
+    const sp<MetricProducer> oldMetricProducer = oldMetricProducers[oldMetricProducerIt->second];
+    if (oldMetricProducer->getMetricType() != metricType ||
+        oldMetricProducer->getProtoHash() != metricHash) {
+        updateStatus = UPDATE_REPLACE;
+        return true;
+    }
+
+    // Take intersections of the matchers/predicates/states that the metric
+    // depends on with those that have been replaced. If a metric depends on any
+    // replaced component, it too must be replaced.
+    set<int64_t> intersection;
+    set_intersection(matcherDependencies.begin(), matcherDependencies.end(),
+                     replacedMatchers.begin(), replacedMatchers.end(),
+                     inserter(intersection, intersection.begin()));
+    if (intersection.size() > 0) {
+        updateStatus = UPDATE_REPLACE;
+        return true;
+    }
+    set_intersection(conditionDependencies.begin(), conditionDependencies.end(),
+                     replacedConditions.begin(), replacedConditions.end(),
+                     inserter(intersection, intersection.begin()));
+    if (intersection.size() > 0) {
+        updateStatus = UPDATE_REPLACE;
+        return true;
+    }
+    set_intersection(stateDependencies.begin(), stateDependencies.end(), replacedStates.begin(),
+                     replacedStates.end(), inserter(intersection, intersection.begin()));
+    if (intersection.size() > 0) {
+        updateStatus = UPDATE_REPLACE;
+        return true;
+    }
+
+    for (const auto& metricConditionLink : conditionLinks) {
+        if (replacedConditions.find(metricConditionLink.condition()) != replacedConditions.end()) {
+            updateStatus = UPDATE_REPLACE;
+            return true;
+        }
+    }
+
+    if (metricActivationDepsChange(config, metricToActivationMap, metricId, replacedMatchers)) {
+        updateStatus = UPDATE_REPLACE;
+        return true;
+    }
+
+    updateStatus = UPDATE_PRESERVE;
+    return true;
+}
+
+bool determineAllMetricUpdateStatuses(const StatsdConfig& config,
+                                      const unordered_map<int64_t, int>& oldMetricProducerMap,
+                                      const vector<sp<MetricProducer>>& oldMetricProducers,
+                                      const unordered_map<int64_t, int>& metricToActivationMap,
+                                      const set<int64_t>& replacedMatchers,
+                                      const set<int64_t>& replacedConditions,
+                                      const set<int64_t>& replacedStates,
+                                      vector<UpdateStatus>& metricsToUpdate) {
+    int metricIndex = 0;
+    for (int i = 0; i < config.count_metric_size(); i++, metricIndex++) {
+        const CountMetric& metric = config.count_metric(i);
+        set<int64_t> conditionDependencies;
+        if (metric.has_condition()) {
+            conditionDependencies.insert(metric.condition());
+        }
+        if (!determineMetricUpdateStatus(
+                    config, metric, metric.id(), METRIC_TYPE_COUNT, {metric.what()},
+                    conditionDependencies, metric.slice_by_state(), metric.links(),
+                    oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+                    replacedMatchers, replacedConditions, replacedStates,
+                    metricsToUpdate[metricIndex])) {
+            return false;
+        }
+    }
+    for (int i = 0; i < config.duration_metric_size(); i++, metricIndex++) {
+        const DurationMetric& metric = config.duration_metric(i);
+        set<int64_t> conditionDependencies({metric.what()});
+        if (metric.has_condition()) {
+            conditionDependencies.insert(metric.condition());
+        }
+        if (!determineMetricUpdateStatus(
+                    config, metric, metric.id(), METRIC_TYPE_DURATION, /*matcherDependencies=*/{},
+                    conditionDependencies, metric.slice_by_state(), metric.links(),
+                    oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+                    replacedMatchers, replacedConditions, replacedStates,
+                    metricsToUpdate[metricIndex])) {
+            return false;
+        }
+    }
+    for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
+        const EventMetric& metric = config.event_metric(i);
+        set<int64_t> conditionDependencies;
+        if (metric.has_condition()) {
+            conditionDependencies.insert(metric.condition());
+        }
+        if (!determineMetricUpdateStatus(
+                    config, metric, metric.id(), METRIC_TYPE_EVENT, {metric.what()},
+                    conditionDependencies, ::google::protobuf::RepeatedField<int64_t>(),
+                    metric.links(), oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+                    replacedMatchers, replacedConditions, replacedStates,
+                    metricsToUpdate[metricIndex])) {
+            return false;
+        }
+    }
+    for (int i = 0; i < config.value_metric_size(); i++, metricIndex++) {
+        const ValueMetric& metric = config.value_metric(i);
+        set<int64_t> conditionDependencies;
+        if (metric.has_condition()) {
+            conditionDependencies.insert(metric.condition());
+        }
+        if (!determineMetricUpdateStatus(
+                    config, metric, metric.id(), METRIC_TYPE_VALUE, {metric.what()},
+                    conditionDependencies, metric.slice_by_state(), metric.links(),
+                    oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+                    replacedMatchers, replacedConditions, replacedStates,
+                    metricsToUpdate[metricIndex])) {
+            return false;
+        }
+    }
+    for (int i = 0; i < config.gauge_metric_size(); i++, metricIndex++) {
+        const GaugeMetric& metric = config.gauge_metric(i);
+        set<int64_t> conditionDependencies;
+        if (metric.has_condition()) {
+            conditionDependencies.insert(metric.condition());
+        }
+        set<int64_t> matcherDependencies({metric.what()});
+        if (metric.has_trigger_event()) {
+            matcherDependencies.insert(metric.trigger_event());
+        }
+        if (!determineMetricUpdateStatus(
+                    config, metric, metric.id(), METRIC_TYPE_GAUGE, matcherDependencies,
+                    conditionDependencies, ::google::protobuf::RepeatedField<int64_t>(),
+                    metric.links(), oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+                    replacedMatchers, replacedConditions, replacedStates,
+                    metricsToUpdate[metricIndex])) {
+            return false;
+        }
+    }
+    return true;
+}
+
+// Called when a metric is preserved during a config update. Finds the metric in oldMetricProducers
+// and calls onConfigUpdated to update all indices.
+optional<sp<MetricProducer>> updateMetric(
+        const StatsdConfig& config, const int configIndex, const int metricIndex,
+        const int64_t metricId, const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap, const sp<ConditionWizard>& wizard,
+        const unordered_map<int64_t, int>& oldMetricProducerMap,
+        const vector<sp<MetricProducer>>& oldMetricProducers,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    const auto& oldMetricProducerIt = oldMetricProducerMap.find(metricId);
+    if (oldMetricProducerIt == oldMetricProducerMap.end()) {
+        ALOGE("Could not find Metric %lld in the previous config, but expected it "
+              "to be there",
+              (long long)metricId);
+        return nullopt;
+    }
+    const int oldIndex = oldMetricProducerIt->second;
+    sp<MetricProducer> producer = oldMetricProducers[oldIndex];
+    if (!producer->onConfigUpdated(config, configIndex, metricIndex, allAtomMatchingTrackers,
+                                   oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap,
+                                   matcherWizard, allConditionTrackers, conditionTrackerMap, wizard,
+                                   metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
+                                   activationAtomTrackerToMetricMap,
+                                   deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+        return nullopt;
+    }
+    return {producer};
+}
+
+bool updateMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+                   const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+                   const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+                   const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+                   const set<int64_t>& replacedMatchers,
+                   const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+                   const unordered_map<int64_t, int>& conditionTrackerMap,
+                   const set<int64_t>& replacedConditions,
+                   vector<sp<ConditionTracker>>& allConditionTrackers,
+                   const vector<ConditionState>& initialConditionCache,
+                   const unordered_map<int64_t, int>& stateAtomIdMap,
+                   const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+                   const set<int64_t>& replacedStates,
+                   const unordered_map<int64_t, int>& oldMetricProducerMap,
+                   const vector<sp<MetricProducer>>& oldMetricProducers,
+                   unordered_map<int64_t, int>& newMetricProducerMap,
+                   vector<sp<MetricProducer>>& newMetricProducers,
+                   unordered_map<int, vector<int>>& conditionToMetricMap,
+                   unordered_map<int, vector<int>>& trackerToMetricMap,
+                   set<int64_t>& noReportMetricIds,
+                   unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+                   unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+                   vector<int>& metricsWithActivation, set<int64_t>& replacedMetrics) {
+    sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
+    sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchingTrackers);
+    const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
+                                config.event_metric_size() + config.gauge_metric_size() +
+                                config.value_metric_size();
+    newMetricProducers.reserve(allMetricsCount);
+
+    // Construct map from metric id to metric activation index. The map will be used to determine
+    // the metric activation corresponding to a metric.
+    unordered_map<int64_t, int> metricToActivationMap;
+    for (int i = 0; i < config.metric_activation_size(); i++) {
+        const MetricActivation& metricActivation = config.metric_activation(i);
+        int64_t metricId = metricActivation.metric_id();
+        if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) {
+            ALOGE("Metric %lld has multiple MetricActivations", (long long)metricId);
+            return false;
+        }
+        metricToActivationMap.insert({metricId, i});
+    }
+
+    vector<UpdateStatus> metricsToUpdate(allMetricsCount, UPDATE_UNKNOWN);
+    if (!determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+                                          metricToActivationMap, replacedMatchers,
+                                          replacedConditions, replacedStates, metricsToUpdate)) {
+        return false;
+    }
+
+    // Now, perform the update. Must iterate the metric types in the same order
+    int metricIndex = 0;
+    for (int i = 0; i < config.count_metric_size(); i++, metricIndex++) {
+        const CountMetric& metric = config.count_metric(i);
+        newMetricProducerMap[metric.id()] = metricIndex;
+        optional<sp<MetricProducer>> producer;
+        switch (metricsToUpdate[metricIndex]) {
+            case UPDATE_PRESERVE: {
+                producer = updateMetric(
+                        config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+                        oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                        allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+                        oldMetricProducers, metricToActivationMap, trackerToMetricMap,
+                        conditionToMetricMap, activationAtomTrackerToMetricMap,
+                        deactivationAtomTrackerToMetricMap, metricsWithActivation);
+                break;
+            }
+            case UPDATE_REPLACE:
+                replacedMetrics.insert(metric.id());
+                [[fallthrough]];  // Intentionally fallthrough to create the new metric producer.
+            case UPDATE_NEW: {
+                producer = createCountMetricProducerAndUpdateMetadata(
+                        key, config, timeBaseNs, currentTimeNs, metric, metricIndex,
+                        allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
+                        conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
+                        allStateGroupMaps, metricToActivationMap, trackerToMetricMap,
+                        conditionToMetricMap, activationAtomTrackerToMetricMap,
+                        deactivationAtomTrackerToMetricMap, metricsWithActivation);
+                break;
+            }
+            default: {
+                ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
+                      (long long)metric.id());
+                return false;
+            }
+        }
+        if (!producer) {
+            return false;
+        }
+        newMetricProducers.push_back(producer.value());
+    }
+    for (int i = 0; i < config.duration_metric_size(); i++, metricIndex++) {
+        const DurationMetric& metric = config.duration_metric(i);
+        newMetricProducerMap[metric.id()] = metricIndex;
+        optional<sp<MetricProducer>> producer;
+        switch (metricsToUpdate[metricIndex]) {
+            case UPDATE_PRESERVE: {
+                producer = updateMetric(
+                        config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+                        oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                        allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+                        oldMetricProducers, metricToActivationMap, trackerToMetricMap,
+                        conditionToMetricMap, activationAtomTrackerToMetricMap,
+                        deactivationAtomTrackerToMetricMap, metricsWithActivation);
+                break;
+            }
+            case UPDATE_REPLACE:
+                replacedMetrics.insert(metric.id());
+                [[fallthrough]];  // Intentionally fallthrough to create the new metric producer.
+            case UPDATE_NEW: {
+                producer = createDurationMetricProducerAndUpdateMetadata(
+                        key, config, timeBaseNs, currentTimeNs, metric, metricIndex,
+                        allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
+                        conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
+                        allStateGroupMaps, metricToActivationMap, trackerToMetricMap,
+                        conditionToMetricMap, activationAtomTrackerToMetricMap,
+                        deactivationAtomTrackerToMetricMap, metricsWithActivation);
+                break;
+            }
+            default: {
+                ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
+                      (long long)metric.id());
+                return false;
+            }
+        }
+        if (!producer) {
+            return false;
+        }
+        newMetricProducers.push_back(producer.value());
+    }
+    for (int i = 0; i < config.event_metric_size(); i++, metricIndex++) {
+        const EventMetric& metric = config.event_metric(i);
+        newMetricProducerMap[metric.id()] = metricIndex;
+        optional<sp<MetricProducer>> producer;
+        switch (metricsToUpdate[metricIndex]) {
+            case UPDATE_PRESERVE: {
+                producer = updateMetric(
+                        config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+                        oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                        allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+                        oldMetricProducers, metricToActivationMap, trackerToMetricMap,
+                        conditionToMetricMap, activationAtomTrackerToMetricMap,
+                        deactivationAtomTrackerToMetricMap, metricsWithActivation);
+                break;
+            }
+            case UPDATE_REPLACE:
+                replacedMetrics.insert(metric.id());
+                [[fallthrough]];  // Intentionally fallthrough to create the new metric producer.
+            case UPDATE_NEW: {
+                producer = createEventMetricProducerAndUpdateMetadata(
+                        key, config, timeBaseNs, metric, metricIndex, allAtomMatchingTrackers,
+                        newAtomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap,
+                        initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap,
+                        conditionToMetricMap, activationAtomTrackerToMetricMap,
+                        deactivationAtomTrackerToMetricMap, metricsWithActivation);
+                break;
+            }
+            default: {
+                ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
+                      (long long)metric.id());
+                return false;
+            }
+        }
+        if (!producer) {
+            return false;
+        }
+        newMetricProducers.push_back(producer.value());
+    }
+
+    for (int i = 0; i < config.value_metric_size(); i++, metricIndex++) {
+        const ValueMetric& metric = config.value_metric(i);
+        newMetricProducerMap[metric.id()] = metricIndex;
+        optional<sp<MetricProducer>> producer;
+        switch (metricsToUpdate[metricIndex]) {
+            case UPDATE_PRESERVE: {
+                producer = updateMetric(
+                        config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+                        oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                        allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+                        oldMetricProducers, metricToActivationMap, trackerToMetricMap,
+                        conditionToMetricMap, activationAtomTrackerToMetricMap,
+                        deactivationAtomTrackerToMetricMap, metricsWithActivation);
+                break;
+            }
+            case UPDATE_REPLACE:
+                replacedMetrics.insert(metric.id());
+                [[fallthrough]];  // Intentionally fallthrough to create the new metric producer.
+            case UPDATE_NEW: {
+                producer = createValueMetricProducerAndUpdateMetadata(
+                        key, config, timeBaseNs, currentTimeNs, pullerManager, metric, metricIndex,
+                        allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
+                        conditionTrackerMap, initialConditionCache, wizard, matcherWizard,
+                        stateAtomIdMap, allStateGroupMaps, metricToActivationMap,
+                        trackerToMetricMap, conditionToMetricMap, activationAtomTrackerToMetricMap,
+                        deactivationAtomTrackerToMetricMap, metricsWithActivation);
+                break;
+            }
+            default: {
+                ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
+                      (long long)metric.id());
+                return false;
+            }
+        }
+        if (!producer) {
+            return false;
+        }
+        newMetricProducers.push_back(producer.value());
+    }
+
+    for (int i = 0; i < config.gauge_metric_size(); i++, metricIndex++) {
+        const GaugeMetric& metric = config.gauge_metric(i);
+        newMetricProducerMap[metric.id()] = metricIndex;
+        optional<sp<MetricProducer>> producer;
+        switch (metricsToUpdate[metricIndex]) {
+            case UPDATE_PRESERVE: {
+                producer = updateMetric(
+                        config, i, metricIndex, metric.id(), allAtomMatchingTrackers,
+                        oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, matcherWizard,
+                        allConditionTrackers, conditionTrackerMap, wizard, oldMetricProducerMap,
+                        oldMetricProducers, metricToActivationMap, trackerToMetricMap,
+                        conditionToMetricMap, activationAtomTrackerToMetricMap,
+                        deactivationAtomTrackerToMetricMap, metricsWithActivation);
+                break;
+            }
+            case UPDATE_REPLACE:
+                replacedMetrics.insert(metric.id());
+                [[fallthrough]];  // Intentionally fallthrough to create the new metric producer.
+            case UPDATE_NEW: {
+                producer = createGaugeMetricProducerAndUpdateMetadata(
+                        key, config, timeBaseNs, currentTimeNs, pullerManager, metric, metricIndex,
+                        allAtomMatchingTrackers, newAtomMatchingTrackerMap, allConditionTrackers,
+                        conditionTrackerMap, initialConditionCache, wizard, matcherWizard,
+                        metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
+                        activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+                        metricsWithActivation);
+                break;
+            }
+            default: {
+                ALOGE("Metric \"%lld\" update state is unknown. This should never happen",
+                      (long long)metric.id());
+                return false;
+            }
+        }
+        if (!producer) {
+            return false;
+        }
+        newMetricProducers.push_back(producer.value());
+    }
+
+    for (int i = 0; i < config.no_report_metric_size(); ++i) {
+        const int64_t noReportMetric = config.no_report_metric(i);
+        if (newMetricProducerMap.find(noReportMetric) == newMetricProducerMap.end()) {
+            ALOGW("no_report_metric %" PRId64 " not exist", noReportMetric);
+            return false;
+        }
+        noReportMetricIds.insert(noReportMetric);
+    }
+    const set<int> atomsAllowedFromAnyUid(config.whitelisted_atom_ids().begin(),
+                                          config.whitelisted_atom_ids().end());
+    for (int i = 0; i < allMetricsCount; i++) {
+        sp<MetricProducer> producer = newMetricProducers[i];
+        // Register metrics to StateTrackers
+        for (int atomId : producer->getSlicedStateAtoms()) {
+            // Register listener for atoms that use allowed_log_sources.
+            // Using atoms allowed from any uid as a sliced state atom is not allowed.
+            // Redo this check for all metrics in case the atoms allowed from any uid changed.
+            if (atomsAllowedFromAnyUid.find(atomId) != atomsAllowedFromAnyUid.end()) {
+                return false;
+                // Preserved metrics should've already registered.`
+            } else if (metricsToUpdate[i] != UPDATE_PRESERVE) {
+                StateManager::getInstance().registerListener(atomId, producer);
+            }
+        }
+    }
+
+    // Init new/replaced metrics.
+    for (size_t i = 0; i < newMetricProducers.size(); i++) {
+        if (metricsToUpdate[i] == UPDATE_REPLACE || metricsToUpdate[i] == UPDATE_NEW) {
+            newMetricProducers[i]->prepareFirstBucket();
+        }
+    }
+    return true;
+}
+
+bool determineAlertUpdateStatus(const Alert& alert,
+                                const unordered_map<int64_t, int>& oldAlertTrackerMap,
+                                const vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
+                                const set<int64_t>& replacedMetrics, UpdateStatus& updateStatus) {
+    // Check if new alert.
+    const auto& oldAnomalyTrackerIt = oldAlertTrackerMap.find(alert.id());
+    if (oldAnomalyTrackerIt == oldAlertTrackerMap.end()) {
+        updateStatus = UPDATE_NEW;
+        return true;
+    }
+
+    // This is an existing alert, check if it has changed.
+    string serializedAlert;
+    if (!alert.SerializeToString(&serializedAlert)) {
+        ALOGW("Unable to serialize alert %lld", (long long)alert.id());
+        return false;
+    }
+    uint64_t newProtoHash = Hash64(serializedAlert);
+    const auto [success, oldProtoHash] =
+            oldAnomalyTrackers[oldAnomalyTrackerIt->second]->getProtoHash();
+    if (!success) {
+        return false;
+    }
+    if (newProtoHash != oldProtoHash) {
+        updateStatus = UPDATE_REPLACE;
+        return true;
+    }
+
+    // Check if the metric this alert relies on has changed.
+    if (replacedMetrics.find(alert.metric_id()) != replacedMetrics.end()) {
+        updateStatus = UPDATE_REPLACE;
+        return true;
+    }
+
+    updateStatus = UPDATE_PRESERVE;
+    return true;
+}
+
+bool updateAlerts(const StatsdConfig& config, const unordered_map<int64_t, int>& metricProducerMap,
+                  const set<int64_t>& replacedMetrics,
+                  const unordered_map<int64_t, int>& oldAlertTrackerMap,
+                  const vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
+                  const sp<AlarmMonitor>& anomalyAlarmMonitor,
+                  vector<sp<MetricProducer>>& allMetricProducers,
+                  unordered_map<int64_t, int>& newAlertTrackerMap,
+                  vector<sp<AnomalyTracker>>& newAnomalyTrackers) {
+    int alertCount = config.alert_size();
+    vector<UpdateStatus> alertUpdateStatuses(alertCount);
+    for (int i = 0; i < alertCount; i++) {
+        if (!determineAlertUpdateStatus(config.alert(i), oldAlertTrackerMap, oldAnomalyTrackers,
+                                        replacedMetrics, alertUpdateStatuses[i])) {
+            return false;
+        }
+    }
+
+    for (int i = 0; i < alertCount; i++) {
+        const Alert& alert = config.alert(i);
+        newAlertTrackerMap[alert.id()] = newAnomalyTrackers.size();
+        switch (alertUpdateStatuses[i]) {
+            case UPDATE_PRESERVE: {
+                // Find the alert and update it.
+                const auto& oldAnomalyTrackerIt = oldAlertTrackerMap.find(alert.id());
+                if (oldAnomalyTrackerIt == oldAlertTrackerMap.end()) {
+                    ALOGW("Could not find AnomalyTracker %lld in the previous config, but "
+                          "expected it to be there",
+                          (long long)alert.id());
+                    return false;
+                }
+                sp<AnomalyTracker> anomalyTracker = oldAnomalyTrackers[oldAnomalyTrackerIt->second];
+                anomalyTracker->onConfigUpdated();
+                // Add the alert to the relevant metric.
+                const auto& metricProducerIt = metricProducerMap.find(alert.metric_id());
+                if (metricProducerIt == metricProducerMap.end()) {
+                    ALOGW("alert \"%lld\" has unknown metric id: \"%lld\"", (long long)alert.id(),
+                          (long long)alert.metric_id());
+                    return false;
+                }
+                allMetricProducers[metricProducerIt->second]->addAnomalyTracker(anomalyTracker);
+                newAnomalyTrackers.push_back(anomalyTracker);
+                break;
+            }
+            case UPDATE_REPLACE:
+            case UPDATE_NEW: {
+                optional<sp<AnomalyTracker>> anomalyTracker = createAnomalyTracker(
+                        alert, anomalyAlarmMonitor, metricProducerMap, allMetricProducers);
+                if (!anomalyTracker) {
+                    return false;
+                }
+                newAnomalyTrackers.push_back(anomalyTracker.value());
+                break;
+            }
+            default: {
+                ALOGE("Alert \"%lld\" update state is unknown. This should never happen",
+                      (long long)alert.id());
+                return false;
+            }
+        }
+    }
+    if (!initSubscribersForSubscriptionType(config, Subscription::ALERT, newAlertTrackerMap,
+                                            newAnomalyTrackers)) {
+        return false;
+    }
+    return true;
+}
+
+bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
+                        const sp<StatsPullerManager>& pullerManager,
+                        const sp<AlarmMonitor>& anomalyAlarmMonitor,
+                        const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
+                        const int64_t currentTimeNs,
+                        const vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
+                        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+                        const vector<sp<ConditionTracker>>& oldConditionTrackers,
+                        const unordered_map<int64_t, int>& oldConditionTrackerMap,
+                        const vector<sp<MetricProducer>>& oldMetricProducers,
+                        const unordered_map<int64_t, int>& oldMetricProducerMap,
+                        const vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
+                        const unordered_map<int64_t, int>& oldAlertTrackerMap,
+                        const map<int64_t, uint64_t>& oldStateProtoHashes, set<int>& allTagIds,
+                        vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
+                        unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+                        vector<sp<ConditionTracker>>& newConditionTrackers,
+                        unordered_map<int64_t, int>& newConditionTrackerMap,
+                        vector<sp<MetricProducer>>& newMetricProducers,
+                        unordered_map<int64_t, int>& newMetricProducerMap,
+                        vector<sp<AnomalyTracker>>& newAnomalyTrackers,
+                        unordered_map<int64_t, int>& newAlertTrackerMap,
+                        vector<sp<AlarmTracker>>& newPeriodicAlarmTrackers,
+                        unordered_map<int, vector<int>>& conditionToMetricMap,
+                        unordered_map<int, vector<int>>& trackerToMetricMap,
+                        unordered_map<int, vector<int>>& trackerToConditionMap,
+                        unordered_map<int, vector<int>>& activationTrackerToMetricMap,
+                        unordered_map<int, vector<int>>& deactivationTrackerToMetricMap,
+                        vector<int>& metricsWithActivation,
+                        map<int64_t, uint64_t>& newStateProtoHashes,
+                        set<int64_t>& noReportMetricIds) {
+    set<int64_t> replacedMatchers;
+    set<int64_t> replacedConditions;
+    set<int64_t> replacedStates;
+    set<int64_t> replacedMetrics;
+    vector<ConditionState> conditionCache;
+    unordered_map<int64_t, int> stateAtomIdMap;
+    unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
+
+    if (!updateAtomMatchingTrackers(config, uidMap, oldAtomMatchingTrackerMap,
+                                    oldAtomMatchingTrackers, allTagIds, newAtomMatchingTrackerMap,
+                                    newAtomMatchingTrackers, replacedMatchers)) {
+        ALOGE("updateAtomMatchingTrackers failed");
+        return false;
+    }
+
+    if (!updateConditions(key, config, newAtomMatchingTrackerMap, replacedMatchers,
+                          oldConditionTrackerMap, oldConditionTrackers, newConditionTrackerMap,
+                          newConditionTrackers, trackerToConditionMap, conditionCache,
+                          replacedConditions)) {
+        ALOGE("updateConditions failed");
+        return false;
+    }
+
+    if (!updateStates(config, oldStateProtoHashes, stateAtomIdMap, allStateGroupMaps,
+                      newStateProtoHashes, replacedStates)) {
+        ALOGE("updateStates failed");
+        return false;
+    }
+    if (!updateMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager,
+                       oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
+                       newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
+                       newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps,
+                       replacedStates, oldMetricProducerMap, oldMetricProducers,
+                       newMetricProducerMap, newMetricProducers, conditionToMetricMap,
+                       trackerToMetricMap, noReportMetricIds, activationTrackerToMetricMap,
+                       deactivationTrackerToMetricMap, metricsWithActivation, replacedMetrics)) {
+        ALOGE("updateMetrics failed");
+        return false;
+    }
+
+    if (!updateAlerts(config, newMetricProducerMap, replacedMetrics, oldAlertTrackerMap,
+                      oldAnomalyTrackers, anomalyAlarmMonitor, newMetricProducers,
+                      newAlertTrackerMap, newAnomalyTrackers)) {
+        ALOGE("updateAlerts failed");
+        return false;
+    }
+
+    // Alarms do not have any state, so we can reuse the initialization logic.
+    if (!initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs,
+                    newPeriodicAlarmTrackers)) {
+        ALOGE("initAlarms failed");
+        return false;
+    }
+    return true;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/bin/src/metrics/parsing_utils/config_update_utils.h b/bin/src/metrics/parsing_utils/config_update_utils.h
new file mode 100644
index 0000000..8e2be68
--- /dev/null
+++ b/bin/src/metrics/parsing_utils/config_update_utils.h
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <vector>
+
+#include "anomaly/AlarmMonitor.h"
+#include "anomaly/AlarmTracker.h"
+#include "condition/ConditionTracker.h"
+#include "external/StatsPullerManager.h"
+#include "matchers/AtomMatchingTracker.h"
+#include "metrics/MetricProducer.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Helper functions for MetricsManager to update itself from a new StatsdConfig.
+// *Note*: only updateStatsdConfig() should be called from outside this file.
+// All other functions are intermediate steps, created to make unit testing easier.
+
+// Possible update states for a component. PRESERVE means we should keep the existing one.
+// REPLACE means we should create a new one because the existing one changed
+// NEW means we should create a new one because one does not currently exist.
+enum UpdateStatus {
+    UPDATE_UNKNOWN = 0,
+    UPDATE_PRESERVE = 1,
+    UPDATE_REPLACE = 2,
+    UPDATE_NEW = 3,
+};
+
+// Recursive function to determine if a matcher needs to be updated.
+// input:
+// [config]: the input StatsdConfig
+// [matcherIdx]: the index of the current matcher to be updated
+// [oldAtomMatchingTrackerMap]: matcher id to index mapping in the existing MetricsManager
+// [oldAtomMatchingTrackers]: stores the existing AtomMatchingTrackers
+// [newAtomMatchingTrackerMap]: matcher id to index mapping in the input StatsdConfig
+// output:
+// [matchersToUpdate]: vector of the update status of each matcher. The matcherIdx index will
+//                     be updated from UPDATE_UNKNOWN after this call.
+// [cycleTracker]: intermediate param used during recursion.
+// Returns whether the function was successful or not.
+bool determineMatcherUpdateStatus(
+        const StatsdConfig& config, const int matcherIdx,
+        const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+        const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
+        const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+        std::vector<UpdateStatus>& matchersToUpdate, std::vector<bool>& cycleTracker);
+
+// Updates the AtomMatchingTrackers.
+// input:
+// [config]: the input StatsdConfig
+// [oldAtomMatchingTrackerMap]: existing matcher id to index mapping
+// [oldAtomMatchingTrackers]: stores the existing AtomMatchingTrackers
+// output:
+// [allTagIds]: contains the set of all interesting tag ids to this config.
+// [newAtomMatchingTrackerMap]: new matcher id to index mapping
+// [newAtomMatchingTrackers]: stores the new AtomMatchingTrackers
+// [replacedMatchers]: set of matcher ids that changed and have been replaced
+bool updateAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
+                                const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+                                const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
+                                std::set<int>& allTagIds,
+                                std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+                                std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
+                                std::set<int64_t>& replacedMatchers);
+
+// Recursive function to determine if a condition needs to be updated.
+// input:
+// [config]: the input StatsdConfig
+// [conditionIdx]: the index of the current condition to be updated
+// [oldConditionTrackerMap]: condition id to index mapping in the existing MetricsManager
+// [oldConditionTrackers]: stores the existing ConditionTrackers
+// [newConditionTrackerMap]: condition id to index mapping in the input StatsdConfig
+// [replacedMatchers]: set of replaced matcher ids. conditions using these matchers must be replaced
+// output:
+// [conditionsToUpdate]: vector of the update status of each condition. The conditionIdx index will
+//                       be updated from UPDATE_UNKNOWN after this call.
+// [cycleTracker]: intermediate param used during recursion.
+// Returns whether the function was successful or not.
+bool determineConditionUpdateStatus(const StatsdConfig& config, const int conditionIdx,
+                                    const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
+                                    const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
+                                    const std::unordered_map<int64_t, int>& newConditionTrackerMap,
+                                    const std::set<int64_t>& replacedMatchers,
+                                    std::vector<UpdateStatus>& conditionsToUpdate,
+                                    std::vector<bool>& cycleTracker);
+
+// Updates ConditionTrackers
+// input:
+// [config]: the input config
+// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step.
+// [replacedMatchers]: ids of replaced matchers. conditions depending on these must also be replaced
+// [oldConditionTrackerMap]: existing matcher id to index mapping
+// [oldConditionTrackers]: stores the existing ConditionTrackers
+// output:
+// [newConditionTrackerMap]: new condition id to index mapping
+// [newConditionTrackers]: stores the sp to all the ConditionTrackers
+// [trackerToConditionMap]: contains the mapping from the index of an atom matcher
+//                          to indices of condition trackers that use the matcher
+// [conditionCache]: stores the current conditions for each ConditionTracker
+// [replacedConditions]: set of condition ids that have changed and have been replaced
+bool updateConditions(const ConfigKey& key, const StatsdConfig& config,
+                      const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                      const std::set<int64_t>& replacedMatchers,
+                      const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
+                      const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
+                      std::unordered_map<int64_t, int>& newConditionTrackerMap,
+                      std::vector<sp<ConditionTracker>>& newConditionTrackers,
+                      std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
+                      std::vector<ConditionState>& conditionCache,
+                      std::set<int64_t>& replacedConditions);
+
+bool updateStates(const StatsdConfig& config,
+                  const std::map<int64_t, uint64_t>& oldStateProtoHashes,
+                  std::unordered_map<int64_t, int>& stateAtomIdMap,
+                  std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
+                  std::map<int64_t, uint64_t>& newStateProtoHashes,
+                  std::set<int64_t>& replacedStates);
+
+// Function to determine the update status (preserve/replace/new) of all metrics in the config.
+// [config]: the input StatsdConfig
+// [oldMetricProducerMap]: metric id to index mapping in the existing MetricsManager
+// [oldMetricProducers]: stores the existing MetricProducers
+// [metricToActivationMap]:  map from metric id to metric activation index
+// [replacedMatchers]: set of replaced matcher ids. metrics using these matchers must be replaced
+// [replacedConditions]: set of replaced conditions. metrics using these conditions must be replaced
+// [replacedStates]: set of replaced state ids. metrics using these states must be replaced
+// output:
+// [metricsToUpdate]: update status of each metric. Will be changed from UPDATE_UNKNOWN
+// Returns whether the function was successful or not.
+bool determineAllMetricUpdateStatuses(const StatsdConfig& config,
+                                      const unordered_map<int64_t, int>& oldMetricProducerMap,
+                                      const vector<sp<MetricProducer>>& oldMetricProducers,
+                                      const unordered_map<int64_t, int>& metricToActivationMap,
+                                      const set<int64_t>& replacedMatchers,
+                                      const set<int64_t>& replacedConditions,
+                                      const set<int64_t>& replacedStates,
+                                      vector<UpdateStatus>& metricsToUpdate);
+
+// Update MetricProducers.
+// input:
+// [key]: the config key that this config belongs to
+// [config]: the input config
+// [timeBaseNs]: start time base for all metrics
+// [currentTimeNs]: time of the config update
+// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step.
+// [replacedMatchers]: ids of replaced matchers. Metrics depending on these must also be replaced
+// [allAtomMatchingTrackers]: stores the sp of the atom matchers.
+// [conditionTrackerMap]: condition name to index mapping
+// [replacedConditions]: set of condition ids that have changed and have been replaced
+// [stateAtomIdMap]: contains the mapping from state ids to atom ids
+// [allStateGroupMaps]: contains the mapping from atom ids and state values to
+//                      state group ids for all states
+// output:
+// [allMetricProducers]: contains the list of sp to the MetricProducers created.
+// [conditionToMetricMap]: contains the mapping from condition tracker index to
+//                          the list of MetricProducer index
+// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
+bool updateMetrics(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+        const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+        const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+        const std::set<int64_t>& replacedMatchers,
+        const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const std::unordered_map<int64_t, int>& conditionTrackerMap,
+        const std::set<int64_t>& replacedConditions,
+        std::vector<sp<ConditionTracker>>& allConditionTrackers,
+        const std::vector<ConditionState>& initialConditionCache,
+        const std::unordered_map<int64_t, int>& stateAtomIdMap,
+        const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
+        const std::set<int64_t>& replacedStates,
+        const std::unordered_map<int64_t, int>& oldMetricProducerMap,
+        const std::vector<sp<MetricProducer>>& oldMetricProducers,
+        std::unordered_map<int64_t, int>& newMetricProducerMap,
+        std::vector<sp<MetricProducer>>& newMetricProducers,
+        std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+        std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+        std::set<int64_t>& noReportMetricIds,
+        std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+        std::vector<int>& metricsWithActivation, std::set<int64_t>& replacedMetrics);
+
+// Function to determine the update status (preserve/replace/new) of an alert.
+// [alert]: the input Alert
+// [oldAlertTrackerMap]: alert id to index mapping in the existing MetricsManager
+// [oldAnomalyTrackers]: stores the existing AnomalyTrackers
+// [replacedMetrics]: set of replaced metric ids. alerts using these metrics must be replaced
+// output:
+// [updateStatus]: update status of the alert. Will be changed from UPDATE_UNKNOWN
+// Returns whether the function was successful or not.
+bool determineAlertUpdateStatus(const Alert& alert,
+                                const std::unordered_map<int64_t, int>& oldAlertTrackerMap,
+                                const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
+                                const std::set<int64_t>& replacedMetrics,
+                                UpdateStatus& updateStatus);
+
+// Update MetricProducers.
+// input:
+// [config]: the input config
+// [metricProducerMap]: metric id to index mapping in the new config
+// [replacedMetrics]: set of metric ids that have changed and were replaced
+// [oldAlertTrackerMap]: alert id to index mapping in the existing MetricsManager.
+// [oldAnomalyTrackers]: stores the existing AnomalyTrackers
+// [anomalyAlarmMonitor]: AlarmMonitor used for duration metric anomaly detection
+// [allMetricProducers]: stores the sp of the metric producers, AnomalyTrackers need to be added.
+// [stateAtomIdMap]: contains the mapping from state ids to atom ids
+// [allStateGroupMaps]: contains the mapping from atom ids and state values to
+//                      state group ids for all states
+// output:
+// [newAlertTrackerMap]: mapping of alert id to index in the new config
+// [newAnomalyTrackers]: contains the list of sp to the AnomalyTrackers created.
+bool updateAlerts(const StatsdConfig& config,
+                  const std::unordered_map<int64_t, int>& metricProducerMap,
+                  const std::set<int64_t>& replacedMetrics,
+                  const std::unordered_map<int64_t, int>& oldAlertTrackerMap,
+                  const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
+                  const sp<AlarmMonitor>& anomalyAlarmMonitor,
+                  std::vector<sp<MetricProducer>>& allMetricProducers,
+                  std::unordered_map<int64_t, int>& newAlertTrackerMap,
+                  std::vector<sp<AnomalyTracker>>& newAnomalyTrackers);
+
+// Updates the existing MetricsManager from a new StatsdConfig.
+// Parameters are the members of MetricsManager. See MetricsManager for declaration.
+bool updateStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
+                        const sp<StatsPullerManager>& pullerManager,
+                        const sp<AlarmMonitor>& anomalyAlarmMonitor,
+                        const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
+                        const int64_t currentTimeNs,
+                        const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
+                        const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+                        const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
+                        const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
+                        const std::vector<sp<MetricProducer>>& oldMetricProducers,
+                        const std::unordered_map<int64_t, int>& oldMetricProducerMap,
+                        const std::vector<sp<AnomalyTracker>>& oldAnomalyTrackers,
+                        const std::unordered_map<int64_t, int>& oldAlertTrackerMap,
+                        const std::map<int64_t, uint64_t>& oldStateProtoHashes,
+                        std::set<int>& allTagIds,
+                        std::vector<sp<AtomMatchingTracker>>& newAtomMatchingTrackers,
+                        std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+                        std::vector<sp<ConditionTracker>>& newConditionTrackers,
+                        std::unordered_map<int64_t, int>& newConditionTrackerMap,
+                        std::vector<sp<MetricProducer>>& newMetricProducers,
+                        std::unordered_map<int64_t, int>& newMetricProducerMap,
+                        std::vector<sp<AnomalyTracker>>& newAlertTrackers,
+                        std::unordered_map<int64_t, int>& newAlertTrackerMap,
+                        std::vector<sp<AlarmTracker>>& newPeriodicAlarmTrackers,
+                        std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+                        std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+                        std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
+                        std::unordered_map<int, std::vector<int>>& activationTrackerToMetricMap,
+                        std::unordered_map<int, std::vector<int>>& deactivationTrackerToMetricMap,
+                        std::vector<int>& metricsWithActivation,
+                        std::map<int64_t, uint64_t>& newStateProtoHashes,
+                        std::set<int64_t>& noReportMetricIds);
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/bin/src/metrics/parsing_utils/metrics_manager_util.cpp b/bin/src/metrics/parsing_utils/metrics_manager_util.cpp
new file mode 100644
index 0000000..bfa409c
--- /dev/null
+++ b/bin/src/metrics/parsing_utils/metrics_manager_util.cpp
@@ -0,0 +1,1228 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define DEBUG false  // STOPSHIP if true
+#include "Log.h"
+
+#include "metrics_manager_util.h"
+
+#include <inttypes.h>
+
+#include "FieldValue.h"
+#include "condition/CombinationConditionTracker.h"
+#include "condition/SimpleConditionTracker.h"
+#include "external/StatsPullerManager.h"
+#include "hash.h"
+#include "matchers/CombinationAtomMatchingTracker.h"
+#include "matchers/EventMatcherWizard.h"
+#include "matchers/SimpleAtomMatchingTracker.h"
+#include "metrics/CountMetricProducer.h"
+#include "metrics/DurationMetricProducer.h"
+#include "metrics/EventMetricProducer.h"
+#include "metrics/GaugeMetricProducer.h"
+#include "metrics/MetricProducer.h"
+#include "metrics/ValueMetricProducer.h"
+#include "state/StateManager.h"
+#include "stats_util.h"
+
+using google::protobuf::MessageLite;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+namespace {
+
+bool hasLeafNode(const FieldMatcher& matcher) {
+    if (!matcher.has_field()) {
+        return false;
+    }
+    for (int i = 0; i < matcher.child_size(); ++i) {
+        if (hasLeafNode(matcher.child(i))) {
+            return true;
+        }
+    }
+    return true;
+}
+
+}  // namespace
+
+sp<AtomMatchingTracker> createAtomMatchingTracker(const AtomMatcher& logMatcher, const int index,
+                                                  const sp<UidMap>& uidMap) {
+    string serializedMatcher;
+    if (!logMatcher.SerializeToString(&serializedMatcher)) {
+        ALOGE("Unable to serialize matcher %lld", (long long)logMatcher.id());
+        return nullptr;
+    }
+    uint64_t protoHash = Hash64(serializedMatcher);
+    switch (logMatcher.contents_case()) {
+        case AtomMatcher::ContentsCase::kSimpleAtomMatcher:
+            return new SimpleAtomMatchingTracker(logMatcher.id(), index, protoHash,
+                                                 logMatcher.simple_atom_matcher(), uidMap);
+        case AtomMatcher::ContentsCase::kCombination:
+            return new CombinationAtomMatchingTracker(logMatcher.id(), index, protoHash);
+        default:
+            ALOGE("Matcher \"%lld\" malformed", (long long)logMatcher.id());
+            return nullptr;
+    }
+}
+
+sp<ConditionTracker> createConditionTracker(
+        const ConfigKey& key, const Predicate& predicate, const int index,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
+    string serializedPredicate;
+    if (!predicate.SerializeToString(&serializedPredicate)) {
+        ALOGE("Unable to serialize predicate %lld", (long long)predicate.id());
+        return nullptr;
+    }
+    uint64_t protoHash = Hash64(serializedPredicate);
+    switch (predicate.contents_case()) {
+        case Predicate::ContentsCase::kSimplePredicate: {
+            return new SimpleConditionTracker(key, predicate.id(), protoHash, index,
+                                              predicate.simple_predicate(), atomMatchingTrackerMap);
+        }
+        case Predicate::ContentsCase::kCombination: {
+            return new CombinationConditionTracker(predicate.id(), index, protoHash);
+        }
+        default:
+            ALOGE("Predicate \"%lld\" malformed", (long long)predicate.id());
+            return nullptr;
+    }
+}
+
+bool getMetricProtoHash(const StatsdConfig& config, const MessageLite& metric, const int64_t id,
+                        const unordered_map<int64_t, int>& metricToActivationMap,
+                        uint64_t& metricHash) {
+    string serializedMetric;
+    if (!metric.SerializeToString(&serializedMetric)) {
+        ALOGE("Unable to serialize metric %lld", (long long)id);
+        return false;
+    }
+    metricHash = Hash64(serializedMetric);
+
+    // Combine with activation hash, if applicable
+    const auto& metricActivationIt = metricToActivationMap.find(id);
+    if (metricActivationIt != metricToActivationMap.end()) {
+        string serializedActivation;
+        const MetricActivation& activation = config.metric_activation(metricActivationIt->second);
+        if (!activation.SerializeToString(&serializedActivation)) {
+            ALOGE("Unable to serialize metric activation for metric %lld", (long long)id);
+            return false;
+        }
+        metricHash = Hash64(to_string(metricHash).append(to_string(Hash64(serializedActivation))));
+    }
+    return true;
+}
+
+bool handleMetricWithAtomMatchingTrackers(
+        const int64_t matcherId, const int metricIndex, const bool enforceOneAtom,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap, int& logTrackerIndex) {
+    auto logTrackerIt = atomMatchingTrackerMap.find(matcherId);
+    if (logTrackerIt == atomMatchingTrackerMap.end()) {
+        ALOGW("cannot find the AtomMatcher \"%lld\" in config", (long long)matcherId);
+        return false;
+    }
+    if (enforceOneAtom && allAtomMatchingTrackers[logTrackerIt->second]->getAtomIds().size() > 1) {
+        ALOGE("AtomMatcher \"%lld\" has more than one tag ids. When a metric has dimension, "
+              "the \"what\" can only be about one atom type. trigger_event matchers can also only "
+              "be about one atom type.",
+              (long long)matcherId);
+        return false;
+    }
+    logTrackerIndex = logTrackerIt->second;
+    auto& metric_list = trackerToMetricMap[logTrackerIndex];
+    metric_list.push_back(metricIndex);
+    return true;
+}
+
+bool handleMetricWithConditions(
+        const int64_t condition, const int metricIndex,
+        const unordered_map<int64_t, int>& conditionTrackerMap,
+        const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>&
+                links,
+        const vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex,
+        unordered_map<int, vector<int>>& conditionToMetricMap) {
+    auto condition_it = conditionTrackerMap.find(condition);
+    if (condition_it == conditionTrackerMap.end()) {
+        ALOGW("cannot find Predicate \"%lld\" in the config", (long long)condition);
+        return false;
+    }
+
+    for (const auto& link : links) {
+        auto it = conditionTrackerMap.find(link.condition());
+        if (it == conditionTrackerMap.end()) {
+            ALOGW("cannot find Predicate \"%lld\" in the config", (long long)link.condition());
+            return false;
+        }
+    }
+    conditionIndex = condition_it->second;
+
+    // will create new vector if not exist before.
+    auto& metricList = conditionToMetricMap[condition_it->second];
+    metricList.push_back(metricIndex);
+    return true;
+}
+
+// Initializes state data structures for a metric.
+// input:
+// [config]: the input config
+// [stateIds]: the slice_by_state ids for this metric
+// [stateAtomIdMap]: this map contains the mapping from all state ids to atom ids
+// [allStateGroupMaps]: this map contains the mapping from state ids and state
+//                      values to state group ids for all states
+// output:
+// [slicedStateAtoms]: a vector of atom ids of all the slice_by_states
+// [stateGroupMap]: this map should contain the mapping from states ids and state
+//                      values to state group ids for all states that this metric
+//                      is interested in
+bool handleMetricWithStates(
+        const StatsdConfig& config, const ::google::protobuf::RepeatedField<int64_t>& stateIds,
+        const unordered_map<int64_t, int>& stateAtomIdMap,
+        const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+        vector<int>& slicedStateAtoms,
+        unordered_map<int, unordered_map<int, int64_t>>& stateGroupMap) {
+    for (const auto& stateId : stateIds) {
+        auto it = stateAtomIdMap.find(stateId);
+        if (it == stateAtomIdMap.end()) {
+            ALOGW("cannot find State %" PRId64 " in the config", stateId);
+            return false;
+        }
+        int atomId = it->second;
+        slicedStateAtoms.push_back(atomId);
+
+        auto stateIt = allStateGroupMaps.find(stateId);
+        if (stateIt != allStateGroupMaps.end()) {
+            stateGroupMap[atomId] = stateIt->second;
+        }
+    }
+    return true;
+}
+
+bool handleMetricWithStateLink(const FieldMatcher& stateMatcher,
+                               const vector<Matcher>& dimensionsInWhat) {
+    vector<Matcher> stateMatchers;
+    translateFieldMatcher(stateMatcher, &stateMatchers);
+
+    return subsetDimensions(stateMatchers, dimensionsInWhat);
+}
+
+// Validates a metricActivation and populates state.
+// EventActivationMap and EventDeactivationMap are supplied to a MetricProducer
+//      to provide the producer with state about its activators and deactivators.
+// Returns false if there are errors.
+bool handleMetricActivation(
+        const StatsdConfig& config, const int64_t metricId, const int metricIndex,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation,
+        unordered_map<int, shared_ptr<Activation>>& eventActivationMap,
+        unordered_map<int, vector<shared_ptr<Activation>>>& eventDeactivationMap) {
+    // Check if metric has an associated activation
+    auto itr = metricToActivationMap.find(metricId);
+    if (itr == metricToActivationMap.end()) {
+        return true;
+    }
+
+    int activationIndex = itr->second;
+    const MetricActivation& metricActivation = config.metric_activation(activationIndex);
+
+    for (int i = 0; i < metricActivation.event_activation_size(); i++) {
+        const EventActivation& activation = metricActivation.event_activation(i);
+
+        auto itr = atomMatchingTrackerMap.find(activation.atom_matcher_id());
+        if (itr == atomMatchingTrackerMap.end()) {
+            ALOGE("Atom matcher not found for event activation.");
+            return false;
+        }
+
+        ActivationType activationType = (activation.has_activation_type())
+                                                ? activation.activation_type()
+                                                : metricActivation.activation_type();
+        std::shared_ptr<Activation> activationWrapper =
+                std::make_shared<Activation>(activationType, activation.ttl_seconds() * NS_PER_SEC);
+
+        int atomMatcherIndex = itr->second;
+        activationAtomTrackerToMetricMap[atomMatcherIndex].push_back(metricIndex);
+        eventActivationMap.emplace(atomMatcherIndex, activationWrapper);
+
+        if (activation.has_deactivation_atom_matcher_id()) {
+            itr = atomMatchingTrackerMap.find(activation.deactivation_atom_matcher_id());
+            if (itr == atomMatchingTrackerMap.end()) {
+                ALOGE("Atom matcher not found for event deactivation.");
+                return false;
+            }
+            int deactivationAtomMatcherIndex = itr->second;
+            deactivationAtomTrackerToMetricMap[deactivationAtomMatcherIndex].push_back(metricIndex);
+            eventDeactivationMap[deactivationAtomMatcherIndex].push_back(activationWrapper);
+        }
+    }
+
+    metricsWithActivation.push_back(metricIndex);
+    return true;
+}
+
+// Validates a metricActivation and populates state.
+// Fills the new event activation/deactivation maps, preserving the existing activations
+// Returns false if there are errors.
+bool handleMetricActivationOnConfigUpdate(
+        const StatsdConfig& config, const int64_t metricId, const int metricIndex,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        const unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+        const unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+        const unordered_map<int, shared_ptr<Activation>>& oldEventActivationMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation,
+        unordered_map<int, shared_ptr<Activation>>& newEventActivationMap,
+        unordered_map<int, vector<shared_ptr<Activation>>>& newEventDeactivationMap) {
+    // Check if metric has an associated activation.
+    const auto& itr = metricToActivationMap.find(metricId);
+    if (itr == metricToActivationMap.end()) {
+        return true;
+    }
+
+    int activationIndex = itr->second;
+    const MetricActivation& metricActivation = config.metric_activation(activationIndex);
+
+    for (int i = 0; i < metricActivation.event_activation_size(); i++) {
+        const int64_t activationMatcherId = metricActivation.event_activation(i).atom_matcher_id();
+
+        const auto& newActivationIt = newAtomMatchingTrackerMap.find(activationMatcherId);
+        if (newActivationIt == newAtomMatchingTrackerMap.end()) {
+            ALOGE("Atom matcher not found in new config for event activation.");
+            return false;
+        }
+        int newActivationMatcherIndex = newActivationIt->second;
+
+        // Find the old activation struct and copy it over.
+        const auto& oldActivationIt = oldAtomMatchingTrackerMap.find(activationMatcherId);
+        if (oldActivationIt == oldAtomMatchingTrackerMap.end()) {
+            ALOGE("Atom matcher not found in existing config for event activation.");
+            return false;
+        }
+        int oldActivationMatcherIndex = oldActivationIt->second;
+        const auto& oldEventActivationIt = oldEventActivationMap.find(oldActivationMatcherIndex);
+        if (oldEventActivationIt == oldEventActivationMap.end()) {
+            ALOGE("Could not find existing event activation to update");
+            return false;
+        }
+        newEventActivationMap.emplace(newActivationMatcherIndex, oldEventActivationIt->second);
+        activationAtomTrackerToMetricMap[newActivationMatcherIndex].push_back(metricIndex);
+
+        if (metricActivation.event_activation(i).has_deactivation_atom_matcher_id()) {
+            const int64_t deactivationMatcherId =
+                    metricActivation.event_activation(i).deactivation_atom_matcher_id();
+            const auto& newDeactivationIt = newAtomMatchingTrackerMap.find(deactivationMatcherId);
+            if (newDeactivationIt == newAtomMatchingTrackerMap.end()) {
+                ALOGE("Deactivation atom matcher not found in new config for event activation.");
+                return false;
+            }
+            int newDeactivationMatcherIndex = newDeactivationIt->second;
+            newEventDeactivationMap[newDeactivationMatcherIndex].push_back(
+                    oldEventActivationIt->second);
+            deactivationAtomTrackerToMetricMap[newDeactivationMatcherIndex].push_back(metricIndex);
+        }
+    }
+
+    metricsWithActivation.push_back(metricIndex);
+    return true;
+}
+
+optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const int64_t currentTimeNs, const CountMetric& metric, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap,
+        const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+        const unordered_map<int64_t, int>& stateAtomIdMap,
+        const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    if (!metric.has_id() || !metric.has_what()) {
+        ALOGE("cannot find metric id or \"what\" in CountMetric \"%lld\"", (long long)metric.id());
+        return nullopt;
+    }
+    int trackerIndex;
+    if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
+                                              metric.has_dimensions_in_what(),
+                                              allAtomMatchingTrackers, atomMatchingTrackerMap,
+                                              trackerToMetricMap, trackerIndex)) {
+        return nullopt;
+    }
+
+    int conditionIndex = -1;
+    if (metric.has_condition()) {
+        if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                        metric.links(), allConditionTrackers, conditionIndex,
+                                        conditionToMetricMap)) {
+            return nullopt;
+        }
+    } else {
+        if (metric.links_size() > 0) {
+            ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
+            return nullopt;
+        }
+    }
+
+    std::vector<int> slicedStateAtoms;
+    unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+    if (metric.slice_by_state_size() > 0) {
+        if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+                                    allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+            return nullopt;
+        }
+    } else {
+        if (metric.state_link_size() > 0) {
+            ALOGW("CountMetric has a MetricStateLink but doesn't have a slice_by_state");
+            return nullopt;
+        }
+    }
+
+    unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+    unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+    if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
+                                atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
+                                deactivationAtomTrackerToMetricMap, metricsWithActivation,
+                                eventActivationMap, eventDeactivationMap)) {
+        return nullopt;
+    }
+
+    uint64_t metricHash;
+    if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+        return nullopt;
+    }
+
+    return {new CountMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+                                    metricHash, timeBaseNs, currentTimeNs, eventActivationMap,
+                                    eventDeactivationMap, slicedStateAtoms, stateGroupMap)};
+}
+
+optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const int64_t currentTimeNs, const DurationMetric& metric, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap,
+        const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+        const unordered_map<int64_t, int>& stateAtomIdMap,
+        const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    if (!metric.has_id() || !metric.has_what()) {
+        ALOGE("cannot find metric id or \"what\" in DurationMetric \"%lld\"",
+              (long long)metric.id());
+        return nullopt;
+    }
+    const auto& what_it = conditionTrackerMap.find(metric.what());
+    if (what_it == conditionTrackerMap.end()) {
+        ALOGE("DurationMetric's \"what\" is not present in the condition trackers");
+        return nullopt;
+    }
+
+    const int whatIndex = what_it->second;
+    const Predicate& durationWhat = config.predicate(whatIndex);
+    if (durationWhat.contents_case() != Predicate::ContentsCase::kSimplePredicate) {
+        ALOGE("DurationMetric's \"what\" must be a simple condition");
+        return nullopt;
+    }
+
+    const SimplePredicate& simplePredicate = durationWhat.simple_predicate();
+    bool nesting = simplePredicate.count_nesting();
+
+    int startIndex = -1, stopIndex = -1, stopAllIndex = -1;
+    if (!simplePredicate.has_start() ||
+        !handleMetricWithAtomMatchingTrackers(
+                simplePredicate.start(), metricIndex, metric.has_dimensions_in_what(),
+                allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap, startIndex)) {
+        ALOGE("Duration metrics must specify a valid start event matcher");
+        return nullopt;
+    }
+
+    if (simplePredicate.has_stop() &&
+        !handleMetricWithAtomMatchingTrackers(
+                simplePredicate.stop(), metricIndex, metric.has_dimensions_in_what(),
+                allAtomMatchingTrackers, atomMatchingTrackerMap, trackerToMetricMap, stopIndex)) {
+        return nullopt;
+    }
+
+    if (simplePredicate.has_stop_all() &&
+        !handleMetricWithAtomMatchingTrackers(simplePredicate.stop_all(), metricIndex,
+                                              metric.has_dimensions_in_what(),
+                                              allAtomMatchingTrackers, atomMatchingTrackerMap,
+                                              trackerToMetricMap, stopAllIndex)) {
+        return nullopt;
+    }
+
+    FieldMatcher internalDimensions = simplePredicate.dimensions();
+
+    int conditionIndex = -1;
+    if (metric.has_condition()) {
+        if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                        metric.links(), allConditionTrackers, conditionIndex,
+                                        conditionToMetricMap)) {
+            return nullopt;
+        }
+    } else if (metric.links_size() > 0) {
+        ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
+        return nullopt;
+    }
+
+    std::vector<int> slicedStateAtoms;
+    unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+    if (metric.slice_by_state_size() > 0) {
+        if (metric.aggregation_type() == DurationMetric::MAX_SPARSE) {
+            ALOGE("DurationMetric with aggregation type MAX_SPARSE cannot be sliced by state");
+            return nullopt;
+        }
+        if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+                                    allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+            return nullopt;
+        }
+    } else if (metric.state_link_size() > 0) {
+        ALOGW("DurationMetric has a MetricStateLink but doesn't have a sliced state");
+        return nullopt;
+    }
+
+    // Check that all metric state links are a subset of dimensions_in_what fields.
+    std::vector<Matcher> dimensionsInWhat;
+    translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
+    for (const auto& stateLink : metric.state_link()) {
+        if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
+            ALOGW("DurationMetric's MetricStateLinks must be a subset of dimensions in what");
+            return nullopt;
+        }
+    }
+
+    unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+    unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+    if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
+                                atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
+                                deactivationAtomTrackerToMetricMap, metricsWithActivation,
+                                eventActivationMap, eventDeactivationMap)) {
+        return nullopt;
+    }
+
+    uint64_t metricHash;
+    if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+        return nullopt;
+    }
+
+    sp<MetricProducer> producer = new DurationMetricProducer(
+            key, metric, conditionIndex, initialConditionCache, whatIndex, startIndex, stopIndex,
+            stopAllIndex, nesting, wizard, metricHash, internalDimensions, timeBaseNs,
+            currentTimeNs, eventActivationMap, eventDeactivationMap, slicedStateAtoms,
+            stateGroupMap);
+    if (!producer->isValid()) {
+        return nullopt;
+    }
+    return {producer};
+}
+
+optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const EventMetric& metric, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap,
+        const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    if (!metric.has_id() || !metric.has_what()) {
+        ALOGE("cannot find the metric name or what in config");
+        return nullopt;
+    }
+    int trackerIndex;
+    if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex, false,
+                                              allAtomMatchingTrackers, atomMatchingTrackerMap,
+                                              trackerToMetricMap, trackerIndex)) {
+        return nullopt;
+    }
+
+    int conditionIndex = -1;
+    if (metric.has_condition()) {
+        if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                        metric.links(), allConditionTrackers, conditionIndex,
+                                        conditionToMetricMap)) {
+            return nullopt;
+        }
+    } else {
+        if (metric.links_size() > 0) {
+            ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
+            return nullopt;
+        }
+    }
+
+    unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+    unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+    bool success = handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
+                                          atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
+                                          deactivationAtomTrackerToMetricMap, metricsWithActivation,
+                                          eventActivationMap, eventDeactivationMap);
+    if (!success) return nullptr;
+
+    uint64_t metricHash;
+    if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+        return nullopt;
+    }
+
+    return {new EventMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+                                    metricHash, timeBaseNs, eventActivationMap,
+                                    eventDeactivationMap)};
+}
+
+optional<sp<MetricProducer>> createValueMetricProducerAndUpdateMetadata(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+        const ValueMetric& metric, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap,
+        const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const unordered_map<int64_t, int>& stateAtomIdMap,
+        const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    if (!metric.has_id() || !metric.has_what()) {
+        ALOGE("cannot find metric id or \"what\" in ValueMetric \"%lld\"", (long long)metric.id());
+        return nullopt;
+    }
+    if (!metric.has_value_field()) {
+        ALOGE("cannot find \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
+        return nullopt;
+    }
+    std::vector<Matcher> fieldMatchers;
+    translateFieldMatcher(metric.value_field(), &fieldMatchers);
+    if (fieldMatchers.size() < 1) {
+        ALOGE("incorrect \"value_field\" in ValueMetric \"%lld\"", (long long)metric.id());
+        return nullopt;
+    }
+
+    int trackerIndex;
+    if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
+                                              metric.has_dimensions_in_what(),
+                                              allAtomMatchingTrackers, atomMatchingTrackerMap,
+                                              trackerToMetricMap, trackerIndex)) {
+        return nullopt;
+    }
+
+    sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
+    // If it is pulled atom, it should be simple matcher with one tagId.
+    if (atomMatcher->getAtomIds().size() != 1) {
+        return nullopt;
+    }
+    int atomTagId = *(atomMatcher->getAtomIds().begin());
+    int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
+
+    int conditionIndex = -1;
+    if (metric.has_condition()) {
+        if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                        metric.links(), allConditionTrackers, conditionIndex,
+                                        conditionToMetricMap)) {
+            return nullopt;
+        }
+    } else if (metric.links_size() > 0) {
+        ALOGE("metrics has a MetricConditionLink but doesn't have a condition");
+        return nullopt;
+    }
+
+    std::vector<int> slicedStateAtoms;
+    unordered_map<int, unordered_map<int, int64_t>> stateGroupMap;
+    if (metric.slice_by_state_size() > 0) {
+        if (!handleMetricWithStates(config, metric.slice_by_state(), stateAtomIdMap,
+                                    allStateGroupMaps, slicedStateAtoms, stateGroupMap)) {
+            return nullopt;
+        }
+    } else if (metric.state_link_size() > 0) {
+        ALOGE("ValueMetric has a MetricStateLink but doesn't have a sliced state");
+        return nullopt;
+    }
+
+    // Check that all metric state links are a subset of dimensions_in_what fields.
+    std::vector<Matcher> dimensionsInWhat;
+    translateFieldMatcher(metric.dimensions_in_what(), &dimensionsInWhat);
+    for (const auto& stateLink : metric.state_link()) {
+        if (!handleMetricWithStateLink(stateLink.fields_in_what(), dimensionsInWhat)) {
+            ALOGW("ValueMetric's MetricStateLinks must be a subset of the dimensions in what");
+            return nullopt;
+        }
+    }
+
+    unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+    unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+    if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
+                                          atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
+                                          deactivationAtomTrackerToMetricMap, metricsWithActivation,
+                                          eventActivationMap, eventDeactivationMap)) {
+        return nullopt;
+    }
+
+    uint64_t metricHash;
+    if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+        return nullopt;
+    }
+
+    return {new ValueMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+                                    metricHash, trackerIndex, matcherWizard, pullTagId, timeBaseNs,
+                                    currentTimeNs, pullerManager, eventActivationMap,
+                                    eventDeactivationMap, slicedStateAtoms, stateGroupMap)};
+}
+
+optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+        const GaugeMetric& metric, const int metricIndex,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        vector<sp<ConditionTracker>>& allConditionTrackers,
+        const unordered_map<int64_t, int>& conditionTrackerMap,
+        const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const unordered_map<int64_t, int>& metricToActivationMap,
+        unordered_map<int, vector<int>>& trackerToMetricMap,
+        unordered_map<int, vector<int>>& conditionToMetricMap,
+        unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+        unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+        vector<int>& metricsWithActivation) {
+    if (!metric.has_id() || !metric.has_what()) {
+        ALOGE("cannot find metric id or \"what\" in GaugeMetric \"%lld\"", (long long)metric.id());
+        return nullopt;
+    }
+
+    if ((!metric.gauge_fields_filter().has_include_all() ||
+         (metric.gauge_fields_filter().include_all() == false)) &&
+        !hasLeafNode(metric.gauge_fields_filter().fields())) {
+        ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
+        return nullopt;
+    }
+    if ((metric.gauge_fields_filter().has_include_all() &&
+         metric.gauge_fields_filter().include_all() == true) &&
+        hasLeafNode(metric.gauge_fields_filter().fields())) {
+        ALOGW("Incorrect field filter setting in GaugeMetric %lld", (long long)metric.id());
+        return nullopt;
+    }
+
+    int trackerIndex;
+    if (!handleMetricWithAtomMatchingTrackers(metric.what(), metricIndex,
+                                              metric.has_dimensions_in_what(),
+                                              allAtomMatchingTrackers, atomMatchingTrackerMap,
+                                              trackerToMetricMap, trackerIndex)) {
+        return nullopt;
+    }
+
+    sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
+    // For GaugeMetric atom, it should be simple matcher with one tagId.
+    if (atomMatcher->getAtomIds().size() != 1) {
+        return nullopt;
+    }
+    int atomTagId = *(atomMatcher->getAtomIds().begin());
+    int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
+
+    int triggerTrackerIndex;
+    int triggerAtomId = -1;
+    if (metric.has_trigger_event()) {
+        if (pullTagId == -1) {
+            ALOGW("Pull atom not specified for trigger");
+            return nullopt;
+        }
+        // trigger_event should be used with FIRST_N_SAMPLES
+        if (metric.sampling_type() != GaugeMetric::FIRST_N_SAMPLES) {
+            ALOGW("Gauge Metric with trigger event must have sampling type FIRST_N_SAMPLES");
+            return nullopt;
+        }
+        if (!handleMetricWithAtomMatchingTrackers(metric.trigger_event(), metricIndex,
+                                                  /*enforceOneAtom=*/true, allAtomMatchingTrackers,
+                                                  atomMatchingTrackerMap, trackerToMetricMap,
+                                                  triggerTrackerIndex)) {
+            return nullopt;
+        }
+        sp<AtomMatchingTracker> triggerAtomMatcher =
+                allAtomMatchingTrackers.at(triggerTrackerIndex);
+        triggerAtomId = *(triggerAtomMatcher->getAtomIds().begin());
+    }
+
+    if (!metric.has_trigger_event() && pullTagId != -1 &&
+        metric.sampling_type() == GaugeMetric::FIRST_N_SAMPLES) {
+        ALOGW("FIRST_N_SAMPLES is only for pushed event or pull_on_trigger");
+        return nullopt;
+    }
+
+    int conditionIndex = -1;
+    if (metric.has_condition()) {
+        if (!handleMetricWithConditions(metric.condition(), metricIndex, conditionTrackerMap,
+                                        metric.links(), allConditionTrackers, conditionIndex,
+                                        conditionToMetricMap)) {
+            return nullopt;
+        }
+    } else {
+        if (metric.links_size() > 0) {
+            ALOGW("metrics has a MetricConditionLink but doesn't have a condition");
+            return nullopt;
+        }
+    }
+
+    unordered_map<int, shared_ptr<Activation>> eventActivationMap;
+    unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
+    if (!handleMetricActivation(config, metric.id(), metricIndex, metricToActivationMap,
+                                atomMatchingTrackerMap, activationAtomTrackerToMetricMap,
+                                deactivationAtomTrackerToMetricMap, metricsWithActivation,
+                                eventActivationMap, eventDeactivationMap)) {
+        return nullopt;
+    }
+
+    uint64_t metricHash;
+    if (!getMetricProtoHash(config, metric, metric.id(), metricToActivationMap, metricHash)) {
+        return nullopt;
+    }
+
+    return {new GaugeMetricProducer(key, metric, conditionIndex, initialConditionCache, wizard,
+                                    metricHash, trackerIndex, matcherWizard, pullTagId,
+                                    triggerAtomId, atomTagId, timeBaseNs, currentTimeNs,
+                                    pullerManager, eventActivationMap, eventDeactivationMap)};
+}
+
+optional<sp<AnomalyTracker>> createAnomalyTracker(
+        const Alert& alert, const sp<AlarmMonitor>& anomalyAlarmMonitor,
+        const unordered_map<int64_t, int>& metricProducerMap,
+        vector<sp<MetricProducer>>& allMetricProducers) {
+    const auto& itr = metricProducerMap.find(alert.metric_id());
+    if (itr == metricProducerMap.end()) {
+        ALOGW("alert \"%lld\" has unknown metric id: \"%lld\"", (long long)alert.id(),
+              (long long)alert.metric_id());
+        return nullopt;
+    }
+    if (!alert.has_trigger_if_sum_gt()) {
+        ALOGW("invalid alert: missing threshold");
+        return nullopt;
+    }
+    if (alert.trigger_if_sum_gt() < 0 || alert.num_buckets() <= 0) {
+        ALOGW("invalid alert: threshold=%f num_buckets= %d", alert.trigger_if_sum_gt(),
+              alert.num_buckets());
+        return nullopt;
+    }
+    const int metricIndex = itr->second;
+    sp<MetricProducer> metric = allMetricProducers[metricIndex];
+    sp<AnomalyTracker> anomalyTracker = metric->addAnomalyTracker(alert, anomalyAlarmMonitor);
+    if (anomalyTracker == nullptr) {
+        // The ALOGW for this invalid alert was already displayed in addAnomalyTracker().
+        return nullopt;
+    }
+    return {anomalyTracker};
+}
+
+bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
+                              unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                              vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+                              set<int>& allTagIds) {
+    vector<AtomMatcher> matcherConfigs;
+    const int atomMatcherCount = config.atom_matcher_size();
+    matcherConfigs.reserve(atomMatcherCount);
+    allAtomMatchingTrackers.reserve(atomMatcherCount);
+
+    for (int i = 0; i < atomMatcherCount; i++) {
+        const AtomMatcher& logMatcher = config.atom_matcher(i);
+        sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(logMatcher, i, uidMap);
+        if (tracker == nullptr) {
+            return false;
+        }
+        allAtomMatchingTrackers.push_back(tracker);
+        if (atomMatchingTrackerMap.find(logMatcher.id()) != atomMatchingTrackerMap.end()) {
+            ALOGE("Duplicate AtomMatcher found!");
+            return false;
+        }
+        atomMatchingTrackerMap[logMatcher.id()] = i;
+        matcherConfigs.push_back(logMatcher);
+    }
+
+    vector<bool> stackTracker2(allAtomMatchingTrackers.size(), false);
+    for (auto& matcher : allAtomMatchingTrackers) {
+        if (!matcher->init(matcherConfigs, allAtomMatchingTrackers, atomMatchingTrackerMap,
+                           stackTracker2)) {
+            return false;
+        }
+        // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only.
+        const set<int>& tagIds = matcher->getAtomIds();
+        allTagIds.insert(tagIds.begin(), tagIds.end());
+    }
+    return true;
+}
+
+bool initConditions(const ConfigKey& key, const StatsdConfig& config,
+                    const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                    unordered_map<int64_t, int>& conditionTrackerMap,
+                    vector<sp<ConditionTracker>>& allConditionTrackers,
+                    unordered_map<int, std::vector<int>>& trackerToConditionMap,
+                    vector<ConditionState>& initialConditionCache) {
+    vector<Predicate> conditionConfigs;
+    const int conditionTrackerCount = config.predicate_size();
+    conditionConfigs.reserve(conditionTrackerCount);
+    allConditionTrackers.reserve(conditionTrackerCount);
+    initialConditionCache.assign(conditionTrackerCount, ConditionState::kNotEvaluated);
+
+    for (int i = 0; i < conditionTrackerCount; i++) {
+        const Predicate& condition = config.predicate(i);
+        sp<ConditionTracker> tracker =
+                createConditionTracker(key, condition, i, atomMatchingTrackerMap);
+        if (tracker == nullptr) {
+            return false;
+        }
+        allConditionTrackers.push_back(tracker);
+        if (conditionTrackerMap.find(condition.id()) != conditionTrackerMap.end()) {
+            ALOGE("Duplicate Predicate found!");
+            return false;
+        }
+        conditionTrackerMap[condition.id()] = i;
+        conditionConfigs.push_back(condition);
+    }
+
+    vector<bool> stackTracker(allConditionTrackers.size(), false);
+    for (size_t i = 0; i < allConditionTrackers.size(); i++) {
+        auto& conditionTracker = allConditionTrackers[i];
+        if (!conditionTracker->init(conditionConfigs, allConditionTrackers, conditionTrackerMap,
+                                    stackTracker, initialConditionCache)) {
+            return false;
+        }
+        for (const int trackerIndex : conditionTracker->getAtomMatchingTrackerIndex()) {
+            auto& conditionList = trackerToConditionMap[trackerIndex];
+            conditionList.push_back(i);
+        }
+    }
+    return true;
+}
+
+bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
+                unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+                map<int64_t, uint64_t>& stateProtoHashes) {
+    for (int i = 0; i < config.state_size(); i++) {
+        const State& state = config.state(i);
+        const int64_t stateId = state.id();
+        stateAtomIdMap[stateId] = state.atom_id();
+
+        string serializedState;
+        if (!state.SerializeToString(&serializedState)) {
+            ALOGE("Unable to serialize state %lld", (long long)stateId);
+            return false;
+        }
+        stateProtoHashes[stateId] = Hash64(serializedState);
+
+        const StateMap& stateMap = state.map();
+        for (auto group : stateMap.group()) {
+            for (auto value : group.value()) {
+                allStateGroupMaps[stateId][value] = group.group_id();
+            }
+        }
+    }
+
+    return true;
+}
+
+bool initMetrics(const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
+                 const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+                 const unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                 const unordered_map<int64_t, int>& conditionTrackerMap,
+                 const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+                 const unordered_map<int64_t, int>& stateAtomIdMap,
+                 const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+                 vector<sp<ConditionTracker>>& allConditionTrackers,
+                 const vector<ConditionState>& initialConditionCache,
+                 vector<sp<MetricProducer>>& allMetricProducers,
+                 unordered_map<int, vector<int>>& conditionToMetricMap,
+                 unordered_map<int, vector<int>>& trackerToMetricMap,
+                 unordered_map<int64_t, int>& metricMap, std::set<int64_t>& noReportMetricIds,
+                 unordered_map<int, vector<int>>& activationAtomTrackerToMetricMap,
+                 unordered_map<int, vector<int>>& deactivationAtomTrackerToMetricMap,
+                 vector<int>& metricsWithActivation) {
+    sp<ConditionWizard> wizard = new ConditionWizard(allConditionTrackers);
+    sp<EventMatcherWizard> matcherWizard = new EventMatcherWizard(allAtomMatchingTrackers);
+    const int allMetricsCount = config.count_metric_size() + config.duration_metric_size() +
+                                config.event_metric_size() + config.gauge_metric_size() +
+                                config.value_metric_size();
+    allMetricProducers.reserve(allMetricsCount);
+
+    // Construct map from metric id to metric activation index. The map will be used to determine
+    // the metric activation corresponding to a metric.
+    unordered_map<int64_t, int> metricToActivationMap;
+    for (int i = 0; i < config.metric_activation_size(); i++) {
+        const MetricActivation& metricActivation = config.metric_activation(i);
+        int64_t metricId = metricActivation.metric_id();
+        if (metricToActivationMap.find(metricId) != metricToActivationMap.end()) {
+            ALOGE("Metric %lld has multiple MetricActivations", (long long)metricId);
+            return false;
+        }
+        metricToActivationMap.insert({metricId, i});
+    }
+
+    // Build MetricProducers for each metric defined in config.
+    // build CountMetricProducer
+    for (int i = 0; i < config.count_metric_size(); i++) {
+        int metricIndex = allMetricProducers.size();
+        const CountMetric& metric = config.count_metric(i);
+        metricMap.insert({metric.id(), metricIndex});
+        optional<sp<MetricProducer>> producer = createCountMetricProducerAndUpdateMetadata(
+                key, config, timeBaseTimeNs, currentTimeNs, metric, metricIndex,
+                allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
+                conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
+                allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
+                activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+                metricsWithActivation);
+        if (!producer) {
+            return false;
+        }
+        allMetricProducers.push_back(producer.value());
+    }
+
+    // build DurationMetricProducer
+    for (int i = 0; i < config.duration_metric_size(); i++) {
+        int metricIndex = allMetricProducers.size();
+        const DurationMetric& metric = config.duration_metric(i);
+        metricMap.insert({metric.id(), metricIndex});
+
+        optional<sp<MetricProducer>> producer = createDurationMetricProducerAndUpdateMetadata(
+                key, config, timeBaseTimeNs, currentTimeNs, metric, metricIndex,
+                allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
+                conditionTrackerMap, initialConditionCache, wizard, stateAtomIdMap,
+                allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
+                activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+                metricsWithActivation);
+        if (!producer) {
+            return false;
+        }
+        allMetricProducers.push_back(producer.value());
+    }
+
+    // build EventMetricProducer
+    for (int i = 0; i < config.event_metric_size(); i++) {
+        int metricIndex = allMetricProducers.size();
+        const EventMetric& metric = config.event_metric(i);
+        metricMap.insert({metric.id(), metricIndex});
+        optional<sp<MetricProducer>> producer = createEventMetricProducerAndUpdateMetadata(
+                key, config, timeBaseTimeNs, metric, metricIndex, allAtomMatchingTrackers,
+                atomMatchingTrackerMap, allConditionTrackers, conditionTrackerMap,
+                initialConditionCache, wizard, metricToActivationMap, trackerToMetricMap,
+                conditionToMetricMap, activationAtomTrackerToMetricMap,
+                deactivationAtomTrackerToMetricMap, metricsWithActivation);
+        if (!producer) {
+            return false;
+        }
+        allMetricProducers.push_back(producer.value());
+    }
+
+    // build ValueMetricProducer
+    for (int i = 0; i < config.value_metric_size(); i++) {
+        int metricIndex = allMetricProducers.size();
+        const ValueMetric& metric = config.value_metric(i);
+        metricMap.insert({metric.id(), metricIndex});
+        optional<sp<MetricProducer>> producer = createValueMetricProducerAndUpdateMetadata(
+                key, config, timeBaseTimeNs, currentTimeNs, pullerManager, metric, metricIndex,
+                allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
+                conditionTrackerMap, initialConditionCache, wizard, matcherWizard, stateAtomIdMap,
+                allStateGroupMaps, metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
+                activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+                metricsWithActivation);
+        if (!producer) {
+            return false;
+        }
+        allMetricProducers.push_back(producer.value());
+    }
+
+    // Gauge metrics.
+    for (int i = 0; i < config.gauge_metric_size(); i++) {
+        int metricIndex = allMetricProducers.size();
+        const GaugeMetric& metric = config.gauge_metric(i);
+        metricMap.insert({metric.id(), metricIndex});
+        optional<sp<MetricProducer>> producer = createGaugeMetricProducerAndUpdateMetadata(
+                key, config, timeBaseTimeNs, currentTimeNs, pullerManager, metric, metricIndex,
+                allAtomMatchingTrackers, atomMatchingTrackerMap, allConditionTrackers,
+                conditionTrackerMap, initialConditionCache, wizard, matcherWizard,
+                metricToActivationMap, trackerToMetricMap, conditionToMetricMap,
+                activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+                metricsWithActivation);
+        if (!producer) {
+            return false;
+        }
+        allMetricProducers.push_back(producer.value());
+    }
+    for (int i = 0; i < config.no_report_metric_size(); ++i) {
+        const auto no_report_metric = config.no_report_metric(i);
+        if (metricMap.find(no_report_metric) == metricMap.end()) {
+            ALOGW("no_report_metric %" PRId64 " not exist", no_report_metric);
+            return false;
+        }
+        noReportMetricIds.insert(no_report_metric);
+    }
+
+    const set<int> whitelistedAtomIds(config.whitelisted_atom_ids().begin(),
+                                      config.whitelisted_atom_ids().end());
+    for (const auto& it : allMetricProducers) {
+        // Register metrics to StateTrackers
+        for (int atomId : it->getSlicedStateAtoms()) {
+            // Register listener for non-whitelisted atoms only. Using whitelisted atom as a sliced
+            // state atom is not allowed.
+            if (whitelistedAtomIds.find(atomId) == whitelistedAtomIds.end()) {
+                StateManager::getInstance().registerListener(atomId, it);
+            } else {
+                return false;
+            }
+        }
+    }
+    return true;
+}
+
+bool initAlerts(const StatsdConfig& config, const unordered_map<int64_t, int>& metricProducerMap,
+                unordered_map<int64_t, int>& alertTrackerMap,
+                const sp<AlarmMonitor>& anomalyAlarmMonitor,
+                vector<sp<MetricProducer>>& allMetricProducers,
+                vector<sp<AnomalyTracker>>& allAnomalyTrackers) {
+    for (int i = 0; i < config.alert_size(); i++) {
+        const Alert& alert = config.alert(i);
+        alertTrackerMap.insert(std::make_pair(alert.id(), allAnomalyTrackers.size()));
+        optional<sp<AnomalyTracker>> anomalyTracker = createAnomalyTracker(
+                alert, anomalyAlarmMonitor, metricProducerMap, allMetricProducers);
+        if (!anomalyTracker) {
+            return false;
+        }
+        allAnomalyTrackers.push_back(anomalyTracker.value());
+    }
+    if (!initSubscribersForSubscriptionType(config, Subscription::ALERT, alertTrackerMap,
+                                            allAnomalyTrackers)) {
+        return false;
+    }
+    return true;
+}
+
+bool initAlarms(const StatsdConfig& config, const ConfigKey& key,
+                const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
+                const int64_t currentTimeNs, vector<sp<AlarmTracker>>& allAlarmTrackers) {
+    unordered_map<int64_t, int> alarmTrackerMap;
+    int64_t startMillis = timeBaseNs / 1000 / 1000;
+    int64_t currentTimeMillis = currentTimeNs / 1000 / 1000;
+    for (int i = 0; i < config.alarm_size(); i++) {
+        const Alarm& alarm = config.alarm(i);
+        if (alarm.offset_millis() <= 0) {
+            ALOGW("Alarm offset_millis should be larger than 0.");
+            return false;
+        }
+        if (alarm.period_millis() <= 0) {
+            ALOGW("Alarm period_millis should be larger than 0.");
+            return false;
+        }
+        alarmTrackerMap.insert(std::make_pair(alarm.id(), allAlarmTrackers.size()));
+        allAlarmTrackers.push_back(
+                new AlarmTracker(startMillis, currentTimeMillis, alarm, key, periodicAlarmMonitor));
+    }
+    if (!initSubscribersForSubscriptionType(config, Subscription::ALARM, alarmTrackerMap,
+                                            allAlarmTrackers)) {
+        return false;
+    }
+    return true;
+}
+
+bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
+                      const sp<StatsPullerManager>& pullerManager,
+                      const sp<AlarmMonitor>& anomalyAlarmMonitor,
+                      const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
+                      const int64_t currentTimeNs, set<int>& allTagIds,
+                      vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+                      unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                      vector<sp<ConditionTracker>>& allConditionTrackers,
+                      unordered_map<int64_t, int>& conditionTrackerMap,
+                      vector<sp<MetricProducer>>& allMetricProducers,
+                      unordered_map<int64_t, int>& metricProducerMap,
+                      vector<sp<AnomalyTracker>>& allAnomalyTrackers,
+                      vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers,
+                      unordered_map<int, std::vector<int>>& conditionToMetricMap,
+                      unordered_map<int, std::vector<int>>& trackerToMetricMap,
+                      unordered_map<int, std::vector<int>>& trackerToConditionMap,
+                      unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+                      unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+                      unordered_map<int64_t, int>& alertTrackerMap,
+                      vector<int>& metricsWithActivation, map<int64_t, uint64_t>& stateProtoHashes,
+                      set<int64_t>& noReportMetricIds) {
+    vector<ConditionState> initialConditionCache;
+    unordered_map<int64_t, int> stateAtomIdMap;
+    unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
+
+    if (!initAtomMatchingTrackers(config, uidMap, atomMatchingTrackerMap, allAtomMatchingTrackers,
+                                  allTagIds)) {
+        ALOGE("initAtomMatchingTrackers failed");
+        return false;
+    }
+    VLOG("initAtomMatchingTrackers succeed...");
+
+    if (!initConditions(key, config, atomMatchingTrackerMap, conditionTrackerMap,
+                        allConditionTrackers, trackerToConditionMap, initialConditionCache)) {
+        ALOGE("initConditionTrackers failed");
+        return false;
+    }
+
+    if (!initStates(config, stateAtomIdMap, allStateGroupMaps, stateProtoHashes)) {
+        ALOGE("initStates failed");
+        return false;
+    }
+    if (!initMetrics(key, config, timeBaseNs, currentTimeNs, pullerManager, atomMatchingTrackerMap,
+                     conditionTrackerMap, allAtomMatchingTrackers, stateAtomIdMap,
+                     allStateGroupMaps, allConditionTrackers, initialConditionCache,
+                     allMetricProducers, conditionToMetricMap, trackerToMetricMap,
+                     metricProducerMap, noReportMetricIds, activationAtomTrackerToMetricMap,
+                     deactivationAtomTrackerToMetricMap, metricsWithActivation)) {
+        ALOGE("initMetricProducers failed");
+        return false;
+    }
+    if (!initAlerts(config, metricProducerMap, alertTrackerMap, anomalyAlarmMonitor,
+                    allMetricProducers, allAnomalyTrackers)) {
+        ALOGE("initAlerts failed");
+        return false;
+    }
+    if (!initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs,
+                    allPeriodicAlarmTrackers)) {
+        ALOGE("initAlarms failed");
+        return false;
+    }
+
+    return true;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/bin/src/metrics/parsing_utils/metrics_manager_util.h b/bin/src/metrics/parsing_utils/metrics_manager_util.h
new file mode 100644
index 0000000..84e1e4e
--- /dev/null
+++ b/bin/src/metrics/parsing_utils/metrics_manager_util.h
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+#include "anomaly/AlarmTracker.h"
+#include "condition/ConditionTracker.h"
+#include "external/StatsPullerManager.h"
+#include "matchers/AtomMatchingTracker.h"
+#include "metrics/MetricProducer.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Helper functions for creating, validating, and updating config components from StatsdConfig.
+// Should only be called from metrics_manager_util and config_update_utils.
+
+// Create a AtomMatchingTracker.
+// input:
+// [logMatcher]: the input AtomMatcher from the StatsdConfig
+// [index]: the index of the matcher
+// output:
+// new AtomMatchingTracker, or null if the tracker is unable to be created
+sp<AtomMatchingTracker> createAtomMatchingTracker(const AtomMatcher& logMatcher, const int index,
+                                                  const sp<UidMap>& uidMap);
+
+// Create a ConditionTracker.
+// input:
+// [predicate]: the input Predicate from the StatsdConfig
+// [index]: the index of the condition tracker
+// [atomMatchingTrackerMap]: map of atom matcher id to its index in allAtomMatchingTrackers
+// output:
+// new ConditionTracker, or null if the tracker is unable to be created
+sp<ConditionTracker> createConditionTracker(
+        const ConfigKey& key, const Predicate& predicate, const int index,
+        const unordered_map<int64_t, int>& atomMatchingTrackerMap);
+
+// Get the hash of a metric, combining the activation if the metric has one.
+bool getMetricProtoHash(const StatsdConfig& config, const google::protobuf::MessageLite& metric,
+                        const int64_t id,
+                        const std::unordered_map<int64_t, int>& metricToActivationMap,
+                        uint64_t& metricHash);
+
+// 1. Validates matcher existence
+// 2. Enforces matchers with dimensions and those used for trigger_event are about one atom
+// 3. Gets matcher index and updates tracker to metric map
+bool handleMetricWithAtomMatchingTrackers(
+        const int64_t matcherId, const int metricIndex, const bool enforceOneAtom,
+        const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        std::unordered_map<int, std::vector<int>>& trackerToMetricMap, int& logTrackerIndex);
+
+// 1. Validates condition existence, including those in links
+// 2. Gets condition index and updates condition to metric map
+bool handleMetricWithConditions(
+        const int64_t condition, const int metricIndex,
+        const std::unordered_map<int64_t, int>& conditionTrackerMap,
+        const ::google::protobuf::RepeatedPtrField<::android::os::statsd::MetricConditionLink>&
+                links,
+        const std::vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex,
+        std::unordered_map<int, std::vector<int>>& conditionToMetricMap);
+
+// Validates a metricActivation and populates state.
+// Fills the new event activation/deactivation maps, preserving the existing activations.
+// Returns false if there are errors.
+bool handleMetricActivationOnConfigUpdate(
+        const StatsdConfig& config, const int64_t metricId, const int metricIndex,
+        const std::unordered_map<int64_t, int>& metricToActivationMap,
+        const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
+        const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
+        const std::unordered_map<int, shared_ptr<Activation>>& oldEventActivationMap,
+        std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+        std::vector<int>& metricsWithActivation,
+        std::unordered_map<int, shared_ptr<Activation>>& newEventActivationMap,
+        std::unordered_map<int, std::vector<shared_ptr<Activation>>>& newEventDeactivationMap);
+
+// Creates a CountMetricProducer and updates the vectors/maps used by MetricsManager with
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createCountMetricProducerAndUpdateMetadata(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const int64_t currentTimeNs, const CountMetric& metric, const int metricIndex,
+        const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        std::vector<sp<ConditionTracker>>& allConditionTrackers,
+        const std::unordered_map<int64_t, int>& conditionTrackerMap,
+        const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+        const std::unordered_map<int64_t, int>& stateAtomIdMap,
+        const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
+        const std::unordered_map<int64_t, int>& metricToActivationMap,
+        std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+        std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+        std::vector<int>& metricsWithActivation);
+
+// Creates a DurationMetricProducer and updates the vectors/maps used by MetricsManager with
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createDurationMetricProducerAndUpdateMetadata(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const int64_t currentTimeNs, const DurationMetric& metric, const int metricIndex,
+        const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        std::vector<sp<ConditionTracker>>& allConditionTrackers,
+        const std::unordered_map<int64_t, int>& conditionTrackerMap,
+        const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+        const std::unordered_map<int64_t, int>& stateAtomIdMap,
+        const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
+        const std::unordered_map<int64_t, int>& metricToActivationMap,
+        std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+        std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+        std::vector<int>& metricsWithActivation);
+
+// Creates an EventMetricProducer and updates the vectors/maps used by MetricsManager with
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createEventMetricProducerAndUpdateMetadata(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const EventMetric& metric, const int metricIndex,
+        const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        std::vector<sp<ConditionTracker>>& allConditionTrackers,
+        const std::unordered_map<int64_t, int>& conditionTrackerMap,
+        const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+        const std::unordered_map<int64_t, int>& metricToActivationMap,
+        std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+        std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+        std::vector<int>& metricsWithActivation);
+
+// Creates a CountMetricProducer and updates the vectors/maps used by MetricsManager with
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createValueMetricProducerAndUpdateMetadata(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+        const ValueMetric& metric, const int metricIndex,
+        const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        std::vector<sp<ConditionTracker>>& allConditionTrackers,
+        const std::unordered_map<int64_t, int>& conditionTrackerMap,
+        const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const std::unordered_map<int64_t, int>& stateAtomIdMap,
+        const std::unordered_map<int64_t, std::unordered_map<int, int64_t>>& allStateGroupMaps,
+        const std::unordered_map<int64_t, int>& metricToActivationMap,
+        std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+        std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+        std::vector<int>& metricsWithActivation);
+
+// Creates a GaugeMetricProducer and updates the vectors/maps used by MetricsManager with
+// the appropriate indices. Returns an sp to the producer, or nullopt if there was an error.
+optional<sp<MetricProducer>> createGaugeMetricProducerAndUpdateMetadata(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+        const GaugeMetric& metric, const int metricIndex,
+        const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        std::vector<sp<ConditionTracker>>& allConditionTrackers,
+        const std::unordered_map<int64_t, int>& conditionTrackerMap,
+        const std::vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
+        const sp<EventMatcherWizard>& matcherWizard,
+        const std::unordered_map<int64_t, int>& metricToActivationMap,
+        std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+        std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+        std::vector<int>& metricsWithActivation);
+
+// Creates an AnomalyTracker and adds it to the appropriate metric.
+// Returns an sp to the AnomalyTracker, or nullopt if there was an error.
+optional<sp<AnomalyTracker>> createAnomalyTracker(
+        const Alert& alert, const sp<AlarmMonitor>& anomalyAlarmMonitor,
+        const std::unordered_map<int64_t, int>& metricProducerMap,
+        std::vector<sp<MetricProducer>>& allMetricProducers);
+
+// Templated function for adding subscriptions to alarms or alerts. Returns true if successful.
+template <typename T>
+bool initSubscribersForSubscriptionType(const StatsdConfig& config,
+                                        const Subscription_RuleType ruleType,
+                                        const std::unordered_map<int64_t, int>& ruleMap,
+                                        std::vector<T>& allRules) {
+    for (int i = 0; i < config.subscription_size(); ++i) {
+        const Subscription& subscription = config.subscription(i);
+        if (subscription.rule_type() != ruleType) {
+            continue;
+        }
+        if (subscription.subscriber_information_case() ==
+            Subscription::SubscriberInformationCase::SUBSCRIBER_INFORMATION_NOT_SET) {
+            ALOGW("subscription \"%lld\" has no subscriber info.\"", (long long)subscription.id());
+            return false;
+        }
+        const auto& itr = ruleMap.find(subscription.rule_id());
+        if (itr == ruleMap.end()) {
+            ALOGW("subscription \"%lld\" has unknown rule id: \"%lld\"",
+                  (long long)subscription.id(), (long long)subscription.rule_id());
+            return false;
+        }
+        const int ruleIndex = itr->second;
+        allRules[ruleIndex]->addSubscription(subscription);
+    }
+    return true;
+}
+
+// Helper functions for MetricsManager to initialize from StatsdConfig.
+// *Note*: only initStatsdConfig() should be called from outside.
+// All other functions are intermediate
+// steps, created to make unit tests easier. And most of the parameters in these
+// functions are temporary objects in the initialization phase.
+
+// Initialize the AtomMatchingTrackers.
+// input:
+// [key]: the config key that this config belongs to
+// [config]: the input StatsdConfig
+// output:
+// [atomMatchingTrackerMap]: this map should contain matcher name to index mapping
+// [allAtomMatchingTrackers]: should store the sp to all the AtomMatchingTracker
+// [allTagIds]: contains the set of all interesting tag ids to this config.
+bool initAtomMatchingTrackers(const StatsdConfig& config, const sp<UidMap>& uidMap,
+                              std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                              std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+                              std::set<int>& allTagIds);
+
+// Initialize ConditionTrackers
+// input:
+// [key]: the config key that this config belongs to
+// [config]: the input config
+// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step.
+// output:
+// [conditionTrackerMap]: this map should contain condition name to index mapping
+// [allConditionTrackers]: stores the sp to all the ConditionTrackers
+// [trackerToConditionMap]: contain the mapping from index of
+//                        log tracker to condition trackers that use the log tracker
+// [initialConditionCache]: stores the initial conditions for each ConditionTracker
+bool initConditions(const ConfigKey& key, const StatsdConfig& config,
+                    const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                    std::unordered_map<int64_t, int>& conditionTrackerMap,
+                    std::vector<sp<ConditionTracker>>& allConditionTrackers,
+                    std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
+                    std::vector<ConditionState>& initialConditionCache);
+
+// Initialize State maps using State protos in the config. These maps will
+// eventually be passed to MetricProducers to initialize their state info.
+// input:
+// [config]: the input config
+// output:
+// [stateAtomIdMap]: this map should contain the mapping from state ids to atom ids
+// [allStateGroupMaps]: this map should contain the mapping from states ids and state
+//                      values to state group ids for all states
+// [stateProtoHashes]: contains a map of state id to the hash of the State proto from the config
+bool initStates(const StatsdConfig& config, unordered_map<int64_t, int>& stateAtomIdMap,
+                unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+                std::map<int64_t, uint64_t>& stateProtoHashes);
+
+// Initialize MetricProducers.
+// input:
+// [key]: the config key that this config belongs to
+// [config]: the input config
+// [timeBaseSec]: start time base for all metrics
+// [atomMatchingTrackerMap]: AtomMatchingTracker name to index mapping from previous step.
+// [conditionTrackerMap]: condition name to index mapping
+// [stateAtomIdMap]: contains the mapping from state ids to atom ids
+// [allStateGroupMaps]: contains the mapping from atom ids and state values to
+//                      state group ids for all states
+// output:
+// [allMetricProducers]: contains the list of sp to the MetricProducers created.
+// [conditionToMetricMap]: contains the mapping from condition tracker index to
+//                          the list of MetricProducer index
+// [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
+bool initMetrics(
+        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
+        const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
+        const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+        const std::unordered_map<int64_t, int>& conditionTrackerMap,
+        const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+        const unordered_map<int64_t, int>& stateAtomIdMap,
+        const unordered_map<int64_t, unordered_map<int, int64_t>>& allStateGroupMaps,
+        vector<sp<ConditionTracker>>& allConditionTrackers,
+        const std::vector<ConditionState>& initialConditionCache,
+        std::vector<sp<MetricProducer>>& allMetricProducers,
+        std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+        std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+        std::set<int64_t>& noReportMetricIds,
+        std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+        std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+        std::vector<int>& metricsWithActivation);
+
+// Initialize alarms
+// Is called both on initialize new configs and config updates since alarms do not have any state.
+bool initAlarms(const StatsdConfig& config, const ConfigKey& key,
+                const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
+                const int64_t currentTimeNs, std::vector<sp<AlarmTracker>>& allAlarmTrackers);
+
+// Initialize MetricsManager from StatsdConfig.
+// Parameters are the members of MetricsManager. See MetricsManager for declaration.
+bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const sp<UidMap>& uidMap,
+                      const sp<StatsPullerManager>& pullerManager,
+                      const sp<AlarmMonitor>& anomalyAlarmMonitor,
+                      const sp<AlarmMonitor>& periodicAlarmMonitor, const int64_t timeBaseNs,
+                      const int64_t currentTimeNs, std::set<int>& allTagIds,
+                      std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+                      std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
+                      std::vector<sp<ConditionTracker>>& allConditionTrackers,
+                      std::unordered_map<int64_t, int>& conditionTrackerMap,
+                      std::vector<sp<MetricProducer>>& allMetricProducers,
+                      std::unordered_map<int64_t, int>& metricProducerMap,
+                      vector<sp<AnomalyTracker>>& allAnomalyTrackers,
+                      vector<sp<AlarmTracker>>& allPeriodicAlarmTrackers,
+                      std::unordered_map<int, std::vector<int>>& conditionToMetricMap,
+                      std::unordered_map<int, std::vector<int>>& trackerToMetricMap,
+                      std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
+                      std::unordered_map<int, std::vector<int>>& activationAtomTrackerToMetricMap,
+                      std::unordered_map<int, std::vector<int>>& deactivationAtomTrackerToMetricMap,
+                      std::unordered_map<int64_t, int>& alertTrackerMap,
+                      std::vector<int>& metricsWithActivation,
+                      std::map<int64_t, uint64_t>& stateProtoHashes,
+                      std::set<int64_t>& noReportMetricIds);
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/bin/src/packages/PackageInfoListener.h b/bin/src/packages/PackageInfoListener.h
index 6c50a8c..1bc84c5 100644
--- a/bin/src/packages/PackageInfoListener.h
+++ b/bin/src/packages/PackageInfoListener.h
@@ -17,6 +17,8 @@
 #ifndef STATSD_PACKAGE_INFO_LISTENER_H
 #define STATSD_PACKAGE_INFO_LISTENER_H
 
+#include <utils/RefBase.h>
+
 #include <string>
 
 namespace android {
diff --git a/bin/src/packages/UidMap.h b/bin/src/packages/UidMap.h
index 22250ae..622321b 100644
--- a/bin/src/packages/UidMap.h
+++ b/bin/src/packages/UidMap.h
@@ -17,7 +17,6 @@
 #pragma once
 
 #include "config/ConfigKey.h"
-#include "config/ConfigListener.h"
 #include "packages/PackageInfoListener.h"
 #include "stats_util.h"
 
diff --git a/bin/src/shell/ShellSubscriber.cpp b/bin/src/shell/ShellSubscriber.cpp
index fd883c2..9d8f0c2 100644
--- a/bin/src/shell/ShellSubscriber.cpp
+++ b/bin/src/shell/ShellSubscriber.cpp
@@ -191,7 +191,7 @@
     mProto.clear();
     int count = 0;
     for (const auto& event : data) {
-        if (matchesSimple(*mUidMap, matcher, *event)) {
+        if (matchesSimple(mUidMap, matcher, *event)) {
             count++;
             uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
                                               util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
@@ -209,7 +209,7 @@
 
     mProto.clear();
     for (const auto& matcher : mSubscriptionInfo->mPushedMatchers) {
-        if (matchesSimple(*mUidMap, matcher, event)) {
+        if (matchesSimple(mUidMap, matcher, event)) {
             uint64_t atomToken = mProto.start(util::FIELD_TYPE_MESSAGE |
                                               util::FIELD_COUNT_REPEATED | FIELD_ID_ATOM);
             event.ToProto(mProto);
diff --git a/bin/src/stats_log.proto b/bin/src/stats_log.proto
index bb07963..f91c4c0 100644
--- a/bin/src/stats_log.proto
+++ b/bin/src/stats_log.proto
@@ -488,6 +488,9 @@
       optional int64 max_bucket_boundary_delay_ns = 10;
       optional int64 bucket_unknown_condition = 11;
       optional int64 bucket_count = 12;
+      optional int64 late_log_event = 13;
+      optional int64 sum_late_log_event_extra_duration_ns = 14;
+      optional int64 max_late_log_event_extra_duration_ns = 15;
     }
     repeated AtomMetricStats atom_metric_stats = 17;
 
diff --git a/bin/src/stats_log_util.cpp b/bin/src/stats_log_util.cpp
index 423bae8..d6e04f7 100644
--- a/bin/src/stats_log_util.cpp
+++ b/bin/src/stats_log_util.cpp
@@ -99,6 +99,9 @@
 const int FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS = 10;
 const int FIELD_ID_BUCKET_UNKNOWN_CONDITION = 11;
 const int FIELD_ID_BUCKET_COUNT = 12;
+const int FIELD_ID_LATE_LOG_EVENT = 13;
+const int FIELD_ID_SUM_LATE_LOG_EVENT_EXTRA_DURATION_NS = 14;
+const int FIELD_ID_MAX_LATE_LOG_EVENT_EXTRA_DURATION_NS = 15;
 
 namespace {
 
@@ -462,6 +465,13 @@
     }
 }
 
+void writeNonZeroStatToStream(const uint64_t fieldId, const int value,
+                              util::ProtoOutputStream* protoOutput) {
+    if (value != 0) {
+        protoOutput->write(fieldId, value);
+    }
+}
+
 void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>& pair,
                               util::ProtoOutputStream* protoOutput) {
     uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_PULLED_ATOM_STATS |
@@ -485,14 +495,12 @@
                        (long long)pair.second.pullTimeout);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_EXCEED_MAX_DELAY,
                        (long long)pair.second.pullExceedMaxDelay);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_FAILED,
-                       (long long)pair.second.pullFailed);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_EMPTY_DATA,
-                       (long long)pair.second.emptyData);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_FAILED, (long long)pair.second.pullFailed);
+    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_EMPTY_DATA, (long long)pair.second.emptyData);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_REGISTERED_COUNT,
-                       (long long) pair.second.registeredCount);
+                       (long long)pair.second.registeredCount);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_PULL_UNREGISTERED_COUNT,
-                       (long long) pair.second.unregisteredCount);
+                       (long long)pair.second.unregisteredCount);
     protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_ERROR_COUNT, pair.second.atomErrorCount);
     protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BINDER_CALL_FAIL_COUNT,
                        (long long)pair.second.binderCallFailCount);
@@ -517,29 +525,37 @@
                                   util::ProtoOutputStream *protoOutput) {
     uint64_t token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_METRIC_STATS |
                                         FIELD_COUNT_REPEATED);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, (long long)pair.first);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_HARD_DIMENSION_LIMIT_REACHED,
-                       (long long)pair.second.hardDimensionLimitReached);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_LATE_LOG_EVENT_SKIPPED,
-                       (long long)pair.second.lateLogEventSkipped);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_FORWARD_BUCKETS,
-                       (long long)pair.second.skippedForwardBuckets);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BAD_VALUE_TYPE,
-                       (long long)pair.second.badValueType);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_CHANGE_IN_NEXT_BUCKET,
-                       (long long)pair.second.conditionChangeInNextBucket);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_INVALIDATED_BUCKET,
-                       (long long)pair.second.invalidatedBucket);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_DROPPED,
-                       (long long)pair.second.bucketDropped);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS,
-                       (long long)pair.second.minBucketBoundaryDelayNs);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS,
-                       (long long)pair.second.maxBucketBoundaryDelayNs);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_UNKNOWN_CONDITION,
-                       (long long)pair.second.bucketUnknownCondition);
-    protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_COUNT,
-                       (long long)pair.second.bucketCount);
+
+    writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_METRIC_ID, (long long)pair.first,
+                             protoOutput);
+    writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_HARD_DIMENSION_LIMIT_REACHED,
+                             (long long)pair.second.hardDimensionLimitReached, protoOutput);
+    writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_LATE_LOG_EVENT_SKIPPED,
+                             (long long)pair.second.lateLogEventSkipped, protoOutput);
+    writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_SKIPPED_FORWARD_BUCKETS,
+                             (long long)pair.second.skippedForwardBuckets, protoOutput);
+    writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_BAD_VALUE_TYPE,
+                             (long long)pair.second.badValueType, protoOutput);
+    writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_CONDITION_CHANGE_IN_NEXT_BUCKET,
+                             (long long)pair.second.conditionChangeInNextBucket, protoOutput);
+    writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_INVALIDATED_BUCKET,
+                             (long long)pair.second.invalidatedBucket, protoOutput);
+    writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_DROPPED,
+                             (long long)pair.second.bucketDropped, protoOutput);
+    writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_MIN_BUCKET_BOUNDARY_DELAY_NS,
+                             (long long)pair.second.minBucketBoundaryDelayNs, protoOutput);
+    writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_MAX_BUCKET_BOUNDARY_DELAY_NS,
+                             (long long)pair.second.maxBucketBoundaryDelayNs, protoOutput);
+    writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_UNKNOWN_CONDITION,
+                             (long long)pair.second.bucketUnknownCondition, protoOutput);
+    writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_BUCKET_COUNT,
+                             (long long)pair.second.bucketCount, protoOutput);
+    writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_LATE_LOG_EVENT,
+                             (long long)pair.second.lateLogEvent, protoOutput);
+    writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_SUM_LATE_LOG_EVENT_EXTRA_DURATION_NS,
+                             (long long)pair.second.sumLateLogEventExtraDurationNs, protoOutput);
+    writeNonZeroStatToStream(FIELD_TYPE_INT64 | FIELD_ID_MAX_LATE_LOG_EVENT_EXTRA_DURATION_NS,
+                             (long long)pair.second.maxLateLogEventExtraDurationNs, protoOutput);
     protoOutput->end(token);
 }
 
diff --git a/bin/src/stats_log_util.h b/bin/src/stats_log_util.h
index 7fdde93..dcfe0c7 100644
--- a/bin/src/stats_log_util.h
+++ b/bin/src/stats_log_util.h
@@ -77,6 +77,10 @@
 
 int64_t MillisToNano(const int64_t millis);
 
+// Helper function to write a stats field to ProtoOutputStream if it's a non-zero value.
+void writeNonZeroStatToStream(const uint64_t fieldId, const int value,
+                              ProtoOutputStream* protoOutput);
+
 // Helper function to write PulledAtomStats to ProtoOutputStream
 void writePullerStatsToStream(const std::pair<int, StatsdStats::PulledAtomStats>& pair,
                               ProtoOutputStream* protoOutput);
diff --git a/bin/tests/ConfigManager_test.cpp b/bin/tests/ConfigManager_test.cpp
index 9455304..24c8f2b 100644
--- a/bin/tests/ConfigManager_test.cpp
+++ b/bin/tests/ConfigManager_test.cpp
@@ -44,8 +44,8 @@
  */
 class MockListener : public ConfigListener {
 public:
-    MOCK_METHOD3(OnConfigUpdated, void(const int64_t timestampNs, const ConfigKey& key,
-                                       const StatsdConfig& config));
+    MOCK_METHOD3(OnConfigUpdated,
+                 void(const int64_t timestampNs, const ConfigKey& key, const StatsdConfig& config));
     MOCK_METHOD1(OnConfigRemoved, void(const ConfigKey& key));
 };
 
@@ -89,26 +89,26 @@
         manager->StartupForTest();
 
         // Add another one
-        EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, ConfigKeyEq(1, StringToId("zzz")),
-            StatsdConfigEq(91)))
+        EXPECT_CALL(*(listener.get()),
+                    OnConfigUpdated(_, ConfigKeyEq(1, StringToId("zzz")), StatsdConfigEq(91)))
                 .RetiresOnSaturation();
         manager->UpdateConfig(ConfigKey(1, StringToId("zzz")), config91);
 
         // Update It
-        EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, ConfigKeyEq(1, StringToId("zzz")),
-            StatsdConfigEq(92)))
+        EXPECT_CALL(*(listener.get()),
+                    OnConfigUpdated(_, ConfigKeyEq(1, StringToId("zzz")), StatsdConfigEq(92)))
                 .RetiresOnSaturation();
         manager->UpdateConfig(ConfigKey(1, StringToId("zzz")), config92);
 
         // Add one with the same uid but a different name
-        EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, ConfigKeyEq(1, StringToId("yyy")),
-            StatsdConfigEq(93)))
+        EXPECT_CALL(*(listener.get()),
+                    OnConfigUpdated(_, ConfigKeyEq(1, StringToId("yyy")), StatsdConfigEq(93)))
                 .RetiresOnSaturation();
         manager->UpdateConfig(ConfigKey(1, StringToId("yyy")), config93);
 
         // Add one with the same name but a different uid
-        EXPECT_CALL(*(listener.get()), OnConfigUpdated(_, ConfigKeyEq(2, StringToId("zzz")),
-            StatsdConfigEq(94)))
+        EXPECT_CALL(*(listener.get()),
+                    OnConfigUpdated(_, ConfigKeyEq(2, StringToId("zzz")), StatsdConfigEq(94)))
                 .RetiresOnSaturation();
         manager->UpdateConfig(ConfigKey(2, StringToId("zzz")), config94);
 
diff --git a/bin/tests/LogEntryMatcher_test.cpp b/bin/tests/LogEntryMatcher_test.cpp
index 00fa55f..6a433d4 100644
--- a/bin/tests/LogEntryMatcher_test.cpp
+++ b/bin/tests/LogEntryMatcher_test.cpp
@@ -110,7 +110,7 @@
 }  // anonymous namespace
 
 TEST(AtomMatcherTest, TestSimpleMatcher) {
-    UidMap uidMap;
+    sp<UidMap> uidMap = new UidMap();
 
     // Set up the matcher
     AtomMatcher matcher;
@@ -129,7 +129,7 @@
 }
 
 TEST(AtomMatcherTest, TestAttributionMatcher) {
-    UidMap uidMap;
+    sp<UidMap> uidMap = new UidMap();
     std::vector<int> attributionUids = {1111, 2222, 3333};
     std::vector<string> attributionTags = {"location1", "location2", "location3"};
 
@@ -204,7 +204,7 @@
             "pkg0");
     EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
 
-    uidMap.updateMap(
+    uidMap->updateMap(
             1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
             {android::String16("v1"), android::String16("v1"), android::String16("v2"),
              android::String16("v1"), android::String16("v2")},
@@ -356,8 +356,8 @@
 }
 
 TEST(AtomMatcherTest, TestUidFieldMatcher) {
-    UidMap uidMap;
-    uidMap.updateMap(
+    sp<UidMap> uidMap = new UidMap();
+    uidMap->updateMap(
             1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
             {android::String16("v1"), android::String16("v1"), android::String16("v2"),
              android::String16("v1"), android::String16("v2")},
@@ -392,8 +392,8 @@
 }
 
 TEST(AtomMatcherTest, TestNeqAnyStringMatcher) {
-    UidMap uidMap;
-    uidMap.updateMap(
+    sp<UidMap> uidMap = new UidMap();
+    uidMap->updateMap(
             1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
             {android::String16("v1"), android::String16("v1"), android::String16("v2"),
              android::String16("v1"), android::String16("v2")},
@@ -453,8 +453,8 @@
 }
 
 TEST(AtomMatcherTest, TestEqAnyStringMatcher) {
-    UidMap uidMap;
-    uidMap.updateMap(
+    sp<UidMap> uidMap = new UidMap();
+    uidMap->updateMap(
             1, {1111, 1111, 2222, 3333, 3333} /* uid list */, {1, 1, 2, 1, 2} /* version list */,
             {android::String16("v1"), android::String16("v1"), android::String16("v2"),
              android::String16("v1"), android::String16("v2")},
@@ -517,7 +517,7 @@
 }
 
 TEST(AtomMatcherTest, TestBoolMatcher) {
-    UidMap uidMap;
+    sp<UidMap> uidMap = new UidMap();
     // Set up the matcher
     AtomMatcher matcher;
     auto simpleMatcher = matcher.mutable_simple_atom_matcher();
@@ -550,7 +550,7 @@
 }
 
 TEST(AtomMatcherTest, TestStringMatcher) {
-    UidMap uidMap;
+    sp<UidMap> uidMap = new UidMap();
     // Set up the matcher
     AtomMatcher matcher;
     auto simpleMatcher = matcher.mutable_simple_atom_matcher();
@@ -568,7 +568,7 @@
 }
 
 TEST(AtomMatcherTest, TestMultiFieldsMatcher) {
-    UidMap uidMap;
+    sp<UidMap> uidMap = new UidMap();
     // Set up the matcher
     AtomMatcher matcher;
     auto simpleMatcher = matcher.mutable_simple_atom_matcher();
@@ -597,7 +597,7 @@
 }
 
 TEST(AtomMatcherTest, TestIntComparisonMatcher) {
-    UidMap uidMap;
+    sp<UidMap> uidMap = new UidMap();
     // Set up the matcher
     AtomMatcher matcher;
     auto simpleMatcher = matcher.mutable_simple_atom_matcher();
@@ -654,7 +654,7 @@
 }
 
 TEST(AtomMatcherTest, TestFloatComparisonMatcher) {
-    UidMap uidMap;
+    sp<UidMap> uidMap = new UidMap();
     // Set up the matcher
     AtomMatcher matcher;
     auto simpleMatcher = matcher.mutable_simple_atom_matcher();
diff --git a/bin/tests/MetricsManager_test.cpp b/bin/tests/MetricsManager_test.cpp
index aa562f1..62c9e2e 100644
--- a/bin/tests/MetricsManager_test.cpp
+++ b/bin/tests/MetricsManager_test.cpp
@@ -12,6 +12,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <gmock/gmock.h>
 #include <gtest/gtest.h>
 #include <private/android_filesystem_config.h>
 #include <stdio.h>
@@ -23,12 +24,12 @@
 #include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
 #include "metrics/metrics_test_helper.h"
 #include "src/condition/ConditionTracker.h"
-#include "src/matchers/LogMatchingTracker.h"
+#include "src/matchers/AtomMatchingTracker.h"
 #include "src/metrics/CountMetricProducer.h"
 #include "src/metrics/GaugeMetricProducer.h"
 #include "src/metrics/MetricProducer.h"
 #include "src/metrics/ValueMetricProducer.h"
-#include "src/metrics/metrics_manager_util.h"
+#include "src/metrics/parsing_utils/metrics_manager_util.h"
 #include "src/state/StateManager.h"
 #include "statsd_test_util.h"
 
@@ -48,7 +49,6 @@
 
 namespace {
 const ConfigKey kConfigKey(0, 12345);
-const long kAlertId = 3;
 
 const long timeBaseSec = 1000;
 
@@ -90,563 +90,18 @@
     metric->set_bucket(ONE_MINUTE);
     metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/);
     metric->mutable_dimensions_in_what()->add_child()->set_field(1);
-
-    config.add_no_report_metric(3);
-
-    auto alert = config.add_alert();
-    alert->set_id(kAlertId);
-    alert->set_metric_id(3);
-    alert->set_num_buckets(10);
-    alert->set_refractory_period_secs(100);
-    alert->set_trigger_if_sum_gt(100);
     return config;
 }
 
-StatsdConfig buildCircleMatchers() {
-    StatsdConfig config;
-    config.set_id(12345);
-
-    AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
-
-    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
-    simpleAtomMatcher->add_field_value_matcher()->set_field(
-            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
-            2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
-
-    eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
-
-    AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
-    combination->set_operation(LogicalOperation::OR);
-    combination->add_matcher(StringToId("SCREEN_IS_ON"));
-    // Circle dependency
-    combination->add_matcher(StringToId("SCREEN_ON_OR_OFF"));
-
-    return config;
-}
-
-StatsdConfig buildAlertWithUnknownMetric() {
-    StatsdConfig config;
-    config.set_id(12345);
-
-    AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
-
-    CountMetric* metric = config.add_count_metric();
-    metric->set_id(3);
-    metric->set_what(StringToId("SCREEN_IS_ON"));
-    metric->set_bucket(ONE_MINUTE);
-    metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/);
-    metric->mutable_dimensions_in_what()->add_child()->set_field(1);
-
-    auto alert = config.add_alert();
-    alert->set_id(3);
-    alert->set_metric_id(2);
-    alert->set_num_buckets(10);
-    alert->set_refractory_period_secs(100);
-    alert->set_trigger_if_sum_gt(100);
-    return config;
-}
-
-StatsdConfig buildMissingMatchers() {
-    StatsdConfig config;
-    config.set_id(12345);
-
-    AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
-
-    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
-    simpleAtomMatcher->add_field_value_matcher()->set_field(
-            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
-            2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
-
-    eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
-
-    AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
-    combination->set_operation(LogicalOperation::OR);
-    combination->add_matcher(StringToId("SCREEN_IS_ON"));
-    // undefined matcher
-    combination->add_matcher(StringToId("ABC"));
-
-    return config;
-}
-
-StatsdConfig buildMissingPredicate() {
-    StatsdConfig config;
-    config.set_id(12345);
-
-    CountMetric* metric = config.add_count_metric();
-    metric->set_id(3);
-    metric->set_what(StringToId("SCREEN_EVENT"));
-    metric->set_bucket(ONE_MINUTE);
-    metric->set_condition(StringToId("SOME_CONDITION"));
-
-    AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("SCREEN_EVENT"));
-
-    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(2);
-
-    return config;
-}
-
-StatsdConfig buildDimensionMetricsWithMultiTags() {
-    StatsdConfig config;
-    config.set_id(12345);
-
-    AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("BATTERY_VERY_LOW"));
-    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(2);
-
-    eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("BATTERY_VERY_VERY_LOW"));
-    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(3);
-
-    eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("BATTERY_LOW"));
-
-    AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
-    combination->set_operation(LogicalOperation::OR);
-    combination->add_matcher(StringToId("BATTERY_VERY_LOW"));
-    combination->add_matcher(StringToId("BATTERY_VERY_VERY_LOW"));
-
-    // Count process state changes, slice by uid, while SCREEN_IS_OFF
-    CountMetric* metric = config.add_count_metric();
-    metric->set_id(3);
-    metric->set_what(StringToId("BATTERY_LOW"));
-    metric->set_bucket(ONE_MINUTE);
-    // This case is interesting. We want to dimension across two atoms.
-    metric->mutable_dimensions_in_what()->add_child()->set_field(1);
-
-    auto alert = config.add_alert();
-    alert->set_id(kAlertId);
-    alert->set_metric_id(3);
-    alert->set_num_buckets(10);
-    alert->set_refractory_period_secs(100);
-    alert->set_trigger_if_sum_gt(100);
-    return config;
-}
-
-StatsdConfig buildCirclePredicates() {
-    StatsdConfig config;
-    config.set_id(12345);
-
-    AtomMatcher* eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
-
-    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
-    simpleAtomMatcher->add_field_value_matcher()->set_field(
-            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
-            2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
-
-    eventMatcher = config.add_atom_matcher();
-    eventMatcher->set_id(StringToId("SCREEN_IS_OFF"));
-
-    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
-    simpleAtomMatcher->add_field_value_matcher()->set_field(
-            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
-            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
-
-    auto condition = config.add_predicate();
-    condition->set_id(StringToId("SCREEN_IS_ON"));
-    SimplePredicate* simplePredicate = condition->mutable_simple_predicate();
-    simplePredicate->set_start(StringToId("SCREEN_IS_ON"));
-    simplePredicate->set_stop(StringToId("SCREEN_IS_OFF"));
-
-    condition = config.add_predicate();
-    condition->set_id(StringToId("SCREEN_IS_EITHER_ON_OFF"));
-
-    Predicate_Combination* combination = condition->mutable_combination();
-    combination->set_operation(LogicalOperation::OR);
-    combination->add_predicate(StringToId("SCREEN_IS_ON"));
-    combination->add_predicate(StringToId("SCREEN_IS_EITHER_ON_OFF"));
-
-    return config;
-}
-
-StatsdConfig buildConfigWithDifferentPredicates() {
-    StatsdConfig config;
-    config.set_id(12345);
-
-    auto pulledAtomMatcher =
-            CreateSimpleAtomMatcher("SUBSYSTEM_SLEEP", util::SUBSYSTEM_SLEEP_STATE);
-    *config.add_atom_matcher() = pulledAtomMatcher;
-    auto screenOnAtomMatcher = CreateScreenTurnedOnAtomMatcher();
-    *config.add_atom_matcher() = screenOnAtomMatcher;
-    auto screenOffAtomMatcher = CreateScreenTurnedOffAtomMatcher();
-    *config.add_atom_matcher() = screenOffAtomMatcher;
-    auto batteryNoneAtomMatcher = CreateBatteryStateNoneMatcher();
-    *config.add_atom_matcher() = batteryNoneAtomMatcher;
-    auto batteryUsbAtomMatcher = CreateBatteryStateUsbMatcher();
-    *config.add_atom_matcher() = batteryUsbAtomMatcher;
-
-    // Simple condition with InitialValue set to default (unknown).
-    auto screenOnUnknownPredicate = CreateScreenIsOnPredicate();
-    *config.add_predicate() = screenOnUnknownPredicate;
-
-    // Simple condition with InitialValue set to false.
-    auto screenOnFalsePredicate = config.add_predicate();
-    screenOnFalsePredicate->set_id(StringToId("ScreenIsOnInitialFalse"));
-    SimplePredicate* simpleScreenOnFalsePredicate =
-            screenOnFalsePredicate->mutable_simple_predicate();
-    simpleScreenOnFalsePredicate->set_start(screenOnAtomMatcher.id());
-    simpleScreenOnFalsePredicate->set_stop(screenOffAtomMatcher.id());
-    simpleScreenOnFalsePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE);
-
-    // Simple condition with InitialValue set to false.
-    auto onBatteryFalsePredicate = config.add_predicate();
-    onBatteryFalsePredicate->set_id(StringToId("OnBatteryInitialFalse"));
-    SimplePredicate* simpleOnBatteryFalsePredicate =
-            onBatteryFalsePredicate->mutable_simple_predicate();
-    simpleOnBatteryFalsePredicate->set_start(batteryNoneAtomMatcher.id());
-    simpleOnBatteryFalsePredicate->set_stop(batteryUsbAtomMatcher.id());
-    simpleOnBatteryFalsePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE);
-
-    // Combination condition with both simple condition InitialValues set to false.
-    auto screenOnFalseOnBatteryFalsePredicate = config.add_predicate();
-    screenOnFalseOnBatteryFalsePredicate->set_id(StringToId("ScreenOnFalseOnBatteryFalse"));
-    screenOnFalseOnBatteryFalsePredicate->mutable_combination()->set_operation(
-            LogicalOperation::AND);
-    addPredicateToPredicateCombination(*screenOnFalsePredicate,
-                                       screenOnFalseOnBatteryFalsePredicate);
-    addPredicateToPredicateCombination(*onBatteryFalsePredicate,
-                                       screenOnFalseOnBatteryFalsePredicate);
-
-    // Combination condition with one simple condition InitialValue set to unknown and one set to
-    // false.
-    auto screenOnUnknownOnBatteryFalsePredicate = config.add_predicate();
-    screenOnUnknownOnBatteryFalsePredicate->set_id(StringToId("ScreenOnUnknowneOnBatteryFalse"));
-    screenOnUnknownOnBatteryFalsePredicate->mutable_combination()->set_operation(
-            LogicalOperation::AND);
-    addPredicateToPredicateCombination(screenOnUnknownPredicate,
-                                       screenOnUnknownOnBatteryFalsePredicate);
-    addPredicateToPredicateCombination(*onBatteryFalsePredicate,
-                                       screenOnUnknownOnBatteryFalsePredicate);
-
-    // Simple condition metric with initial value false.
-    ValueMetric* metric1 = config.add_value_metric();
-    metric1->set_id(StringToId("ValueSubsystemSleepWhileScreenOnInitialFalse"));
-    metric1->set_what(pulledAtomMatcher.id());
-    *metric1->mutable_value_field() =
-            CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
-    metric1->set_bucket(FIVE_MINUTES);
-    metric1->set_condition(screenOnFalsePredicate->id());
-
-    // Simple condition metric with initial value unknown.
-    ValueMetric* metric2 = config.add_value_metric();
-    metric2->set_id(StringToId("ValueSubsystemSleepWhileScreenOnInitialUnknown"));
-    metric2->set_what(pulledAtomMatcher.id());
-    *metric2->mutable_value_field() =
-            CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
-    metric2->set_bucket(FIVE_MINUTES);
-    metric2->set_condition(screenOnUnknownPredicate.id());
-
-    // Combination condition metric with initial values false and false.
-    ValueMetric* metric3 = config.add_value_metric();
-    metric3->set_id(StringToId("ValueSubsystemSleepWhileScreenOnFalseDeviceUnpluggedFalse"));
-    metric3->set_what(pulledAtomMatcher.id());
-    *metric3->mutable_value_field() =
-            CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
-    metric3->set_bucket(FIVE_MINUTES);
-    metric3->set_condition(screenOnFalseOnBatteryFalsePredicate->id());
-
-    // Combination condition metric with initial values unknown and false.
-    ValueMetric* metric4 = config.add_value_metric();
-    metric4->set_id(StringToId("ValueSubsystemSleepWhileScreenOnUnknownDeviceUnpluggedFalse"));
-    metric4->set_what(pulledAtomMatcher.id());
-    *metric4->mutable_value_field() =
-            CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
-    metric4->set_bucket(FIVE_MINUTES);
-    metric4->set_condition(screenOnUnknownOnBatteryFalsePredicate->id());
-
-    return config;
-}
-
-bool isSubset(const set<int32_t>& set1, const set<int32_t>& set2) {
-    return std::includes(set2.begin(), set2.end(), set1.begin(), set1.end());
+set<int32_t> unionSet(const vector<set<int32_t>> sets) {
+    set<int32_t> toRet;
+    for (const set<int32_t>& s : sets) {
+        toRet.insert(s.begin(), s.end());
+    }
+    return toRet;
 }
 }  // anonymous namespace
 
-TEST(MetricsManagerTest, TestInitialConditions) {
-    UidMap uidMap;
-    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-    sp<AlarmMonitor> anomalyAlarmMonitor;
-    sp<AlarmMonitor> periodicAlarmMonitor;
-    StatsdConfig config = buildConfigWithDifferentPredicates();
-    set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allAtomMatchers;
-    vector<sp<ConditionTracker>> allConditionTrackers;
-    vector<sp<MetricProducer>> allMetricProducers;
-    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
-    std::vector<sp<AlarmTracker>> allAlarmTrackers;
-    unordered_map<int, std::vector<int>> conditionToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToConditionMap;
-    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
-    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
-    unordered_map<int64_t, int> alertTrackerMap;
-    vector<int> metricsWithActivation;
-    std::set<int64_t> noReportMetricIds;
-
-    EXPECT_TRUE(initStatsdConfig(
-            kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchers, allConditionTrackers,
-            allMetricProducers, allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-            trackerToMetricMap, trackerToConditionMap, activationAtomTrackerToMetricMap,
-            deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
-            noReportMetricIds));
-    ASSERT_EQ(4u, allMetricProducers.size());
-    ASSERT_EQ(5u, allConditionTrackers.size());
-
-    ConditionKey queryKey;
-    vector<ConditionState> conditionCache(5, ConditionState::kNotEvaluated);
-
-    allConditionTrackers[3]->isConditionMet(queryKey, allConditionTrackers, false, conditionCache);
-    allConditionTrackers[4]->isConditionMet(queryKey, allConditionTrackers, false, conditionCache);
-    EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]);
-    EXPECT_EQ(ConditionState::kFalse, conditionCache[1]);
-    EXPECT_EQ(ConditionState::kFalse, conditionCache[2]);
-    EXPECT_EQ(ConditionState::kFalse, conditionCache[3]);
-    EXPECT_EQ(ConditionState::kUnknown, conditionCache[4]);
-
-    EXPECT_EQ(ConditionState::kFalse, allMetricProducers[0]->mCondition);
-    EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[1]->mCondition);
-    EXPECT_EQ(ConditionState::kFalse, allMetricProducers[2]->mCondition);
-    EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[3]->mCondition);
-}
-
-TEST(MetricsManagerTest, TestGoodConfig) {
-    UidMap uidMap;
-    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-    sp<AlarmMonitor> anomalyAlarmMonitor;
-    sp<AlarmMonitor> periodicAlarmMonitor;
-    StatsdConfig config = buildGoodConfig();
-    set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allAtomMatchers;
-    vector<sp<ConditionTracker>> allConditionTrackers;
-    vector<sp<MetricProducer>> allMetricProducers;
-    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
-    std::vector<sp<AlarmTracker>> allAlarmTrackers;
-    unordered_map<int, std::vector<int>> conditionToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToConditionMap;
-    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
-    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
-    unordered_map<int64_t, int> alertTrackerMap;
-    vector<int> metricsWithActivation;
-    std::set<int64_t> noReportMetricIds;
-
-    EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
-                                 periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
-                                 allAtomMatchers, allConditionTrackers, allMetricProducers,
-                                 allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                 trackerToMetricMap, trackerToConditionMap,
-                                 activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                 alertTrackerMap, metricsWithActivation,
-                                 noReportMetricIds));
-    ASSERT_EQ(1u, allMetricProducers.size());
-    ASSERT_EQ(1u, allAnomalyTrackers.size());
-    ASSERT_EQ(1u, noReportMetricIds.size());
-    ASSERT_EQ(1u, alertTrackerMap.size());
-    EXPECT_NE(alertTrackerMap.find(kAlertId), alertTrackerMap.end());
-    EXPECT_EQ(alertTrackerMap.find(kAlertId)->second, 0);
-}
-
-TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
-    UidMap uidMap;
-    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-    sp<AlarmMonitor> anomalyAlarmMonitor;
-    sp<AlarmMonitor> periodicAlarmMonitor;
-    StatsdConfig config = buildDimensionMetricsWithMultiTags();
-    set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allAtomMatchers;
-    vector<sp<ConditionTracker>> allConditionTrackers;
-    vector<sp<MetricProducer>> allMetricProducers;
-    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
-    std::vector<sp<AlarmTracker>> allAlarmTrackers;
-    unordered_map<int, std::vector<int>> conditionToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToConditionMap;
-    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
-    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
-    unordered_map<int64_t, int> alertTrackerMap;
-    vector<int> metricsWithActivation;
-    std::set<int64_t> noReportMetricIds;
-
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
-                                  periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
-                                  allAtomMatchers, allConditionTrackers, allMetricProducers,
-                                  allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap,
-                                  activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                  alertTrackerMap, metricsWithActivation,
-                                  noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
-    UidMap uidMap;
-    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-    sp<AlarmMonitor> anomalyAlarmMonitor;
-    sp<AlarmMonitor> periodicAlarmMonitor;
-    StatsdConfig config = buildCircleMatchers();
-    set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allAtomMatchers;
-    vector<sp<ConditionTracker>> allConditionTrackers;
-    vector<sp<MetricProducer>> allMetricProducers;
-    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
-    std::vector<sp<AlarmTracker>> allAlarmTrackers;
-    unordered_map<int, std::vector<int>> conditionToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToConditionMap;
-    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
-    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
-    unordered_map<int64_t, int> alertTrackerMap;
-    vector<int> metricsWithActivation;
-    std::set<int64_t> noReportMetricIds;
-
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
-                                  periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
-                                  allAtomMatchers, allConditionTrackers, allMetricProducers,
-                                  allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap,
-                                  activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                  alertTrackerMap, metricsWithActivation,
-                                  noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, TestMissingMatchers) {
-    UidMap uidMap;
-    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-    sp<AlarmMonitor> anomalyAlarmMonitor;
-    sp<AlarmMonitor> periodicAlarmMonitor;
-    StatsdConfig config = buildMissingMatchers();
-    set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allAtomMatchers;
-    vector<sp<ConditionTracker>> allConditionTrackers;
-    vector<sp<MetricProducer>> allMetricProducers;
-    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
-    std::vector<sp<AlarmTracker>> allAlarmTrackers;
-    unordered_map<int, std::vector<int>> conditionToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToConditionMap;
-    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
-    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
-    unordered_map<int64_t, int> alertTrackerMap;
-    vector<int> metricsWithActivation;
-    std::set<int64_t> noReportMetricIds;
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
-                                  periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
-                                  allAtomMatchers, allConditionTrackers, allMetricProducers,
-                                  allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap,
-                                  activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                  alertTrackerMap, metricsWithActivation,
-                                  noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, TestMissingPredicate) {
-    UidMap uidMap;
-    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-    sp<AlarmMonitor> anomalyAlarmMonitor;
-    sp<AlarmMonitor> periodicAlarmMonitor;
-    StatsdConfig config = buildMissingPredicate();
-    set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allAtomMatchers;
-    vector<sp<ConditionTracker>> allConditionTrackers;
-    vector<sp<MetricProducer>> allMetricProducers;
-    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
-    std::vector<sp<AlarmTracker>> allAlarmTrackers;
-    unordered_map<int, std::vector<int>> conditionToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToConditionMap;
-    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
-    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
-    unordered_map<int64_t, int> alertTrackerMap;
-    vector<int> metricsWithActivation;
-    std::set<int64_t> noReportMetricIds;
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
-                                  periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
-                                  allAtomMatchers, allConditionTrackers, allMetricProducers,
-                                  allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap,
-                                  activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                  alertTrackerMap, metricsWithActivation, noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, TestCirclePredicateDependency) {
-    UidMap uidMap;
-    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-    sp<AlarmMonitor> anomalyAlarmMonitor;
-    sp<AlarmMonitor> periodicAlarmMonitor;
-    StatsdConfig config = buildCirclePredicates();
-    set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allAtomMatchers;
-    vector<sp<ConditionTracker>> allConditionTrackers;
-    vector<sp<MetricProducer>> allMetricProducers;
-    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
-    std::vector<sp<AlarmTracker>> allAlarmTrackers;
-    unordered_map<int, std::vector<int>> conditionToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToConditionMap;
-    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
-    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
-    unordered_map<int64_t, int> alertTrackerMap;
-    vector<int> metricsWithActivation;
-    std::set<int64_t> noReportMetricIds;
-
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
-                                  periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
-                                  allAtomMatchers, allConditionTrackers, allMetricProducers,
-                                  allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap,
-                                  activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                  alertTrackerMap, metricsWithActivation,
-                                  noReportMetricIds));
-}
-
-TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
-    UidMap uidMap;
-    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-    sp<AlarmMonitor> anomalyAlarmMonitor;
-    sp<AlarmMonitor> periodicAlarmMonitor;
-    StatsdConfig config = buildAlertWithUnknownMetric();
-    set<int> allTagIds;
-    vector<sp<LogMatchingTracker>> allAtomMatchers;
-    vector<sp<ConditionTracker>> allConditionTrackers;
-    vector<sp<MetricProducer>> allMetricProducers;
-    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
-    std::vector<sp<AlarmTracker>> allAlarmTrackers;
-    unordered_map<int, std::vector<int>> conditionToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToMetricMap;
-    unordered_map<int, std::vector<int>> trackerToConditionMap;
-    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
-    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
-    unordered_map<int64_t, int> alertTrackerMap;
-    vector<int> metricsWithActivation;
-    std::set<int64_t> noReportMetricIds;
-
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor,
-                                  periodicAlarmMonitor, timeBaseSec, timeBaseSec, allTagIds,
-                                  allAtomMatchers, allConditionTrackers, allMetricProducers,
-                                  allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap,
-                                  activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
-                                  alertTrackerMap, metricsWithActivation,
-                                  noReportMetricIds));
-}
-
 TEST(MetricsManagerTest, TestLogSources) {
     string app1 = "app1";
     set<int32_t> app1Uids = {1111, 11111};
@@ -660,9 +115,7 @@
     pkgToUids[app2] = app2Uids;
     pkgToUids[app3] = app3Uids;
 
-    int32_t atom1 = 10;
-    int32_t atom2 = 20;
-    int32_t atom3 = 30;
+    int32_t atom1 = 10, atom2 = 20, atom3 = 30;
     sp<MockUidMap> uidMap = new StrictMock<MockUidMap>();
     EXPECT_CALL(*uidMap, getAppUid(_))
             .Times(4)
@@ -680,7 +133,7 @@
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> periodicAlarmMonitor;
 
-    StatsdConfig config = buildGoodConfig();
+    StatsdConfig config;
     config.add_allowed_log_source("AID_SYSTEM");
     config.add_allowed_log_source(app1);
     config.add_default_pull_packages("AID_SYSTEM");
@@ -700,42 +153,115 @@
 
     MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap,
                                   pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor);
-
     EXPECT_TRUE(metricsManager.isConfigValid());
 
-    ASSERT_EQ(metricsManager.mAllowedUid.size(), 1);
-    EXPECT_EQ(metricsManager.mAllowedUid[0], AID_SYSTEM);
-
-    ASSERT_EQ(metricsManager.mAllowedPkg.size(), 1);
-    EXPECT_EQ(metricsManager.mAllowedPkg[0], app1);
-
-    ASSERT_EQ(metricsManager.mAllowedLogSources.size(), 3);
-    EXPECT_TRUE(isSubset({AID_SYSTEM}, metricsManager.mAllowedLogSources));
-    EXPECT_TRUE(isSubset(app1Uids, metricsManager.mAllowedLogSources));
-
-    ASSERT_EQ(metricsManager.mDefaultPullUids.size(), 2);
-    EXPECT_TRUE(isSubset(defaultPullUids, metricsManager.mDefaultPullUids));
-    ;
+    EXPECT_THAT(metricsManager.mAllowedUid, ElementsAre(AID_SYSTEM));
+    EXPECT_THAT(metricsManager.mAllowedPkg, ElementsAre(app1));
+    EXPECT_THAT(metricsManager.mAllowedLogSources,
+                ContainerEq(unionSet(vector<set<int32_t>>({app1Uids, {AID_SYSTEM}}))));
+    EXPECT_THAT(metricsManager.mDefaultPullUids, ContainerEq(defaultPullUids));
 
     vector<int32_t> atom1Uids = metricsManager.getPullAtomUids(atom1);
-    ASSERT_EQ(atom1Uids.size(), 5);
-    set<int32_t> expectedAtom1Uids;
-    expectedAtom1Uids.insert(defaultPullUids.begin(), defaultPullUids.end());
-    expectedAtom1Uids.insert(app1Uids.begin(), app1Uids.end());
-    expectedAtom1Uids.insert(app3Uids.begin(), app3Uids.end());
-    EXPECT_TRUE(isSubset(expectedAtom1Uids, set<int32_t>(atom1Uids.begin(), atom1Uids.end())));
+    EXPECT_THAT(atom1Uids,
+                UnorderedElementsAreArray(unionSet({defaultPullUids, app1Uids, app3Uids})));
 
     vector<int32_t> atom2Uids = metricsManager.getPullAtomUids(atom2);
-    ASSERT_EQ(atom2Uids.size(), 4);
-    set<int32_t> expectedAtom2Uids;
-    expectedAtom1Uids.insert(defaultPullUids.begin(), defaultPullUids.end());
-    expectedAtom1Uids.insert(app2Uids.begin(), app2Uids.end());
-    expectedAtom1Uids.insert(AID_STATSD);
-    EXPECT_TRUE(isSubset(expectedAtom2Uids, set<int32_t>(atom2Uids.begin(), atom2Uids.end())));
+    EXPECT_THAT(atom2Uids,
+                UnorderedElementsAreArray(unionSet({defaultPullUids, app2Uids, {AID_STATSD}})));
 
     vector<int32_t> atom3Uids = metricsManager.getPullAtomUids(atom3);
-    ASSERT_EQ(atom3Uids.size(), 2);
-    EXPECT_TRUE(isSubset(defaultPullUids, set<int32_t>(atom3Uids.begin(), atom3Uids.end())));
+    EXPECT_THAT(atom3Uids, UnorderedElementsAreArray(defaultPullUids));
+}
+
+TEST(MetricsManagerTest, TestLogSourcesOnConfigUpdate) {
+    string app1 = "app1";
+    set<int32_t> app1Uids = {1111, 11111};
+    string app2 = "app2";
+    set<int32_t> app2Uids = {2222};
+    string app3 = "app3";
+    set<int32_t> app3Uids = {3333, 1111};
+
+    map<string, set<int32_t>> pkgToUids;
+    pkgToUids[app1] = app1Uids;
+    pkgToUids[app2] = app2Uids;
+    pkgToUids[app3] = app3Uids;
+
+    int32_t atom1 = 10, atom2 = 20, atom3 = 30;
+    sp<MockUidMap> uidMap = new StrictMock<MockUidMap>();
+    EXPECT_CALL(*uidMap, getAppUid(_))
+            .Times(8)
+            .WillRepeatedly(Invoke([&pkgToUids](const string& pkg) {
+                const auto& it = pkgToUids.find(pkg);
+                if (it != pkgToUids.end()) {
+                    return it->second;
+                }
+                return set<int32_t>();
+            }));
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, RegisterPullUidProvider(kConfigKey, _)).Times(1);
+    EXPECT_CALL(*pullerManager, UnregisterPullUidProvider(kConfigKey, _)).Times(1);
+
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
+
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_SYSTEM");
+    config.add_allowed_log_source(app1);
+    config.add_default_pull_packages("AID_SYSTEM");
+    config.add_default_pull_packages("AID_ROOT");
+
+    PullAtomPackages* pullAtomPackages = config.add_pull_atom_packages();
+    pullAtomPackages->set_atom_id(atom1);
+    pullAtomPackages->add_packages(app1);
+    pullAtomPackages->add_packages(app3);
+
+    pullAtomPackages = config.add_pull_atom_packages();
+    pullAtomPackages->set_atom_id(atom2);
+    pullAtomPackages->add_packages(app2);
+    pullAtomPackages->add_packages("AID_STATSD");
+
+    MetricsManager metricsManager(kConfigKey, config, timeBaseSec, timeBaseSec, uidMap,
+                                  pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor);
+    EXPECT_TRUE(metricsManager.isConfigValid());
+
+    // Update with new allowed log sources.
+    StatsdConfig newConfig;
+    newConfig.add_allowed_log_source("AID_ROOT");
+    newConfig.add_allowed_log_source(app2);
+    newConfig.add_default_pull_packages("AID_SYSTEM");
+    newConfig.add_default_pull_packages("AID_STATSD");
+
+    pullAtomPackages = newConfig.add_pull_atom_packages();
+    pullAtomPackages->set_atom_id(atom2);
+    pullAtomPackages->add_packages(app1);
+    pullAtomPackages->add_packages(app3);
+
+    pullAtomPackages = newConfig.add_pull_atom_packages();
+    pullAtomPackages->set_atom_id(atom3);
+    pullAtomPackages->add_packages(app2);
+    pullAtomPackages->add_packages("AID_ADB");
+
+    metricsManager.updateConfig(newConfig, timeBaseSec, timeBaseSec, anomalyAlarmMonitor,
+                                periodicAlarmMonitor);
+    EXPECT_TRUE(metricsManager.isConfigValid());
+
+    EXPECT_THAT(metricsManager.mAllowedUid, ElementsAre(AID_ROOT));
+    EXPECT_THAT(metricsManager.mAllowedPkg, ElementsAre(app2));
+    EXPECT_THAT(metricsManager.mAllowedLogSources,
+                ContainerEq(unionSet(vector<set<int32_t>>({app2Uids, {AID_ROOT}}))));
+    const set<int32_t> defaultPullUids = {AID_SYSTEM, AID_STATSD};
+    EXPECT_THAT(metricsManager.mDefaultPullUids, ContainerEq(defaultPullUids));
+
+    vector<int32_t> atom1Uids = metricsManager.getPullAtomUids(atom1);
+    EXPECT_THAT(atom1Uids, UnorderedElementsAreArray(defaultPullUids));
+
+    vector<int32_t> atom2Uids = metricsManager.getPullAtomUids(atom2);
+    EXPECT_THAT(atom2Uids,
+                UnorderedElementsAreArray(unionSet({defaultPullUids, app1Uids, app3Uids})));
+
+    vector<int32_t> atom3Uids = metricsManager.getPullAtomUids(atom3);
+    EXPECT_THAT(atom3Uids,
+                UnorderedElementsAreArray(unionSet({defaultPullUids, app2Uids, {AID_ADB}})));
 }
 
 TEST(MetricsManagerTest, TestCheckLogCredentialsWhitelistedAtom) {
@@ -744,7 +270,7 @@
     sp<AlarmMonitor> anomalyAlarmMonitor;
     sp<AlarmMonitor> periodicAlarmMonitor;
 
-    StatsdConfig config = buildGoodConfig();
+    StatsdConfig config;
     config.add_whitelisted_atom_ids(3);
     config.add_whitelisted_atom_ids(4);
 
diff --git a/bin/tests/StatsLogProcessor_test.cpp b/bin/tests/StatsLogProcessor_test.cpp
index 6e304b5..26b64f8 100644
--- a/bin/tests/StatsLogProcessor_test.cpp
+++ b/bin/tests/StatsLogProcessor_test.cpp
@@ -896,8 +896,8 @@
     EXPECT_TRUE(metricProducer2->isActive());
 
     int i = 0;
-    for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
-        if (metricsManager1->mAllAtomMatchers[i]->getId() ==
+    for (; i < metricsManager1->mAllAtomMatchingTrackers.size(); i++) {
+        if (metricsManager1->mAllAtomMatchingTrackers[i]->getId() ==
             metric1ActivationTrigger1->atom_matcher_id()) {
             break;
         }
@@ -908,8 +908,8 @@
     EXPECT_EQ(kNotActive, activation1->state);
 
     i = 0;
-    for (; i < metricsManager1->mAllAtomMatchers.size(); i++) {
-        if (metricsManager1->mAllAtomMatchers[i]->getId() ==
+    for (; i < metricsManager1->mAllAtomMatchingTrackers.size(); i++) {
+        if (metricsManager1->mAllAtomMatchingTrackers[i]->getId() ==
             metric1ActivationTrigger2->atom_matcher_id()) {
             break;
         }
@@ -981,8 +981,8 @@
     EXPECT_TRUE(metricProducer1002->isActive());
 
     i = 0;
-    for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
-        if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
+    for (; i < metricsManager1001->mAllAtomMatchingTrackers.size(); i++) {
+        if (metricsManager1001->mAllAtomMatchingTrackers[i]->getId() ==
             metric1ActivationTrigger1->atom_matcher_id()) {
             break;
         }
@@ -993,8 +993,8 @@
     EXPECT_EQ(kNotActive, activation1001_1->state);
 
     i = 0;
-    for (; i < metricsManager1001->mAllAtomMatchers.size(); i++) {
-        if (metricsManager1001->mAllAtomMatchers[i]->getId() ==
+    for (; i < metricsManager1001->mAllAtomMatchingTrackers.size(); i++) {
+        if (metricsManager1001->mAllAtomMatchingTrackers[i]->getId() ==
             metric1ActivationTrigger2->atom_matcher_id()) {
             break;
         }
@@ -1082,8 +1082,8 @@
     EXPECT_TRUE(metricProducerTimeBase3_2->isActive());
 
     i = 0;
-    for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
-        if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
+    for (; i < metricsManagerTimeBase3->mAllAtomMatchingTrackers.size(); i++) {
+        if (metricsManagerTimeBase3->mAllAtomMatchingTrackers[i]->getId() ==
             metric1ActivationTrigger1->atom_matcher_id()) {
             break;
         }
@@ -1094,8 +1094,8 @@
     EXPECT_EQ(kNotActive, activationTimeBase3_1->state);
 
     i = 0;
-    for (; i < metricsManagerTimeBase3->mAllAtomMatchers.size(); i++) {
-        if (metricsManagerTimeBase3->mAllAtomMatchers[i]->getId() ==
+    for (; i < metricsManagerTimeBase3->mAllAtomMatchingTrackers.size(); i++) {
+        if (metricsManagerTimeBase3->mAllAtomMatchingTrackers[i]->getId() ==
             metric1ActivationTrigger2->atom_matcher_id()) {
             break;
         }
@@ -1184,8 +1184,8 @@
     EXPECT_TRUE(metricProducerTimeBase4_2->isActive());
 
     i = 0;
-    for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) {
-        if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() ==
+    for (; i < metricsManagerTimeBase4->mAllAtomMatchingTrackers.size(); i++) {
+        if (metricsManagerTimeBase4->mAllAtomMatchingTrackers[i]->getId() ==
             metric1ActivationTrigger1->atom_matcher_id()) {
             break;
         }
@@ -1196,8 +1196,8 @@
     EXPECT_EQ(kNotActive, activationTimeBase4_1->state);
 
     i = 0;
-    for (; i < metricsManagerTimeBase4->mAllAtomMatchers.size(); i++) {
-        if (metricsManagerTimeBase4->mAllAtomMatchers[i]->getId() ==
+    for (; i < metricsManagerTimeBase4->mAllAtomMatchingTrackers.size(); i++) {
+        if (metricsManagerTimeBase4->mAllAtomMatchingTrackers[i]->getId() ==
             metric1ActivationTrigger2->atom_matcher_id()) {
             break;
         }
@@ -1585,8 +1585,8 @@
     EXPECT_TRUE(metricProducer3->isActive());
 
     // Check event activations.
-    ASSERT_EQ(metricsManager1->mAllAtomMatchers.size(), 4);
-    EXPECT_EQ(metricsManager1->mAllAtomMatchers[0]->getId(),
+    ASSERT_EQ(metricsManager1->mAllAtomMatchingTrackers.size(), 4);
+    EXPECT_EQ(metricsManager1->mAllAtomMatchingTrackers[0]->getId(),
               metric1ActivationTrigger1->atom_matcher_id());
     const auto& activation1 = metricProducer1->mEventActivationMap.at(0);
     EXPECT_EQ(100 * NS_PER_SEC, activation1->ttl_ns);
@@ -1594,7 +1594,7 @@
     EXPECT_EQ(kNotActive, activation1->state);
     EXPECT_EQ(ACTIVATE_ON_BOOT, activation1->activationType);
 
-    EXPECT_EQ(metricsManager1->mAllAtomMatchers[1]->getId(),
+    EXPECT_EQ(metricsManager1->mAllAtomMatchingTrackers[1]->getId(),
               metric1ActivationTrigger2->atom_matcher_id());
     const auto& activation2 = metricProducer1->mEventActivationMap.at(1);
     EXPECT_EQ(200 * NS_PER_SEC, activation2->ttl_ns);
@@ -1602,7 +1602,7 @@
     EXPECT_EQ(kNotActive, activation2->state);
     EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation2->activationType);
 
-    EXPECT_EQ(metricsManager1->mAllAtomMatchers[2]->getId(),
+    EXPECT_EQ(metricsManager1->mAllAtomMatchingTrackers[2]->getId(),
               metric2ActivationTrigger1->atom_matcher_id());
     const auto& activation3 = metricProducer2->mEventActivationMap.at(2);
     EXPECT_EQ(100 * NS_PER_SEC, activation3->ttl_ns);
@@ -1610,7 +1610,7 @@
     EXPECT_EQ(kNotActive, activation3->state);
     EXPECT_EQ(ACTIVATE_ON_BOOT, activation3->activationType);
 
-    EXPECT_EQ(metricsManager1->mAllAtomMatchers[3]->getId(),
+    EXPECT_EQ(metricsManager1->mAllAtomMatchingTrackers[3]->getId(),
               metric2ActivationTrigger2->atom_matcher_id());
     const auto& activation4 = metricProducer2->mEventActivationMap.at(3);
     EXPECT_EQ(200 * NS_PER_SEC, activation4->ttl_ns);
@@ -1685,8 +1685,8 @@
     // Activation 1 is kActiveOnBoot.
     // Activation 2 and 3 are not active.
     // Activation 4 is active.
-    ASSERT_EQ(metricsManager2->mAllAtomMatchers.size(), 4);
-    EXPECT_EQ(metricsManager2->mAllAtomMatchers[0]->getId(),
+    ASSERT_EQ(metricsManager2->mAllAtomMatchingTrackers.size(), 4);
+    EXPECT_EQ(metricsManager2->mAllAtomMatchingTrackers[0]->getId(),
               metric1ActivationTrigger1->atom_matcher_id());
     const auto& activation1001 = metricProducer1001->mEventActivationMap.at(0);
     EXPECT_EQ(100 * NS_PER_SEC, activation1001->ttl_ns);
@@ -1694,7 +1694,7 @@
     EXPECT_EQ(kActiveOnBoot, activation1001->state);
     EXPECT_EQ(ACTIVATE_ON_BOOT, activation1001->activationType);
 
-    EXPECT_EQ(metricsManager2->mAllAtomMatchers[1]->getId(),
+    EXPECT_EQ(metricsManager2->mAllAtomMatchingTrackers[1]->getId(),
               metric1ActivationTrigger2->atom_matcher_id());
     const auto& activation1002 = metricProducer1001->mEventActivationMap.at(1);
     EXPECT_EQ(200 * NS_PER_SEC, activation1002->ttl_ns);
@@ -1702,7 +1702,7 @@
     EXPECT_EQ(kNotActive, activation1002->state);
     EXPECT_EQ(ACTIVATE_IMMEDIATELY, activation1002->activationType);
 
-    EXPECT_EQ(metricsManager2->mAllAtomMatchers[2]->getId(),
+    EXPECT_EQ(metricsManager2->mAllAtomMatchingTrackers[2]->getId(),
               metric2ActivationTrigger1->atom_matcher_id());
     const auto& activation1003 = metricProducer1002->mEventActivationMap.at(2);
     EXPECT_EQ(100 * NS_PER_SEC, activation1003->ttl_ns);
@@ -1710,7 +1710,7 @@
     EXPECT_EQ(kNotActive, activation1003->state);
     EXPECT_EQ(ACTIVATE_ON_BOOT, activation1003->activationType);
 
-    EXPECT_EQ(metricsManager2->mAllAtomMatchers[3]->getId(),
+    EXPECT_EQ(metricsManager2->mAllAtomMatchingTrackers[3]->getId(),
               metric2ActivationTrigger2->atom_matcher_id());
     const auto& activation1004 = metricProducer1002->mEventActivationMap.at(3);
     EXPECT_EQ(200 * NS_PER_SEC, activation1004->ttl_ns);
diff --git a/bin/tests/condition/ConditionTimer_test.cpp b/bin/tests/condition/ConditionTimer_test.cpp
index ea02cd3..46dc9a9 100644
--- a/bin/tests/condition/ConditionTimer_test.cpp
+++ b/bin/tests/condition/ConditionTimer_test.cpp
@@ -35,11 +35,11 @@
     EXPECT_EQ(0, timer.mTimerNs);
 
     timer.onConditionChanged(true, ct_start_time + 5);
-    EXPECT_EQ(ct_start_time + 5, timer.mLastConditionTrueTimestampNs);
+    EXPECT_EQ(ct_start_time + 5, timer.mLastConditionChangeTimestampNs);
     EXPECT_EQ(true, timer.mCondition);
 
     EXPECT_EQ(95, timer.newBucketStart(ct_start_time + 100));
-    EXPECT_EQ(ct_start_time + 100, timer.mLastConditionTrueTimestampNs);
+    EXPECT_EQ(ct_start_time + 100, timer.mLastConditionChangeTimestampNs);
     EXPECT_EQ(true, timer.mCondition);
 }
 
@@ -51,7 +51,7 @@
     EXPECT_EQ(ct_start_time - time_base, timer.newBucketStart(ct_start_time));
     EXPECT_EQ(true, timer.mCondition);
     EXPECT_EQ(0, timer.mTimerNs);
-    EXPECT_EQ(ct_start_time, timer.mLastConditionTrueTimestampNs);
+    EXPECT_EQ(ct_start_time, timer.mLastConditionChangeTimestampNs);
 
     timer.onConditionChanged(false, ct_start_time + 5);
     EXPECT_EQ(5, timer.mTimerNs);
diff --git a/bin/tests/condition/SimpleConditionTracker_test.cpp b/bin/tests/condition/SimpleConditionTracker_test.cpp
index 07b5311..8998b5f 100644
--- a/bin/tests/condition/SimpleConditionTracker_test.cpp
+++ b/bin/tests/condition/SimpleConditionTracker_test.cpp
@@ -39,6 +39,7 @@
 const int ATTRIBUTION_NODE_FIELD_ID = 1;
 const int ATTRIBUTION_UID_FIELD_ID = 1;
 const int TAG_ID = 1;
+const uint64_t protoHash = 0x123456789;
 
 SimplePredicate getWakeLockHeldCondition(bool countNesting, bool defaultFalse,
                                          bool outputSlicedUid, Position position) {
@@ -123,7 +124,7 @@
     trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
     trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
 
-    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"),
+    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash,
                                             0 /*tracker index*/, simplePredicate,
                                             trackerNameIndexMap);
 
@@ -177,7 +178,7 @@
     trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
     trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
 
-    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"),
+    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash,
                                             0 /*tracker index*/, simplePredicate,
                                             trackerNameIndexMap);
 
@@ -231,8 +232,9 @@
     trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
     trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
 
-    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), 0 /*tracker index*/,
-                                            simplePredicate, trackerNameIndexMap);
+    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash,
+                                            0 /*tracker index*/, simplePredicate,
+                                            trackerNameIndexMap);
     EXPECT_FALSE(conditionTracker.isSliced());
 
     // This event is not accessed in this test besides dimensions which is why this is okay.
@@ -317,7 +319,7 @@
     trackerNameIndexMap[StringToId("SCREEN_TURNED_ON")] = 0;
     trackerNameIndexMap[StringToId("SCREEN_TURNED_OFF")] = 1;
 
-    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"),
+    SimpleConditionTracker conditionTracker(kConfigKey, StringToId("SCREEN_IS_ON"), protoHash,
                                             0 /*condition tracker index*/, simplePredicate,
                                             trackerNameIndexMap);
     EXPECT_FALSE(conditionTracker.isSliced());
@@ -392,7 +394,7 @@
         trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
         trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
 
-        SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
+        SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), protoHash,
                                                 0 /*condition tracker index*/, simplePredicate,
                                                 trackerNameIndexMap);
 
@@ -514,7 +516,7 @@
     trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
     trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
 
-    SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
+    SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), protoHash,
                                             0 /*condition tracker index*/, simplePredicate,
                                             trackerNameIndexMap);
 
@@ -610,7 +612,7 @@
         trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
         trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
 
-        SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
+        SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName), protoHash,
                                                 0 /*condition tracker index*/, simplePredicate,
                                                 trackerNameIndexMap);
 
diff --git a/bin/tests/e2e/ConfigUpdate_e2e_ab_test.cpp b/bin/tests/e2e/ConfigUpdate_e2e_ab_test.cpp
new file mode 100644
index 0000000..098f284
--- /dev/null
+++ b/bin/tests/e2e/ConfigUpdate_e2e_ab_test.cpp
@@ -0,0 +1,328 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include "flags/flags.h"
+#include "src/StatsLogProcessor.h"
+#include "src/storage/StorageManager.h"
+#include "tests/statsd_test_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+#define STATS_DATA_DIR "/data/misc/stats-data"
+
+using android::base::SetProperty;
+using android::base::StringPrintf;
+using namespace std;
+
+namespace {
+
+StatsdConfig CreateSimpleConfig() {
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_STATSD");
+    config.set_hash_strings_in_metric_report(false);
+
+    *config.add_atom_matcher() = CreateBatteryStateUsbMatcher();
+    // Simple count metric so the config isn't empty.
+    CountMetric* countMetric1 = config.add_count_metric();
+    countMetric1->set_id(StringToId("Count1"));
+    countMetric1->set_what(config.atom_matcher(0).id());
+    countMetric1->set_bucket(FIVE_MINUTES);
+    return config;
+}
+}  // namespace
+
+// Setup for parameterized tests.
+class ConfigUpdateE2eAbTest : public TestWithParam<bool> {
+private:
+    string originalFlagValue;
+
+public:
+    void SetUp() override {
+        originalFlagValue = getFlagBool(PARTIAL_CONFIG_UPDATE_FLAG, "");
+        string rawFlagName =
+                StringPrintf("persist.device_config.%s.%s", STATSD_NATIVE_NAMESPACE.c_str(),
+                             PARTIAL_CONFIG_UPDATE_FLAG.c_str());
+        SetProperty(rawFlagName, GetParam() ? "true" : "false");
+    }
+
+    void TearDown() override {
+        string rawFlagName =
+                StringPrintf("persist.device_config.%s.%s", STATSD_NATIVE_NAMESPACE.c_str(),
+                             PARTIAL_CONFIG_UPDATE_FLAG.c_str());
+        SetProperty(rawFlagName, originalFlagValue);
+    }
+};
+
+INSTANTIATE_TEST_SUITE_P(ConfigUpdateE2eAbTest, ConfigUpdateE2eAbTest, testing::Bool());
+
+TEST_P(ConfigUpdateE2eAbTest, TestUidMapVersionStringInstaller) {
+    sp<UidMap> uidMap = new UidMap();
+    vector<int32_t> uids({1000});
+    vector<int64_t> versions({1});
+    vector<String16> apps({String16("app1")});
+    vector<String16> versionStrings({String16("v1")});
+    vector<String16> installers({String16("installer1")});
+    uidMap->updateMap(1, uids, versions, versionStrings, apps, installers);
+
+    StatsdConfig config = CreateSimpleConfig();
+    config.set_version_strings_in_metric_report(true);
+    config.set_installer_in_metric_report(false);
+    int64_t baseTimeNs = getElapsedRealtimeNs();
+
+    ConfigKey cfgKey(0, 12345);
+    sp<StatsLogProcessor> processor =
+            CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey, nullptr, 0, uidMap);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+
+    // Now update.
+    config.set_version_strings_in_metric_report(false);
+    config.set_installer_in_metric_report(true);
+    processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_EQ(metricsManager == processor->mMetricsManagers.begin()->second, GetParam());
+    EXPECT_TRUE(metricsManager->isConfigValid());
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    // First report is written to disk when the update happens.
+    ASSERT_EQ(reports.reports_size(), 2);
+    UidMapping uidMapping = reports.reports(1).uid_map();
+    ASSERT_EQ(uidMapping.snapshots_size(), 1);
+    ASSERT_EQ(uidMapping.snapshots(0).package_info_size(), 1);
+    EXPECT_FALSE(uidMapping.snapshots(0).package_info(0).has_version_string());
+    EXPECT_EQ(uidMapping.snapshots(0).package_info(0).installer(), "installer1");
+}
+
+TEST_P(ConfigUpdateE2eAbTest, TestHashStrings) {
+    sp<UidMap> uidMap = new UidMap();
+    vector<int32_t> uids({1000});
+    vector<int64_t> versions({1});
+    vector<String16> apps({String16("app1")});
+    vector<String16> versionStrings({String16("v1")});
+    vector<String16> installers({String16("installer1")});
+    uidMap->updateMap(1, uids, versions, versionStrings, apps, installers);
+
+    StatsdConfig config = CreateSimpleConfig();
+    config.set_version_strings_in_metric_report(true);
+    config.set_hash_strings_in_metric_report(true);
+    int64_t baseTimeNs = getElapsedRealtimeNs();
+
+    ConfigKey cfgKey(0, 12345);
+    sp<StatsLogProcessor> processor =
+            CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey, nullptr, 0, uidMap);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_TRUE(metricsManager->isConfigValid());
+
+    // Now update.
+    config.set_hash_strings_in_metric_report(false);
+    processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_EQ(metricsManager == processor->mMetricsManagers.begin()->second, GetParam());
+    EXPECT_TRUE(metricsManager->isConfigValid());
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    // First report is written to disk when the update happens.
+    ASSERT_EQ(reports.reports_size(), 2);
+    UidMapping uidMapping = reports.reports(1).uid_map();
+    ASSERT_EQ(uidMapping.snapshots_size(), 1);
+    ASSERT_EQ(uidMapping.snapshots(0).package_info_size(), 1);
+    EXPECT_TRUE(uidMapping.snapshots(0).package_info(0).has_version_string());
+    EXPECT_FALSE(uidMapping.snapshots(0).package_info(0).has_version_string_hash());
+}
+
+TEST_P(ConfigUpdateE2eAbTest, TestAnnotations) {
+    StatsdConfig config = CreateSimpleConfig();
+    StatsdConfig_Annotation* annotation = config.add_annotation();
+    annotation->set_field_int64(11);
+    annotation->set_field_int32(1);
+    int64_t baseTimeNs = getElapsedRealtimeNs();
+    ConfigKey cfgKey(0, 12345);
+    sp<StatsLogProcessor> processor =
+            CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
+
+    // Now update
+    config.clear_annotation();
+    annotation = config.add_annotation();
+    annotation->set_field_int64(22);
+    annotation->set_field_int32(2);
+    processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    // First report is written to disk when the update happens.
+    ASSERT_EQ(reports.reports_size(), 2);
+    ConfigMetricsReport report = reports.reports(1);
+    EXPECT_EQ(report.annotation_size(), 1);
+    EXPECT_EQ(report.annotation(0).field_int64(), 22);
+    EXPECT_EQ(report.annotation(0).field_int32(), 2);
+}
+
+TEST_P(ConfigUpdateE2eAbTest, TestPersistLocally) {
+    StatsdConfig config = CreateSimpleConfig();
+    config.set_persist_locally(false);
+    int64_t baseTimeNs = getElapsedRealtimeNs();
+    ConfigKey cfgKey(0, 12345);
+    sp<StatsLogProcessor> processor =
+            CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    ASSERT_EQ(reports.reports_size(), 1);
+    // Number of reports should still be 1 since persist_locally is false.
+    reports.Clear();
+    buffer.clear();
+    processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    ASSERT_EQ(reports.reports_size(), 1);
+
+    // Now update.
+    config.set_persist_locally(true);
+    processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config);
+
+    // Should get 2: 1 in memory + 1 on disk. Both should be saved on disk.
+    reports.Clear();
+    buffer.clear();
+    processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    ASSERT_EQ(reports.reports_size(), 2);
+    // Should get 3, 2 on disk + 1 in memory.
+    reports.Clear();
+    buffer.clear();
+    processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    ASSERT_EQ(reports.reports_size(), 3);
+    string suffix = StringPrintf("%d_%lld", cfgKey.GetUid(), (long long)cfgKey.GetId());
+    StorageManager::deleteSuffixedFiles(STATS_DATA_DIR, suffix.c_str());
+    string historySuffix =
+            StringPrintf("%d_%lld_history", cfgKey.GetUid(), (long long)cfgKey.GetId());
+    StorageManager::deleteSuffixedFiles(STATS_DATA_DIR, historySuffix.c_str());
+}
+
+TEST_P(ConfigUpdateE2eAbTest, TestNoReportMetrics) {
+    StatsdConfig config = CreateSimpleConfig();
+    // Second simple count metric.
+    CountMetric* countMetric = config.add_count_metric();
+    countMetric->set_id(StringToId("Count2"));
+    countMetric->set_what(config.atom_matcher(0).id());
+    countMetric->set_bucket(FIVE_MINUTES);
+    config.add_no_report_metric(config.count_metric(0).id());
+    int64_t baseTimeNs = getElapsedRealtimeNs();
+    ConfigKey cfgKey(0, 12345);
+    sp<StatsLogProcessor> processor =
+            CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
+
+    // Now update.
+    config.clear_no_report_metric();
+    config.add_no_report_metric(config.count_metric(1).id());
+    processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, baseTimeNs + 1001, false, true, ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    // First report is written to disk when the update happens.
+    ASSERT_EQ(reports.reports_size(), 2);
+    // First report (before update) has the first count metric.
+    ASSERT_EQ(reports.reports(0).metrics_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics(0).metric_id(), config.count_metric(1).id());
+    // Second report (after update) has the first count metric.
+    ASSERT_EQ(reports.reports(1).metrics_size(), 1);
+    EXPECT_EQ(reports.reports(1).metrics(0).metric_id(), config.count_metric(0).id());
+}
+
+TEST_P(ConfigUpdateE2eAbTest, TestAtomsAllowedFromAnyUid) {
+    StatsdConfig config = CreateSimpleConfig();
+    int64_t baseTimeNs = getElapsedRealtimeNs();
+    ConfigKey cfgKey(0, 12345);
+    sp<StatsLogProcessor> processor =
+            CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
+    // Uses AID_ROOT, which isn't in allowed log sources.
+    unique_ptr<LogEvent> event = CreateBatteryStateChangedEvent(
+            baseTimeNs + 2, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB);
+    processor->OnLogEvent(event.get());
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, baseTimeNs + 1001, true, true, ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    ASSERT_EQ(reports.reports_size(), 1);
+    // Check the metric and make sure it has 0 count.
+    ASSERT_EQ(reports.reports(0).metrics_size(), 1);
+    EXPECT_FALSE(reports.reports(0).metrics(0).has_count_metrics());
+
+    // Now update. Allow plugged state to be logged from any uid, so the atom will be counted.
+    config.add_whitelisted_atom_ids(util::PLUGGED_STATE_CHANGED);
+    processor->OnConfigUpdated(baseTimeNs + 1000, cfgKey, config);
+    unique_ptr<LogEvent> event2 = CreateBatteryStateChangedEvent(
+            baseTimeNs + 2000, BatteryPluggedStateEnum::BATTERY_PLUGGED_USB);
+    processor->OnLogEvent(event.get());
+    reports.Clear();
+    buffer.clear();
+    processor->onDumpReport(cfgKey, baseTimeNs + 3000, true, true, ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    ASSERT_EQ(reports.reports_size(), 2);
+    // Check the metric and make sure it has 0 count.
+    ASSERT_EQ(reports.reports(1).metrics_size(), 1);
+    EXPECT_TRUE(reports.reports(1).metrics(0).has_count_metrics());
+    ASSERT_EQ(reports.reports(1).metrics(0).count_metrics().data_size(), 1);
+    ASSERT_EQ(reports.reports(1).metrics(0).count_metrics().data(0).bucket_info_size(), 1);
+    EXPECT_EQ(reports.reports(1).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1);
+}
+
+TEST_P(ConfigUpdateE2eAbTest, TestConfigTtl) {
+    StatsdConfig config = CreateSimpleConfig();
+    config.set_ttl_in_seconds(1);
+    int64_t baseTimeNs = getElapsedRealtimeNs();
+    ConfigKey cfgKey(0, 12345);
+    sp<StatsLogProcessor> processor =
+            CreateStatsLogProcessor(baseTimeNs, baseTimeNs, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    sp<MetricsManager> metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_EQ(metricsManager->getTtlEndNs(), baseTimeNs + NS_PER_SEC);
+
+    config.set_ttl_in_seconds(5);
+    processor->OnConfigUpdated(baseTimeNs + 2 * NS_PER_SEC, cfgKey, config);
+    metricsManager = processor->mMetricsManagers.begin()->second;
+    EXPECT_EQ(metricsManager->getTtlEndNs(), baseTimeNs + 7 * NS_PER_SEC);
+
+    // Clear the data stored on disk as a result of the update.
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, baseTimeNs + 3 * NS_PER_SEC, false, true, ADB_DUMP, FAST,
+                            &buffer);
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/bin/tests/e2e/ConfigUpdate_e2e_test.cpp b/bin/tests/e2e/ConfigUpdate_e2e_test.cpp
new file mode 100644
index 0000000..94b778c
--- /dev/null
+++ b/bin/tests/e2e/ConfigUpdate_e2e_test.cpp
@@ -0,0 +1,358 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include <android-base/properties.h>
+#include <android-base/stringprintf.h>
+#include <gtest/gtest.h>
+
+#include "flags/flags.h"
+#include "src/StatsLogProcessor.h"
+#include "src/storage/StorageManager.h"
+#include "tests/statsd_test_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+using android::base::SetProperty;
+using android::base::StringPrintf;
+using namespace std;
+
+// Tests that only run with the partial config update feature turned on.
+namespace {
+// Setup for test fixture.
+class ConfigUpdateE2eTest : public ::testing::Test {
+private:
+    string originalFlagValue;
+public:
+    void SetUp() override {
+        originalFlagValue = getFlagBool(PARTIAL_CONFIG_UPDATE_FLAG, "");
+        string rawFlagName =
+                StringPrintf("persist.device_config.%s.%s", STATSD_NATIVE_NAMESPACE.c_str(),
+                             PARTIAL_CONFIG_UPDATE_FLAG.c_str());
+        SetProperty(rawFlagName, "true");
+    }
+
+    void TearDown() override {
+        string rawFlagName =
+                StringPrintf("persist.device_config.%s.%s", STATSD_NATIVE_NAMESPACE.c_str(),
+                             PARTIAL_CONFIG_UPDATE_FLAG.c_str());
+        SetProperty(rawFlagName, originalFlagValue);
+    }
+};
+}  // Anonymous namespace.
+
+TEST_F(ConfigUpdateE2eTest, TestNewDurationExistingWhat) {
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");
+    *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+    *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+
+    Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+    *config.add_predicate() = holdingWakelockPredicate;
+
+    ConfigKey key(123, 987);
+    uint64_t bucketStartTimeNs = 10000000000;  // 0:10
+    uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL;
+    sp<StatsLogProcessor> processor =
+            CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key);
+
+    int app1Uid = 123;
+    vector<int> attributionUids1 = {app1Uid};
+    vector<string> attributionTags1 = {"App1"};
+    // Create a wakelock acquire, causing the condition to be true.
+    unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
+                                                            attributionUids1, attributionTags1,
+                                                            "wl1");  // 0:10
+    processor->OnLogEvent(event.get());
+
+    // Add metric.
+    DurationMetric* durationMetric = config.add_duration_metric();
+    durationMetric->set_id(StringToId("WakelockDuration"));
+    durationMetric->set_what(holdingWakelockPredicate.id());
+    durationMetric->set_aggregation_type(DurationMetric::SUM);
+    durationMetric->set_bucket(FIVE_MINUTES);
+
+    uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC;  // 1:00
+    processor->OnConfigUpdated(updateTimeNs, key, config);
+
+    event = CreateReleaseWakelockEvent(bucketStartTimeNs + 80 * NS_PER_SEC, attributionUids1,
+                                       attributionTags1,
+                                       "wl1");  // 1:20
+    processor->OnLogEvent(event.get());
+    uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC;  // 1:30
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    ASSERT_EQ(reports.reports_size(), 1);
+    ASSERT_EQ(reports.reports(0).metrics_size(), 1);
+    EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
+
+    StatsLogReport::DurationMetricDataWrapper metricData;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData);
+    ASSERT_EQ(metricData.data_size(), 1);
+    DurationMetricData data = metricData.data(0);
+    ASSERT_EQ(data.bucket_info_size(), 1);
+
+    DurationBucketInfo bucketInfo = data.bucket_info(0);
+    EXPECT_EQ(bucketInfo.start_bucket_elapsed_nanos(), updateTimeNs);
+    EXPECT_EQ(bucketInfo.end_bucket_elapsed_nanos(), dumpTimeNs);
+    EXPECT_EQ(bucketInfo.duration_nanos(), 20 * NS_PER_SEC);
+}
+
+TEST_F(ConfigUpdateE2eTest, TestNewDurationExistingWhatSlicedCondition) {
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");
+    *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+    *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+    *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
+    *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
+
+    Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+    // The predicate is dimensioning by first attribution node by uid.
+    *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    *config.add_predicate() = holdingWakelockPredicate;
+
+    Predicate isInBackgroundPredicate = CreateIsInBackgroundPredicate();
+    *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
+            CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /*uid*/});
+    *config.add_predicate() = isInBackgroundPredicate;
+
+    ConfigKey key(123, 987);
+    uint64_t bucketStartTimeNs = 10000000000;  // 0:10
+    uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL;
+    sp<StatsLogProcessor> processor =
+            CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key);
+
+    int app1Uid = 123, app2Uid = 456;
+    vector<int> attributionUids1 = {app1Uid};
+    vector<string> attributionTags1 = {"App1"};
+    vector<int> attributionUids2 = {app2Uid};
+    vector<string> attributionTags2 = {"App2"};
+    unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
+                                                            attributionUids1, attributionTags1,
+                                                            "wl1");  // 0:10
+    processor->OnLogEvent(event.get());
+    event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 22 * NS_PER_SEC, app1Uid);  // 0:22
+    processor->OnLogEvent(event.get());
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + 35 * NS_PER_SEC, attributionUids2,
+                                       attributionTags2,
+                                       "wl1");  // 0:35
+    processor->OnLogEvent(event.get());
+
+    // Add metric.
+    DurationMetric* durationMetric = config.add_duration_metric();
+    durationMetric->set_id(StringToId("WakelockDuration"));
+    durationMetric->set_what(holdingWakelockPredicate.id());
+    durationMetric->set_condition(isInBackgroundPredicate.id());
+    durationMetric->set_aggregation_type(DurationMetric::SUM);
+    // The metric is dimensioning by first attribution node and only by uid.
+    *durationMetric->mutable_dimensions_in_what() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    durationMetric->set_bucket(FIVE_MINUTES);
+    // Links between wakelock state atom and condition of app is in background.
+    auto links = durationMetric->add_links();
+    links->set_condition(isInBackgroundPredicate.id());
+    *links->mutable_fields_in_what() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    *links->mutable_fields_in_condition() =
+            CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /*uid*/});
+
+    uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC;  // 1:00
+    processor->OnConfigUpdated(updateTimeNs, key, config);
+
+    event = CreateMoveToBackgroundEvent(bucketStartTimeNs + 73 * NS_PER_SEC, app2Uid);  // 1:13
+    processor->OnLogEvent(event.get());
+    event = CreateReleaseWakelockEvent(bucketStartTimeNs + 84 * NS_PER_SEC, attributionUids1,
+                                       attributionTags1, "wl1");  // 1:24
+    processor->OnLogEvent(event.get());
+
+    uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC;  //  1:30
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    ASSERT_EQ(reports.reports_size(), 1);
+    ASSERT_EQ(reports.reports(0).metrics_size(), 1);
+    EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
+
+    StatsLogReport::DurationMetricDataWrapper metricData;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData);
+    ASSERT_EQ(metricData.data_size(), 2);
+
+    DurationMetricData data = metricData.data(0);
+    ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED,
+                                    app1Uid);
+    ASSERT_EQ(data.bucket_info_size(), 1);
+    DurationBucketInfo bucketInfo = data.bucket_info(0);
+    EXPECT_EQ(bucketInfo.duration_nanos(), 24 * NS_PER_SEC);
+
+    data = metricData.data(1);
+    ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED,
+                                    app2Uid);
+    ASSERT_EQ(data.bucket_info_size(), 1);
+    bucketInfo = data.bucket_info(0);
+    EXPECT_EQ(bucketInfo.duration_nanos(), 17 * NS_PER_SEC);
+}
+
+TEST_F(ConfigUpdateE2eTest, TestNewDurationExistingWhatSlicedState) {
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");
+    *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+    *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+
+    Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+    // The predicate is dimensioning by first attribution node by uid.
+    *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    *config.add_predicate() = holdingWakelockPredicate;
+
+    auto uidProcessState = CreateUidProcessState();
+    *config.add_state() = uidProcessState;
+
+    // Count metric. We don't care about this one. Only use it so the StateTracker gets persisted.
+    CountMetric* countMetric = config.add_count_metric();
+    countMetric->set_id(StringToId("Tmp"));
+    countMetric->set_what(config.atom_matcher(0).id());
+    countMetric->add_slice_by_state(uidProcessState.id());
+    // The metric is dimensioning by first attribution node and only by uid.
+    *countMetric->mutable_dimensions_in_what() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    countMetric->set_bucket(FIVE_MINUTES);
+    auto stateLink = countMetric->add_state_link();
+    stateLink->set_state_atom_id(util::UID_PROCESS_STATE_CHANGED);
+    *stateLink->mutable_fields_in_what() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    *stateLink->mutable_fields_in_state() =
+            CreateDimensions(util::UID_PROCESS_STATE_CHANGED, {1 /*uid*/});
+    config.add_no_report_metric(countMetric->id());
+
+    ConfigKey key(123, 987);
+    uint64_t bucketStartTimeNs = 10000000000;  // 0:10
+    uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(FIVE_MINUTES) * 1000000LL;
+    sp<StatsLogProcessor> processor =
+            CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, key);
+
+    int app1Uid = 123, app2Uid = 456;
+    vector<int> attributionUids1 = {app1Uid};
+    vector<string> attributionTags1 = {"App1"};
+    vector<int> attributionUids2 = {app2Uid};
+    vector<string> attributionTags2 = {"App2"};
+    unique_ptr<LogEvent> event = CreateUidProcessStateChangedEvent(
+            bucketStartTimeNs + 10 * NS_PER_SEC, app1Uid,
+            android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND);  // 0:10
+    processor->OnLogEvent(event.get());
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + 22 * NS_PER_SEC, attributionUids1,
+                                       attributionTags1,
+                                       "wl1");  // 0:22
+    processor->OnLogEvent(event.get());
+    event = CreateUidProcessStateChangedEvent(
+            bucketStartTimeNs + 30 * NS_PER_SEC, app2Uid,
+            android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND);  // 0:30
+    processor->OnLogEvent(event.get());
+
+    // Add metric.
+    DurationMetric* durationMetric = config.add_duration_metric();
+    durationMetric->set_id(StringToId("WakelockDuration"));
+    durationMetric->set_what(holdingWakelockPredicate.id());
+    durationMetric->add_slice_by_state(uidProcessState.id());
+    durationMetric->set_aggregation_type(DurationMetric::SUM);
+    // The metric is dimensioning by first attribution node and only by uid.
+    *durationMetric->mutable_dimensions_in_what() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    durationMetric->set_bucket(FIVE_MINUTES);
+    // Links between wakelock state atom and condition of app is in background.
+    stateLink = durationMetric->add_state_link();
+    stateLink->set_state_atom_id(util::UID_PROCESS_STATE_CHANGED);
+    *stateLink->mutable_fields_in_what() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    *stateLink->mutable_fields_in_state() =
+            CreateDimensions(util::UID_PROCESS_STATE_CHANGED, {1 /*uid*/});
+
+    uint64_t updateTimeNs = bucketStartTimeNs + 60 * NS_PER_SEC;  // 1:00
+    processor->OnConfigUpdated(updateTimeNs, key, config);
+
+    event = CreateAcquireWakelockEvent(bucketStartTimeNs + 72 * NS_PER_SEC, attributionUids2,
+                                       attributionTags2,
+                                       "wl1");  // 1:13
+    processor->OnLogEvent(event.get());
+    event = CreateUidProcessStateChangedEvent(
+            bucketStartTimeNs + 75 * NS_PER_SEC, app1Uid,
+            android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND);  // 1:15
+    processor->OnLogEvent(event.get());
+    event = CreateReleaseWakelockEvent(bucketStartTimeNs + 84 * NS_PER_SEC, attributionUids1,
+                                       attributionTags1, "wl1");  // 1:24
+    processor->OnLogEvent(event.get());
+
+    uint64_t dumpTimeNs = bucketStartTimeNs + 90 * NS_PER_SEC;  //  1:30
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(key, dumpTimeNs, true, true, ADB_DUMP, FAST, &buffer);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    ASSERT_EQ(reports.reports_size(), 1);
+    ASSERT_EQ(reports.reports(0).metrics_size(), 1);
+    EXPECT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
+
+    StatsLogReport::DurationMetricDataWrapper metricData;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metricData);
+    ASSERT_EQ(metricData.data_size(), 3);
+
+    DurationMetricData data = metricData.data(0);
+    ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED,
+                                    app1Uid);
+    ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
+                       android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND);
+    ASSERT_EQ(data.bucket_info_size(), 1);
+    DurationBucketInfo bucketInfo = data.bucket_info(0);
+    EXPECT_EQ(bucketInfo.duration_nanos(), 15 * NS_PER_SEC);
+
+    data = metricData.data(1);
+    ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED,
+                                    app1Uid);
+    ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
+                       android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND);
+    ASSERT_EQ(data.bucket_info_size(), 1);
+    bucketInfo = data.bucket_info(0);
+    EXPECT_EQ(bucketInfo.duration_nanos(), 9 * NS_PER_SEC);
+
+    data = metricData.data(2);
+    ValidateAttributionUidDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED,
+                                    app2Uid);
+    ValidateStateValue(data.slice_by_state(), util::UID_PROCESS_STATE_CHANGED,
+                       android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND);
+    ASSERT_EQ(data.bucket_info_size(), 1);
+    bucketInfo = data.bucket_info(0);
+    EXPECT_EQ(bucketInfo.duration_nanos(), 18 * NS_PER_SEC);
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/bin/tests/e2e/DurationMetric_e2e_test.cpp b/bin/tests/e2e/DurationMetric_e2e_test.cpp
index 4efb038..2473c1c 100644
--- a/bin/tests/e2e/DurationMetric_e2e_test.cpp
+++ b/bin/tests/e2e/DurationMetric_e2e_test.cpp
@@ -486,11 +486,11 @@
     // Links between wakelock state atom and condition of app is in background.
     auto links = durationMetric->add_links();
     links->set_condition(isInBackgroundPredicate.id());
-    auto dimensionWhat = links->mutable_fields_in_what();
-    dimensionWhat->set_field(util::WAKELOCK_STATE_CHANGED);
-    dimensionWhat->add_child()->set_field(1);  // uid field.
-    *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
-            util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
+    *links->mutable_fields_in_what() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    auto dimensionCondition = links->mutable_fields_in_condition();
+    dimensionCondition->set_field(util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+    dimensionCondition->add_child()->set_field(1);  // uid field.
 
     ConfigKey cfgKey;
     uint64_t bucketStartTimeNs = 10000000000;
@@ -591,11 +591,11 @@
     // Links between wakelock state atom and condition of app is in background.
     auto links = durationMetric->add_links();
     links->set_condition(isInBackgroundPredicate.id());
-    auto dimensionWhat = links->mutable_fields_in_what();
-    dimensionWhat->set_field(util::WAKELOCK_STATE_CHANGED);
-    dimensionWhat->add_child()->set_field(1);  // uid field.
-    *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
-            util::ACTIVITY_FOREGROUND_STATE_CHANGED, {Position::FIRST});
+    *links->mutable_fields_in_what() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    auto dimensionCondition = links->mutable_fields_in_condition();
+    dimensionCondition->set_field(util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+    dimensionCondition->add_child()->set_field(1);  // uid field.
 
     auto metric_activation1 = config.add_metric_activation();
     metric_activation1->set_metric_id(durationMetric->id());
@@ -1228,6 +1228,9 @@
     *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
 
     auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+    *(holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions()) =
+            CreateAttributionUidAndOtherDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST},
+                                                   {3 /* tag */});
     *config.add_predicate() = holdingWakelockPredicate;
 
     auto uidProcessState = CreateUidProcessState();
diff --git a/bin/tests/external/StatsPullerManager_test.cpp b/bin/tests/external/StatsPullerManager_test.cpp
index c76e85e..0d539f4 100644
--- a/bin/tests/external/StatsPullerManager_test.cpp
+++ b/bin/tests/external/StatsPullerManager_test.cpp
@@ -89,10 +89,10 @@
 sp<StatsPullerManager> createPullerManagerAndRegister() {
     sp<StatsPullerManager> pullerManager = new StatsPullerManager();
     shared_ptr<FakePullAtomCallback> cb1 = SharedRefBase::make<FakePullAtomCallback>(uid1);
-    pullerManager->RegisterPullAtomCallback(uid1, pullTagId1, coolDownNs, timeoutNs, {}, cb1, true);
+    pullerManager->RegisterPullAtomCallback(uid1, pullTagId1, coolDownNs, timeoutNs, {}, cb1);
     shared_ptr<FakePullAtomCallback> cb2 = SharedRefBase::make<FakePullAtomCallback>(uid2);
-    pullerManager->RegisterPullAtomCallback(uid2, pullTagId1, coolDownNs, timeoutNs, {}, cb2, true);
-    pullerManager->RegisterPullAtomCallback(uid1, pullTagId2, coolDownNs, timeoutNs, {}, cb1, true);
+    pullerManager->RegisterPullAtomCallback(uid2, pullTagId1, coolDownNs, timeoutNs, {}, cb2);
+    pullerManager->RegisterPullAtomCallback(uid1, pullTagId2, coolDownNs, timeoutNs, {}, cb1);
     return pullerManager;
 }
 }  // anonymous namespace
@@ -101,14 +101,14 @@
     sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
 
     vector<shared_ptr<LogEvent>> data;
-    EXPECT_FALSE(pullerManager->Pull(pullTagId1, {unregisteredUid}, /*timestamp =*/1, &data, true));
+    EXPECT_FALSE(pullerManager->Pull(pullTagId1, {unregisteredUid}, /*timestamp =*/1, &data));
 }
 
 TEST(StatsPullerManagerTest, TestPullChoosesCorrectUid) {
     sp<StatsPullerManager> pullerManager = createPullerManagerAndRegister();
 
     vector<shared_ptr<LogEvent>> data;
-    EXPECT_TRUE(pullerManager->Pull(pullTagId1, {uid1}, /*timestamp =*/1, &data, true));
+    EXPECT_TRUE(pullerManager->Pull(pullTagId1, {uid1}, /*timestamp =*/1, &data));
     ASSERT_EQ(data.size(), 1);
     EXPECT_EQ(data[0]->GetTagId(), pullTagId1);
     ASSERT_EQ(data[0]->getValues().size(), 1);
@@ -121,7 +121,7 @@
     pullerManager->RegisterPullUidProvider(configKey, uidProvider);
 
     vector<shared_ptr<LogEvent>> data;
-    EXPECT_FALSE(pullerManager->Pull(pullTagId1, badConfigKey, /*timestamp =*/1, &data, true));
+    EXPECT_FALSE(pullerManager->Pull(pullTagId1, badConfigKey, /*timestamp =*/1, &data));
 }
 
 TEST(StatsPullerManagerTest, TestPullConfigKeyGood) {
@@ -130,7 +130,7 @@
     pullerManager->RegisterPullUidProvider(configKey, uidProvider);
 
     vector<shared_ptr<LogEvent>> data;
-    EXPECT_TRUE(pullerManager->Pull(pullTagId1, configKey, /*timestamp =*/1, &data, true));
+    EXPECT_TRUE(pullerManager->Pull(pullTagId1, configKey, /*timestamp =*/1, &data));
     EXPECT_EQ(data[0]->GetTagId(), pullTagId1);
     ASSERT_EQ(data[0]->getValues().size(), 1);
     EXPECT_EQ(data[0]->getValues()[0].mValue.int_value, uid2);
@@ -142,7 +142,7 @@
     pullerManager->RegisterPullUidProvider(configKey, uidProvider);
 
     vector<shared_ptr<LogEvent>> data;
-    EXPECT_FALSE(pullerManager->Pull(pullTagId2, configKey, /*timestamp =*/1, &data, true));
+    EXPECT_FALSE(pullerManager->Pull(pullTagId2, configKey, /*timestamp =*/1, &data));
 }
 
 }  // namespace statsd
diff --git a/bin/tests/guardrail/StatsdStats_test.cpp b/bin/tests/guardrail/StatsdStats_test.cpp
index 428c46f..16ae9b5 100644
--- a/bin/tests/guardrail/StatsdStats_test.cpp
+++ b/bin/tests/guardrail/StatsdStats_test.cpp
@@ -345,6 +345,9 @@
     // old event, we get it from the stats buffer. should be ignored.
     stats.noteBucketDropped(1000L);
 
+    stats.noteLateLogEvent(1000L, 10L);
+    stats.noteLateLogEvent(1000L, 50L);
+
     stats.noteBucketBoundaryDelayNs(1000L, -1L);
     stats.noteBucketBoundaryDelayNs(1000L, -10L);
     stats.noteBucketBoundaryDelayNs(1000L, 2L);
@@ -364,12 +367,18 @@
     EXPECT_EQ(1L, atomStats.bucket_dropped());
     EXPECT_EQ(-10L, atomStats.min_bucket_boundary_delay_ns());
     EXPECT_EQ(2L, atomStats.max_bucket_boundary_delay_ns());
+    EXPECT_EQ(2L, atomStats.late_log_event());
+    EXPECT_EQ(60L, atomStats.sum_late_log_event_extra_duration_ns());
+    EXPECT_EQ(50L, atomStats.max_late_log_event_extra_duration_ns());
 
     auto atomStats2 = report.atom_metric_stats(1);
     EXPECT_EQ(1001L, atomStats2.metric_id());
     EXPECT_EQ(0L, atomStats2.bucket_dropped());
     EXPECT_EQ(0L, atomStats2.min_bucket_boundary_delay_ns());
     EXPECT_EQ(1L, atomStats2.max_bucket_boundary_delay_ns());
+    EXPECT_EQ(0L, atomStats2.late_log_event());
+    EXPECT_EQ(0L, atomStats2.sum_late_log_event_extra_duration_ns());
+    EXPECT_EQ(0L, atomStats2.max_late_log_event_extra_duration_ns());
 }
 
 TEST(StatsdStatsTest, TestAnomalyMonitor) {
diff --git a/bin/tests/metrics/CountMetricProducer_test.cpp b/bin/tests/metrics/CountMetricProducer_test.cpp
index bb8e7bf..8e2864c 100644
--- a/bin/tests/metrics/CountMetricProducer_test.cpp
+++ b/bin/tests/metrics/CountMetricProducer_test.cpp
@@ -41,6 +41,7 @@
 
 namespace {
 const ConfigKey kConfigKey(0, 12345);
+const uint64_t protoHash = 0x1234567890;
 
 void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
     AStatsEvent* statsEvent = AStatsEvent_obtain();
@@ -75,7 +76,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2);
+                                      wizard, protoHash, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2);
     EXPECT_EQ(600500000000, countProducer.mCurrentBucketStartTimeNs);
     EXPECT_EQ(10, countProducer.mCurrentBucketNum);
     EXPECT_EQ(660000000005, countProducer.getCurrentBucketEndTimeNs());
@@ -95,7 +96,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, bucketStartTimeNs, bucketStartTimeNs);
+                                      wizard, protoHash, bucketStartTimeNs, bucketStartTimeNs);
 
     // 2 events in bucket 1.
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -158,7 +159,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     CountMetricProducer countProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard,
-                                      bucketStartTimeNs, bucketStartTimeNs);
+                                      protoHash, bucketStartTimeNs, bucketStartTimeNs);
 
     countProducer.onConditionChanged(true, bucketStartTimeNs);
 
@@ -226,8 +227,8 @@
     EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
 
     CountMetricProducer countProducer(kConfigKey, metric, 0 /*condition tracker index*/,
-                                      {ConditionState::kUnknown}, wizard, bucketStartTimeNs,
-                                      bucketStartTimeNs);
+                                      {ConditionState::kUnknown}, wizard, protoHash,
+                                      bucketStartTimeNs, bucketStartTimeNs);
 
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1);
     countProducer.flushIfNeededLocked(bucketStartTimeNs + 1);
@@ -265,7 +266,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard,
-                                      bucketStartTimeNs, bucketStartTimeNs);
+                                      protoHash, bucketStartTimeNs, bucketStartTimeNs);
 
     sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
     EXPECT_TRUE(anomalyTracker != nullptr);
@@ -332,7 +333,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     CountMetricProducer countProducer(kConfigKey, metric, -1 /* no condition */, {}, wizard,
-                                      bucketStartTimeNs, bucketStartTimeNs);
+                                      protoHash, bucketStartTimeNs, bucketStartTimeNs);
 
     // Bucket is flushed yet.
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -397,7 +398,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, bucketStartTimeNs, bucketStartTimeNs);
+                                      wizard, protoHash, bucketStartTimeNs, bucketStartTimeNs);
 
     sp<AnomalyTracker> anomalyTracker = countProducer.addAnomalyTracker(alert, alarmMonitor);
 
@@ -459,7 +460,7 @@
     int64_t fiveWeeksNs = 5 * 7 * oneDayNs;
 
     CountMetricProducer countProducer(kConfigKey, metric, -1 /* meaning no condition */, {}, wizard,
-                                      oneDayNs, fiveWeeksNs);
+                                      protoHash, oneDayNs, fiveWeeksNs);
 
     int64_t fiveWeeksOneDayNs = fiveWeeksNs + oneDayNs;
 
diff --git a/bin/tests/metrics/DurationMetricProducer_test.cpp b/bin/tests/metrics/DurationMetricProducer_test.cpp
index 05cfa37..bb2ede4 100644
--- a/bin/tests/metrics/DurationMetricProducer_test.cpp
+++ b/bin/tests/metrics/DurationMetricProducer_test.cpp
@@ -45,6 +45,7 @@
 namespace {
 
 const ConfigKey kConfigKey(0, 12345);
+const uint64_t protoHash = 0x1234567890;
 void makeLogEvent(LogEvent* logEvent, int64_t timestampNs, int atomId) {
     AStatsEvent* statsEvent = AStatsEvent_obtain();
     AStatsEvent_setAtomId(statsEvent, atomId);
@@ -71,10 +72,10 @@
 
     FieldMatcher dimensions;
 
-    DurationMetricProducer durationProducer(kConfigKey, metric, -1 /*no condition*/, {},
-                                            1 /* start index */, 2 /* stop index */,
-                                            3 /* stop_all index */, false /*nesting*/, wizard,
-                                            dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2);
+    DurationMetricProducer durationProducer(
+            kConfigKey, metric, -1 /*no condition*/, {}, -1 /*what index not needed*/,
+            1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
+            wizard, protoHash, dimensions, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2);
 
     EXPECT_EQ(600500000000, durationProducer.mCurrentBucketStartTimeNs);
     EXPECT_EQ(10, durationProducer.mCurrentBucketNum);
@@ -99,10 +100,10 @@
 
     FieldMatcher dimensions;
 
-    DurationMetricProducer durationProducer(kConfigKey, metric, -1 /*no condition*/, {},
-                                            1 /* start index */, 2 /* stop index */,
-                                            3 /* stop_all index */, false /*nesting*/, wizard,
-                                            dimensions, bucketStartTimeNs, bucketStartTimeNs);
+    DurationMetricProducer durationProducer(
+            kConfigKey, metric, -1 /*no condition*/, {}, -1 /*what index not needed*/,
+            1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
+            wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
 
     durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
@@ -144,8 +145,9 @@
 
     DurationMetricProducer durationProducer(
             kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
-            1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
-            wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+            -1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */,
+            3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
+            bucketStartTimeNs, bucketStartTimeNs);
     durationProducer.mCondition = ConditionState::kFalse;
 
     EXPECT_FALSE(durationProducer.mCondition);
@@ -194,8 +196,9 @@
 
     DurationMetricProducer durationProducer(
             kConfigKey, metric, 0 /* condition index */, {ConditionState::kUnknown},
-            1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
-            wizard, dimensions, bucketStartTimeNs, bucketStartTimeNs);
+            -1 /*what index not needed*/, 1 /* start index */, 2 /* stop index */,
+            3 /* stop_all index */, false /*nesting*/, wizard, protoHash, dimensions,
+            bucketStartTimeNs, bucketStartTimeNs);
 
     EXPECT_EQ(ConditionState::kUnknown, durationProducer.mCondition);
     EXPECT_FALSE(durationProducer.isConditionSliced());
@@ -238,10 +241,10 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     FieldMatcher dimensions;
 
-    DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {},
-                                            1 /* start index */, 2 /* stop index */,
-                                            3 /* stop_all index */, false /*nesting*/, wizard,
-                                            dimensions, bucketStartTimeNs, bucketStartTimeNs);
+    DurationMetricProducer durationProducer(
+            kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
+            1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
+            wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
 
     int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -301,10 +304,10 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     FieldMatcher dimensions;
 
-    DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {},
-                                            1 /* start index */, 2 /* stop index */,
-                                            3 /* stop_all index */, false /*nesting*/, wizard,
-                                            dimensions, bucketStartTimeNs, bucketStartTimeNs);
+    DurationMetricProducer durationProducer(
+            kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
+            1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
+            wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
 
     int64_t startTimeNs = bucketStartTimeNs + 1 * NS_PER_SEC;
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -365,10 +368,10 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     FieldMatcher dimensions;
 
-    DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {},
-                                            1 /* start index */, 2 /* stop index */,
-                                            3 /* stop_all index */, false /*nesting*/, wizard,
-                                            dimensions, bucketStartTimeNs, bucketStartTimeNs);
+    DurationMetricProducer durationProducer(
+            kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
+            1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
+            wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
 
     sp<AnomalyTracker> anomalyTracker = durationProducer.addAnomalyTracker(alert, alarmMonitor);
     EXPECT_TRUE(anomalyTracker != nullptr);
@@ -411,10 +414,10 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     FieldMatcher dimensions;
 
-    DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {},
-                                            1 /* start index */, 2 /* stop index */,
-                                            3 /* stop_all index */, false /*nesting*/, wizard,
-                                            dimensions, bucketStartTimeNs, bucketStartTimeNs);
+    DurationMetricProducer durationProducer(
+            kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
+            1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
+            wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
 
     int64_t startTimeNs = bucketStartTimeNs + 1;
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -465,10 +468,10 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     FieldMatcher dimensions;
 
-    DurationMetricProducer durationProducer(kConfigKey, metric, -1 /* no condition */, {},
-                                            1 /* start index */, 2 /* stop index */,
-                                            3 /* stop_all index */, false /*nesting*/, wizard,
-                                            dimensions, bucketStartTimeNs, bucketStartTimeNs);
+    DurationMetricProducer durationProducer(
+            kConfigKey, metric, -1 /* no condition */, {}, -1 /*what index not needed*/,
+            1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
+            wizard, protoHash, dimensions, bucketStartTimeNs, bucketStartTimeNs);
 
     int64_t startTimeNs = bucketStartTimeNs + 1;
     LogEvent event1(/*uid=*/0, /*pid=*/0);
diff --git a/bin/tests/metrics/EventMetricProducer_test.cpp b/bin/tests/metrics/EventMetricProducer_test.cpp
index dfbb9da..4bbbd2c 100644
--- a/bin/tests/metrics/EventMetricProducer_test.cpp
+++ b/bin/tests/metrics/EventMetricProducer_test.cpp
@@ -36,9 +36,11 @@
 namespace os {
 namespace statsd {
 
-const ConfigKey kConfigKey(0, 12345);
 
 namespace {
+const ConfigKey kConfigKey(0, 12345);
+const uint64_t protoHash = 0x1234567890;
+
 void makeLogEvent(LogEvent* logEvent, int32_t atomId, int64_t timestampNs, string str) {
     AStatsEvent* statsEvent = AStatsEvent_obtain();
     AStatsEvent_setAtomId(statsEvent, atomId);
@@ -66,7 +68,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, bucketStartTimeNs);
+                                      wizard, protoHash, bucketStartTimeNs);
 
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
@@ -102,7 +104,8 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     EventMetricProducer eventProducer(kConfigKey, metric, 0 /*condition index*/,
-                                      {ConditionState::kUnknown}, wizard, bucketStartTimeNs);
+                                      {ConditionState::kUnknown}, wizard, protoHash,
+                                      bucketStartTimeNs);
 
     eventProducer.onConditionChanged(true /*condition*/, bucketStartTimeNs);
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
@@ -157,7 +160,8 @@
     EXPECT_CALL(*wizard, query(_, key2, _)).WillOnce(Return(ConditionState::kTrue));
 
     EventMetricProducer eventProducer(kConfigKey, metric, 0 /*condition index*/,
-                                      {ConditionState::kUnknown}, wizard, bucketStartTimeNs);
+                                      {ConditionState::kUnknown}, wizard, protoHash,
+                                      bucketStartTimeNs);
 
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1);
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2);
diff --git a/bin/tests/metrics/GaugeMetricProducer_test.cpp b/bin/tests/metrics/GaugeMetricProducer_test.cpp
index 5997bed..1060681 100644
--- a/bin/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/bin/tests/metrics/GaugeMetricProducer_test.cpp
@@ -23,7 +23,7 @@
 
 #include "logd/LogEvent.h"
 #include "metrics_test_helper.h"
-#include "src/matchers/SimpleLogMatchingTracker.h"
+#include "src/matchers/SimpleAtomMatchingTracker.h"
 #include "src/metrics/MetricProducer.h"
 #include "src/stats_log_util.h"
 #include "stats_event.h"
@@ -47,7 +47,7 @@
 const ConfigKey kConfigKey(0, 12345);
 const int tagId = 1;
 const int64_t metricId = 123;
-const int64_t atomMatcherId = 678;
+const uint64_t protoHash = 0x123456789;
 const int logEventMatcherIndex = 0;
 const int64_t bucketStartTimeNs = 10 * NS_PER_SEC;
 const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
@@ -94,19 +94,17 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
-    sp<EventMatcherWizard> eventMatcherWizard = new EventMatcherWizard({
-        new SimpleLogMatchingTracker(atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+    sp<EventMatcherWizard> eventMatcherWizard =
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
     // statsd started long ago.
     // The metric starts in the middle of the bucket
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard, -1, -1,
-                                      tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager);
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                      -1, -1, tagId, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2,
+                                      pullerManager);
     gaugeProducer.prepareFirstBucket();
 
     EXPECT_EQ(600500000000, gaugeProducer.mCurrentBucketStartTimeNs);
@@ -127,19 +125,15 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
                 data->clear();
                 data->push_back(makeLogEvent(tagId, eventTimeNs + 10, 3, "some value", 11));
@@ -147,8 +141,9 @@
             }));
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1,
-                                      tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                      tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
+                                      pullerManager);
     gaugeProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
@@ -217,15 +212,11 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard,
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
                                       -1 /* -1 means no pulling */, -1, tagId, bucketStartTimeNs,
                                       bucketStartTimeNs, pullerManager);
     gaugeProducer.prepareFirstBucket();
@@ -301,19 +292,16 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
+
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             .WillOnce(Return(false))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 2));
@@ -321,8 +309,9 @@
             }));
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1,
-                                      tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                      tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
+                                      pullerManager);
     gaugeProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
@@ -378,22 +367,19 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
             .WillOnce(Return(false));
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1,
-                                      tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                      tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
+                                      pullerManager);
     gaugeProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
@@ -428,30 +414,26 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     int64_t conditionChangeNs = bucketStartTimeNs + 8;
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, conditionChangeNs, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, conditionChangeNs, _))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs + 10, 100));
                 return true;
             }));
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/,
-                                      {ConditionState::kUnknown}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+                                      {ConditionState::kUnknown}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     gaugeProducer.prepareFirstBucket();
 
     gaugeProducer.onConditionChanged(true, conditionChangeNs);
@@ -502,12 +484,8 @@
     dim->set_field(tagId);
     dim->add_child()->set_field(1);
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     EXPECT_CALL(*wizard, query(_, _, _))
@@ -527,18 +505,18 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, sliceConditionChangeNs, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, sliceConditionChangeNs, _))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs + 10, 1000, 100));
                 return true;
             }));
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, 0 /*condition index*/,
-                                      {ConditionState::kUnknown}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, -1, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+                                      {ConditionState::kUnknown}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId, -1, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     gaugeProducer.prepareFirstBucket();
 
     gaugeProducer.onSlicedConditionMayChange(true, sliceConditionChangeNs);
@@ -566,7 +544,7 @@
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
             .WillOnce(Return(false));
 
     GaugeMetric metric;
@@ -577,16 +555,13 @@
     gaugeFieldMatcher->set_field(tagId);
     gaugeFieldMatcher->add_child()->set_field(2);
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard, tagId, -1,
-                                      tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                      tagId, -1, tagId, bucketStartTimeNs, bucketStartTimeNs,
+                                      pullerManager);
     gaugeProducer.prepareFirstBucket();
 
     Alert alert;
@@ -657,24 +632,20 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 4));
                 return true;
             }))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 5));
@@ -684,8 +655,8 @@
 
     int triggerId = 5;
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
-                                      triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                      tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
     gaugeProducer.prepareFirstBucket();
 
@@ -729,31 +700,27 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
                 data->clear();
                 data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 3, 4));
                 return true;
             }))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
                 data->clear();
                 data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 5));
                 return true;
             }))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
                 data->clear();
                 data->push_back(CreateTwoValueLogEvent(tagId, eventTimeNs, 4, 6));
@@ -763,8 +730,8 @@
 
     int triggerId = 5;
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
-                                      triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                      tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
     gaugeProducer.prepareFirstBucket();
 
@@ -807,18 +774,14 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 3, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 3, _))
             // Bucket start.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, eventTimeNs, 10));
                 return true;
@@ -826,8 +789,8 @@
 
     int triggerId = 5;
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
-                                      triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                      tagId, triggerId, tagId, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
     gaugeProducer.prepareFirstBucket();
 
diff --git a/bin/tests/metrics/ValueMetricProducer_test.cpp b/bin/tests/metrics/ValueMetricProducer_test.cpp
index 5666501..6cf4192 100644
--- a/bin/tests/metrics/ValueMetricProducer_test.cpp
+++ b/bin/tests/metrics/ValueMetricProducer_test.cpp
@@ -22,7 +22,7 @@
 #include <vector>
 
 #include "metrics_test_helper.h"
-#include "src/matchers/SimpleLogMatchingTracker.h"
+#include "src/matchers/SimpleAtomMatchingTracker.h"
 #include "src/metrics/MetricProducer.h"
 #include "src/stats_log_util.h"
 #include "tests/statsd_test_util.h"
@@ -46,7 +46,7 @@
 const ConfigKey kConfigKey(0, 12345);
 const int tagId = 1;
 const int64_t metricId = 123;
-const int64_t atomMatcherId = 678;
+const uint64_t protoHash = 0x1234567890;
 const int logEventMatcherIndex = 0;
 const int64_t bucketStartTimeNs = 10000000000;
 const int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(ONE_MINUTE) * 1000000LL;
@@ -58,7 +58,7 @@
 double epsilon = 0.001;
 
 static void assertPastBucketValuesSingleKey(
-        const std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>>& mPastBuckets,
+        const std::unordered_map<MetricDimensionKey, std::vector<PastValueBucket>>& mPastBuckets,
         const std::initializer_list<int>& expectedValuesList,
         const std::initializer_list<int64_t>& expectedDurationNsList,
         const std::initializer_list<int64_t>& expectedStartTimeNsList,
@@ -80,7 +80,7 @@
     ASSERT_EQ(1, mPastBuckets.size());
     ASSERT_EQ(expectedValues.size(), mPastBuckets.begin()->second.size());
 
-    const vector<ValueBucket>& buckets = mPastBuckets.begin()->second;
+    const vector<PastValueBucket>& buckets = mPastBuckets.begin()->second;
     for (int i = 0; i < expectedValues.size(); i++) {
         EXPECT_EQ(expectedValues[i], buckets[i].values[0].long_value)
                 << "Values differ at index " << i;
@@ -93,18 +93,21 @@
     }
 }
 
+static void assertConditionTimer(const ConditionTimer& conditionTimer, bool condition,
+                                 int64_t timerNs, int64_t lastConditionTrueTimestampNs) {
+    EXPECT_EQ(condition, conditionTimer.mCondition);
+    EXPECT_EQ(timerNs, conditionTimer.mTimerNs);
+    EXPECT_EQ(lastConditionTrueTimestampNs, conditionTimer.mLastConditionChangeTimestampNs);
+}
+
 }  // anonymous namespace
 
 class ValueMetricProducerTestHelper {
 public:
     static sp<ValueMetricProducer> createValueProducerNoConditions(
             sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric) {
-        UidMap uidMap;
-        SimpleAtomMatcher atomMatcher;
-        atomMatcher.set_atom_id(tagId);
         sp<EventMatcherWizard> eventMatcherWizard =
-                new EventMatcherWizard({new SimpleLogMatchingTracker(
-                        atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+                createEventMatcherWizard(tagId, logEventMatcherIndex);
         sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
         EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
                 .WillOnce(Return());
@@ -113,8 +116,8 @@
 
         sp<ValueMetricProducer> valueProducer =
                 new ValueMetricProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                        wizard, logEventMatcherIndex, eventMatcherWizard, tagId,
-                                        bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+                                        wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                        tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
         valueProducer->prepareFirstBucket();
         return valueProducer;
     }
@@ -122,12 +125,8 @@
     static sp<ValueMetricProducer> createValueProducerWithCondition(
             sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
             ConditionState conditionAfterFirstBucketPrepared) {
-        UidMap uidMap;
-        SimpleAtomMatcher atomMatcher;
-        atomMatcher.set_atom_id(tagId);
         sp<EventMatcherWizard> eventMatcherWizard =
-                new EventMatcherWizard({new SimpleLogMatchingTracker(
-                        atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+                createEventMatcherWizard(tagId, logEventMatcherIndex);
         sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
         EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
                 .WillOnce(Return());
@@ -136,7 +135,7 @@
 
         sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
                 kConfigKey, metric, 0 /*condition index*/, {ConditionState::kUnknown}, wizard,
-                logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs,
+                protoHash, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs,
                 bucketStartTimeNs, pullerManager);
         valueProducer->prepareFirstBucket();
         valueProducer->mCondition = conditionAfterFirstBucketPrepared;
@@ -147,12 +146,8 @@
             sp<MockStatsPullerManager>& pullerManager, ValueMetric& metric,
             vector<int32_t> slicedStateAtoms,
             unordered_map<int, unordered_map<int, int64_t>> stateGroupMap) {
-        UidMap uidMap;
-        SimpleAtomMatcher atomMatcher;
-        atomMatcher.set_atom_id(tagId);
         sp<EventMatcherWizard> eventMatcherWizard =
-                new EventMatcherWizard({new SimpleLogMatchingTracker(
-                        atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+                createEventMatcherWizard(tagId, logEventMatcherIndex);
         sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
         EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
                 .WillOnce(Return());
@@ -160,9 +155,9 @@
                 .WillRepeatedly(Return());
 
         sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
-                kConfigKey, metric, -1 /* no condition */, {}, wizard, logEventMatcherIndex,
-                eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager, {},
-                {}, slicedStateAtoms, stateGroupMap);
+                kConfigKey, metric, -1 /* no condition */, {}, wizard, protoHash,
+                logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs,
+                bucketStartTimeNs, pullerManager, {}, {}, slicedStateAtoms, stateGroupMap);
         valueProducer->prepareFirstBucket();
         return valueProducer;
     }
@@ -172,12 +167,8 @@
             vector<int32_t> slicedStateAtoms,
             unordered_map<int, unordered_map<int, int64_t>> stateGroupMap,
             ConditionState conditionAfterFirstBucketPrepared) {
-        UidMap uidMap;
-        SimpleAtomMatcher atomMatcher;
-        atomMatcher.set_atom_id(tagId);
         sp<EventMatcherWizard> eventMatcherWizard =
-                new EventMatcherWizard({new SimpleLogMatchingTracker(
-                        atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+                createEventMatcherWizard(tagId, logEventMatcherIndex);
         sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
         EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _))
                 .WillOnce(Return());
@@ -186,8 +177,9 @@
 
         sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
                 kConfigKey, metric, 0 /* condition tracker index */, {ConditionState::kUnknown},
-                wizard, logEventMatcherIndex, eventMatcherWizard, tagId, bucketStartTimeNs,
-                bucketStartTimeNs, pullerManager, {}, {}, slicedStateAtoms, stateGroupMap);
+                wizard, protoHash, logEventMatcherIndex, eventMatcherWizard, tagId,
+                bucketStartTimeNs, bucketStartTimeNs, pullerManager, {}, {}, slicedStateAtoms,
+                stateGroupMap);
         valueProducer->prepareFirstBucket();
         valueProducer->mCondition = conditionAfterFirstBucketPrepared;
         return valueProducer;
@@ -237,20 +229,16 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
     int64_t startTimeBase = 11;
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
     // statsd started long ago.
     // The metric starts in the middle of the bucket
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard, -1,
-                                      startTimeBase, 22, pullerManager);
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                      -1, startTimeBase, 22, pullerManager);
     valueProducer.prepareFirstBucket();
 
     EXPECT_EQ(startTimeBase, valueProducer.calcPreviousBucketEndTime(60 * NS_PER_SEC + 10));
@@ -267,20 +255,16 @@
 TEST(ValueMetricProducerTest, TestFirstBucket) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
     // statsd started long ago.
     // The metric starts in the middle of the bucket
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard, -1, 5,
-                                      600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager);
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
+                                      -1, 5, 600 * NS_PER_SEC + NS_PER_SEC / 2, pullerManager);
     valueProducer.prepareFirstBucket();
 
     EXPECT_EQ(600500000000, valueProducer.mCurrentBucketStartTimeNs);
@@ -294,9 +278,9 @@
 TEST(ValueMetricProducerTest, TestPulledEventsNoCondition) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
                 return true;
@@ -313,8 +297,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(11, curBaseInfo.base.long_value);
@@ -329,8 +314,8 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(23, curBaseInfo.base.long_value);
@@ -347,8 +332,8 @@
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(36, curBaseInfo.base.long_value);
@@ -368,19 +353,19 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2;
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // Initialize bucket.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 1));
                 return true;
             }))
             // Partial bucket.
-            .WillOnce(Invoke([partialBucketSplitTimeNs](
-                                     int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                     vector<std::shared_ptr<LogEvent>>* data, bool) {
+            .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
+                                                        const int64_t eventTimeNs,
+                                                        vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
                 data->clear();
                 data->push_back(
@@ -421,30 +406,27 @@
 TEST(ValueMetricProducerTest, TestPulledEventsWithFiltering) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
-    auto keyValue = atomMatcher.add_field_value_matcher();
-    keyValue->set_field(1);
-    keyValue->set_eq_int(3);
+    FieldValueMatcher fvm;
+    fvm.set_field(1);
+    fvm.set_eq_int(3);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex, {fvm});
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 3, 3));
                 return true;
             }));
 
-    sp<ValueMetricProducer> valueProducer = new ValueMetricProducer(
-            kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, wizard, logEventMatcherIndex,
-            eventMatcherWizard, tagId, bucketStartTimeNs, bucketStartTimeNs, pullerManager);
+    sp<ValueMetricProducer> valueProducer =
+            new ValueMetricProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {}, wizard,
+                                    protoHash, logEventMatcherIndex, eventMatcherWizard, tagId,
+                                    bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer->prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
@@ -455,8 +437,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(11, curBaseInfo.base.long_value);
@@ -484,8 +467,8 @@
     allData.push_back(CreateTwoValueLogEvent(tagId, bucket4StartTimeNs + 1, 3, 36));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     // the base was reset
     EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -505,7 +488,7 @@
     metric.set_use_absolute_value_on_reset(true);
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
             .WillOnce(Return(true));
     sp<ValueMetricProducer> valueProducer =
             ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -518,8 +501,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(11, curBaseInfo.base.long_value);
@@ -531,8 +515,8 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(10, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -545,8 +529,8 @@
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(36, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -565,7 +549,7 @@
 TEST(ValueMetricProducerTest, TestPulledEventsTakeZeroOnReset) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
             .WillOnce(Return(false));
     sp<ValueMetricProducer> valueProducer =
             ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -578,8 +562,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(11, curBaseInfo.base.long_value);
@@ -591,8 +576,8 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(10, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -602,8 +587,8 @@
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1, 36));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(36, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -621,23 +606,23 @@
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);  // First condition change.
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
                 return true;
             }))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1);  // Second condition change.
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 130));
                 return true;
             }))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 1);  // Third condition change.
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 1, 180));
@@ -653,8 +638,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     // startUpdated:false sum:0 start:100
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
@@ -670,8 +656,8 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(110, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -683,8 +669,8 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(20, curInterval.value.long_value);
     EXPECT_EQ(false, curBaseInfo.hasBase);
@@ -698,18 +684,14 @@
 TEST_P(ValueMetricProducerTest_PartialBucket, TestPushedEvents) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-                                      pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, -1,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -759,31 +741,27 @@
 TEST_P(ValueMetricProducerTest_PartialBucket, TestPulledValue) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 150;
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             .WillOnce(Return(true))
-            .WillOnce(Invoke([partialBucketSplitTimeNs](
-                                     int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                     vector<std::shared_ptr<LogEvent>>* data, bool) {
+            .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
+                                                        const int64_t eventTimeNs,
+                                                        vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 120));
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
@@ -820,22 +798,18 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.set_split_bucket_for_app_upgrade(false);
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillOnce(Return());
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
             .WillOnce(Return(true));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
@@ -854,16 +828,16 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1);  // Condition change to true time.
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100));
                 return true;
             }))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs,
                           bucket2StartTimeNs - 100);  // Condition change to false time.
                 data->clear();
@@ -900,18 +874,14 @@
 TEST(ValueMetricProducerTest, TestPushedEventsWithoutCondition) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-                                      pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, -1,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -924,8 +894,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
@@ -933,7 +904,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(30, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -944,17 +915,13 @@
 TEST(ValueMetricProducerTest, TestPushedEventsWithCondition) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
     ValueMetricProducer valueProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard,
-                                      logEventMatcherIndex, eventMatcherWizard, -1,
+                                      protoHash, logEventMatcherIndex, eventMatcherWizard, -1,
                                       bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
     valueProducer.mCondition = ConditionState::kFalse;
@@ -974,8 +941,8 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(20, curInterval.value.long_value);
 
     LogEvent event3(/*uid=*/0, /*pid=*/0);
@@ -984,7 +951,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(50, curInterval.value.long_value);
 
     valueProducer.onConditionChangedLocked(false, bucketStartTimeNs + 35);
@@ -995,7 +962,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(50, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -1015,17 +982,13 @@
 
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
     ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, {},
-                                      wizard, logEventMatcherIndex, eventMatcherWizard,
+                                      wizard, protoHash, logEventMatcherIndex, eventMatcherWizard,
                                       -1 /*not pulled*/, bucketStartTimeNs, bucketStartTimeNs,
                                       pullerManager);
     valueProducer.prepareFirstBucket();
@@ -1081,11 +1044,54 @@
               std::ceil(1.0 * event6.GetElapsedTimestampNs() / NS_PER_SEC + refPeriodSec));
 }
 
+TEST(ValueMetricProducerTest, TestAnomalyDetectionMultipleBucketsSkipped) {
+    sp<AlarmMonitor> alarmMonitor;
+    Alert alert;
+    alert.set_id(101);
+    alert.set_metric_id(metricId);
+    alert.set_trigger_if_sum_gt(100);
+    alert.set_num_buckets(1);
+    const int32_t refPeriodSec = 3;
+    alert.set_refractory_period_secs(refPeriodSec);
+
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
+
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1);  // Condition change to true time.
+                data->clear();
+                data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 0));
+                return true;
+            }))
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs,
+                          bucket3StartTimeNs + 100);  // Condition changed to false time.
+                data->clear();
+                data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 100, 120));
+                return true;
+            }));
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithCondition(pullerManager, metric,
+                                                                            ConditionState::kFalse);
+    sp<AnomalyTracker> anomalyTracker = valueProducer->addAnomalyTracker(alert, alarmMonitor);
+
+    valueProducer->onConditionChanged(true, bucketStartTimeNs + 1);
+
+    // multiple buckets should be skipped here.
+    valueProducer->onConditionChanged(false, bucket3StartTimeNs + 100);
+
+    // No alert is fired when multiple buckets are skipped.
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
+}
+
 // Test value metric no condition, the pull on bucket boundary come in time and too late
 TEST(ValueMetricProducerTest, TestBucketBoundaryNoCondition) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
             .WillOnce(Return(true));
     sp<ValueMetricProducer> valueProducer =
             ValueMetricProducerTestHelper::createValueProducerNoConditions(pullerManager, metric);
@@ -1099,8 +1105,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     // startUpdated:true sum:0 start:11
     EXPECT_EQ(true, curBaseInfo.hasBase);
@@ -1114,8 +1121,8 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket3StartTimeNs);
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     // tartUpdated:false sum:12
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(23, curBaseInfo.base.long_value);
@@ -1131,8 +1138,8 @@
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket6StartTimeNs + 1, 36));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket6StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     // startUpdated:false sum:12
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(36, curBaseInfo.base.long_value);
@@ -1164,10 +1171,10 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // condition becomes true
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);  // First condition change.
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
@@ -1175,7 +1182,7 @@
             }))
             // condition becomes false
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1);  // Second condition change.
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120));
@@ -1190,8 +1197,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -1199,8 +1207,8 @@
 
     // pull on bucket boundary come late, condition change happens before it
     valueProducer->onConditionChanged(false, bucket2StartTimeNs + 1);
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
     EXPECT_EQ(false, curBaseInfo.hasBase);
@@ -1213,8 +1221,8 @@
 
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
 }
@@ -1227,10 +1235,10 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // condition becomes true
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
@@ -1238,7 +1246,7 @@
             }))
             // condition becomes false
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 1, 120));
@@ -1246,7 +1254,7 @@
             }))
             // condition becomes true again
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 25);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 25, 130));
@@ -1262,8 +1270,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     // startUpdated:false sum:0 start:100
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
@@ -1275,8 +1284,8 @@
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
 
@@ -1284,8 +1293,8 @@
     valueProducer->onConditionChanged(true, bucket2StartTimeNs + 25);
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {bucketSizeNs - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(130, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -1296,8 +1305,8 @@
     allData.push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 50, 140));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs + 50);
 
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(140, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
@@ -1317,18 +1326,14 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.set_aggregation_type(ValueMetric::MIN);
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-                                      pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, -1,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -1341,7 +1346,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
@@ -1349,7 +1354,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(10, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -1361,18 +1366,14 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.set_aggregation_type(ValueMetric::MAX);
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-                                      pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, -1,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -1382,7 +1383,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
@@ -1392,7 +1393,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(20, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -1404,18 +1405,14 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.set_aggregation_type(ValueMetric::AVG);
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-                                      pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, -1,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -1427,7 +1424,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval;
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(1, curInterval.sampleSize);
@@ -1436,7 +1433,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(25, curInterval.value.long_value);
     EXPECT_EQ(2, curInterval.sampleSize);
 
@@ -1452,18 +1449,14 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     metric.set_aggregation_type(ValueMetric::SUM);
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-                                      pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, -1,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -1475,7 +1468,7 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(10, curInterval.value.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
 
@@ -1483,7 +1476,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(25, curInterval.value.long_value);
 
     valueProducer.flushIfNeededLocked(bucket2StartTimeNs);
@@ -1496,18 +1489,14 @@
     metric.set_aggregation_type(ValueMetric::MIN);
     metric.set_use_diff(true);
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-                                      pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, -1,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -1517,8 +1506,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(10, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -1529,7 +1519,7 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(5, curInterval.value.long_value);
 
@@ -1539,8 +1529,8 @@
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
 
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(15, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
@@ -1550,8 +1540,8 @@
     CreateRepeatedValueLogEvent(&event4, tagId, bucket2StartTimeNs + 15, 15);
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(15, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
@@ -1568,18 +1558,14 @@
     metric.set_aggregation_type(ValueMetric::MIN);
     metric.set_use_diff(true);
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, -1, bucketStartTimeNs, bucketStartTimeNs,
-                                      pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, -1,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     LogEvent event1(/*uid=*/0, /*pid=*/0);
@@ -1592,12 +1578,13 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+            valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(10, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(20, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -1606,12 +1593,12 @@
 
     // has one slice
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(5, curInterval.value.long_value);
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(2, curInterval.value.long_value);
 
@@ -1621,14 +1608,14 @@
 
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
 
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(15, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(25, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
@@ -1638,13 +1625,13 @@
 
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
     ASSERT_EQ(1UL, valueProducer.mCurrentSlicedBucket.size());
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(15, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
-    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second[1];
-    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second[1];
+    curInterval = valueProducer.mCurrentSlicedBucket.begin()->second.intervals[1];
+    curBaseInfo = valueProducer.mCurrentBaseInfo.begin()->second.baseInfos[1];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(29, curBaseInfo.base.long_value);
     EXPECT_EQ(true, curInterval.hasValue);
@@ -1677,9 +1664,9 @@
     metric.set_use_zero_default_base(true);
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
                 return true;
@@ -1690,9 +1677,9 @@
 
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     auto iter = valueProducer->mCurrentSlicedBucket.begin();
-    auto& interval1 = iter->second[0];
+    auto& interval1 = iter->second.intervals[0];
     auto iterBase = valueProducer->mCurrentBaseInfo.begin();
-    auto& baseInfo1 = iterBase->second[0];
+    auto& baseInfo1 = iterBase->second.baseInfos[0];
     EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo1.hasBase);
     EXPECT_EQ(3, baseInfo1.base.long_value);
@@ -1726,8 +1713,8 @@
     }
     EXPECT_TRUE(it != iter);
     EXPECT_TRUE(itBase != iterBase);
-    auto& interval2 = it->second[0];
-    auto& baseInfo2 = itBase->second[0];
+    auto& interval2 = it->second.intervals[0];
+    auto& baseInfo2 = itBase->second.baseInfos[0];
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo2.hasBase);
     EXPECT_EQ(4, baseInfo2.base.long_value);
@@ -1753,9 +1740,9 @@
     metric.set_use_zero_default_base(true);
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
                 return true;
@@ -1766,9 +1753,10 @@
 
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     const auto& it = valueProducer->mCurrentSlicedBucket.begin();
-    ValueMetricProducer::Interval& interval1 = it->second[0];
+    ValueMetricProducer::Interval& interval1 = it->second.intervals[0];
     ValueMetricProducer::BaseInfo& baseInfo1 =
-            valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())->second[0];
+            valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat())
+                    ->second.baseInfos[0];
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo1.hasBase);
     EXPECT_EQ(3, baseInfo1.base.long_value);
@@ -1795,9 +1783,10 @@
         }
     }
     EXPECT_TRUE(it2 != it);
-    ValueMetricProducer::Interval& interval2 = it2->second[0];
+    ValueMetricProducer::Interval& interval2 = it2->second.intervals[0];
     ValueMetricProducer::BaseInfo& baseInfo2 =
-            valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())->second[0];
+            valueProducer->mCurrentBaseInfo.find(it2->first.getDimensionKeyInWhat())
+                    ->second.baseInfos[0];
     EXPECT_EQ(2, it2->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo2.hasBase);
     EXPECT_EQ(4, baseInfo2.base.long_value);
@@ -1826,14 +1815,16 @@
     // Get new references now that entries have been deleted from the map
     const auto& it3 = valueProducer->mCurrentSlicedBucket.begin();
     const auto& it4 = std::next(valueProducer->mCurrentSlicedBucket.begin());
-    ASSERT_EQ(it3->second.size(), 1);
-    ASSERT_EQ(it4->second.size(), 1);
-    ValueMetricProducer::Interval& interval3 = it3->second[0];
-    ValueMetricProducer::Interval& interval4 = it4->second[0];
+    ASSERT_EQ(it3->second.intervals.size(), 1);
+    ASSERT_EQ(it4->second.intervals.size(), 1);
+    ValueMetricProducer::Interval& interval3 = it3->second.intervals[0];
+    ValueMetricProducer::Interval& interval4 = it4->second.intervals[0];
     ValueMetricProducer::BaseInfo& baseInfo3 =
-            valueProducer->mCurrentBaseInfo.find(it3->first.getDimensionKeyInWhat())->second[0];
+            valueProducer->mCurrentBaseInfo.find(it3->first.getDimensionKeyInWhat())
+                    ->second.baseInfos[0];
     ValueMetricProducer::BaseInfo& baseInfo4 =
-            valueProducer->mCurrentBaseInfo.find(it4->first.getDimensionKeyInWhat())->second[0];
+            valueProducer->mCurrentBaseInfo.find(it4->first.getDimensionKeyInWhat())
+                    ->second.baseInfos[0];
 
     EXPECT_EQ(true, baseInfo3.hasBase);
     EXPECT_EQ(5, baseInfo3.base.long_value);
@@ -1858,9 +1849,9 @@
     metric.mutable_dimensions_in_what()->add_child()->set_field(1);
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 1, 3));
                 return true;
@@ -1871,9 +1862,9 @@
 
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     auto iter = valueProducer->mCurrentSlicedBucket.begin();
-    auto& interval1 = iter->second[0];
+    auto& interval1 = iter->second.intervals[0];
     auto iterBase = valueProducer->mCurrentBaseInfo.begin();
-    auto& baseInfo1 = iterBase->second[0];
+    auto& baseInfo1 = iterBase->second.baseInfos[0];
     EXPECT_EQ(1, iter->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo1.hasBase);
     EXPECT_EQ(3, baseInfo1.base.long_value);
@@ -1909,8 +1900,8 @@
     }
     EXPECT_TRUE(it != iter);
     EXPECT_TRUE(itBase != iterBase);
-    auto interval2 = it->second[0];
-    auto baseInfo2 = itBase->second[0];
+    auto interval2 = it->second.intervals[0];
+    auto baseInfo2 = itBase->second.baseInfos[0];
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo2.hasBase);
     EXPECT_EQ(4, baseInfo2.base.long_value);
@@ -1923,8 +1914,8 @@
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket4StartTimeNs);
     // Only one interval left. One was trimmed.
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    interval2 = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     EXPECT_EQ(true, baseInfo2.hasBase);
     EXPECT_EQ(5, baseInfo2.base.long_value);
@@ -1937,8 +1928,8 @@
     allData.push_back(CreateTwoValueLogEvent(tagId, bucket5StartTimeNs + 1, 2, 14));
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket5StartTimeNs);
 
-    interval2 = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    interval2 = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    baseInfo2 = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, baseInfo2.hasBase);
     EXPECT_EQ(14, baseInfo2.base.long_value);
     EXPECT_EQ(false, interval2.hasValue);
@@ -1961,9 +1952,9 @@
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     // Used by onConditionChanged.
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
                 return true;
@@ -1977,8 +1968,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo& curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -1995,9 +1987,9 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);  // Condition change to true.
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
@@ -2014,8 +2006,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo& curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo& curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -2034,16 +2027,16 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 50));
                 return false;
             }))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 1);  // Condition change to false.
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 100));
@@ -2064,8 +2057,9 @@
     valueProducer->onConditionChanged(false, bucketStartTimeNs + 1);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(false, valueProducer->mHasGlobalBase);
@@ -2077,9 +2071,9 @@
     metric.set_max_pull_delay_sec(0);
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 120));
                 return true;
@@ -2097,19 +2091,15 @@
 TEST(ValueMetricProducerTest, TestResetBaseOnPullTooLate) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
 
     ValueMetricProducer valueProducer(kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard,
-                                      logEventMatcherIndex, eventMatcherWizard, tagId,
+                                      protoHash, logEventMatcherIndex, eventMatcherWizard, tagId,
                                       bucket2StartTimeNs, bucket2StartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
     valueProducer.mCondition = ConditionState::kFalse;
@@ -2124,9 +2114,9 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 1, _))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, 100));
                 return true;
@@ -2141,8 +2131,9 @@
     valueProducer->mHasGlobalBase = true;
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(100, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -2156,12 +2147,12 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // First onConditionChanged
             .WillOnce(Return(false))
             // Second onConditionChanged
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
@@ -2194,8 +2185,9 @@
     // Contains base from last pull which was successful.
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(140, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -2233,10 +2225,10 @@
     metric.set_condition(StringToId("SCREEN_ON"));
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 2, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 2, _))
             // First onConditionChanged
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 for (int i = 0; i < 2000; i++) {
                     data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, i));
                 }
@@ -2290,10 +2282,10 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // First onConditionChanged
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 2);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 120));
@@ -2301,7 +2293,7 @@
             }))
             // Second onConditionChanged
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
@@ -2332,8 +2324,9 @@
     // Contains base from last pull which was successful.
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(140, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -2369,10 +2362,10 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // First onConditionChanged
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 2);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 120));
@@ -2380,7 +2373,7 @@
             }))
             // Second onConditionChanged
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 3);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 8, 130));
@@ -2411,8 +2404,9 @@
     // Last pull failed so base has been reset.
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(false, valueProducer->mHasGlobalBase);
@@ -2442,10 +2436,10 @@
 TEST(ValueMetricProducerTest, TestEmptyDataResetsBase_onDataPulled) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
             // Start bucket.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
                 return true;
@@ -2475,17 +2469,17 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // First onConditionChanged
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
                 return true;
             }))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
                 data->clear();
                 return true;
@@ -2498,8 +2492,9 @@
     valueProducer->onConditionChanged(true, bucketStartTimeNs + 10);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
@@ -2507,8 +2502,8 @@
     // Empty pull.
     valueProducer->onConditionChanged(false, bucketStartTimeNs + 10);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
     EXPECT_EQ(false, valueProducer->mHasGlobalBase);
@@ -2518,24 +2513,24 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // First onConditionChanged
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
                 return true;
             }))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 11);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 2));
                 return true;
             }))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 12);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 5));
@@ -2551,8 +2546,9 @@
     valueProducer->onConditionChanged(true, bucketStartTimeNs + 12);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval& curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
@@ -2562,8 +2558,8 @@
     allData.clear();
     valueProducer->onDataPulled(allData, /** succeed */ true, bucket2StartTimeNs);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     // Data is empty, base should be reset.
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(5, curBaseInfo.base.long_value);
@@ -2582,10 +2578,10 @@
     metric.set_condition(StringToId("SCREEN_ON"));
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _))
             // First onConditionChanged
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
                 return true;
@@ -2608,14 +2604,14 @@
     ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
     auto iterator = valueProducer->mCurrentSlicedBucket.begin();
     auto baseInfoIter = valueProducer->mCurrentBaseInfo.begin();
-    EXPECT_EQ(true, baseInfoIter->second[0].hasBase);
-    EXPECT_EQ(2, baseInfoIter->second[0].base.long_value);
-    EXPECT_EQ(false, iterator->second[0].hasValue);
+    EXPECT_EQ(true, baseInfoIter->second.baseInfos[0].hasBase);
+    EXPECT_EQ(2, baseInfoIter->second.baseInfos[0].base.long_value);
+    EXPECT_EQ(false, iterator->second.intervals[0].hasValue);
     iterator++;
     baseInfoIter++;
-    EXPECT_EQ(false, baseInfoIter->second[0].hasBase);
-    EXPECT_EQ(1, baseInfoIter->second[0].base.long_value);
-    EXPECT_EQ(false, iterator->second[0].hasValue);
+    EXPECT_EQ(false, baseInfoIter->second.baseInfos[0].hasBase);
+    EXPECT_EQ(1, baseInfoIter->second.baseInfos[0].base.long_value);
+    EXPECT_EQ(false, iterator->second.intervals[0].hasValue);
 
     EXPECT_EQ(true, valueProducer->mHasGlobalBase);
 }
@@ -2625,19 +2621,19 @@
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     int64_t partialBucketSplitTimeNs = bucketStartTimeNs + bucketSizeNs / 2;
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // Initialization.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
                 return true;
             }))
             // notifyAppUpgrade.
-            .WillOnce(Invoke([partialBucketSplitTimeNs](
-                                     int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                     vector<std::shared_ptr<LogEvent>>* data, bool) {
+            .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
+                                                        const int64_t eventTimeNs,
+                                                        vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10));
@@ -2681,10 +2677,10 @@
 TEST(ValueMetricProducerTest, TestBucketBoundariesOnConditionChange) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // Second onConditionChanged.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 10, 5));
@@ -2692,7 +2688,7 @@
             }))
             // Third onConditionChanged.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucket3StartTimeNs + 10);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket3StartTimeNs + 10, 7));
@@ -2714,8 +2710,8 @@
 
     valueProducer->onConditionChanged(true, bucket2StartTimeNs + 10);
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curBaseInfo.hasBase);
     EXPECT_EQ(5, curBaseInfo.base.long_value);
     EXPECT_EQ(false, curInterval.hasValue);
@@ -2752,10 +2748,10 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
             // Initialization.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
                 return true;
@@ -2782,19 +2778,19 @@
 
     int64_t partialBucketSplitTimeNs = bucket2StartTimeNs + 2;
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // Initialization.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
                 return true;
             }))
             // notifyAppUpgrade.
-            .WillOnce(Invoke([partialBucketSplitTimeNs](
-                                     int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                     vector<std::shared_ptr<LogEvent>>* data, bool) {
+            .WillOnce(Invoke([partialBucketSplitTimeNs](int tagId, const ConfigKey&,
+                                                        const int64_t eventTimeNs,
+                                                        vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, partialBucketSplitTimeNs);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, partialBucketSplitTimeNs, 10));
@@ -2822,10 +2818,10 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // First on condition changed.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
@@ -2833,7 +2829,7 @@
             }))
             // Second on condition changed.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
@@ -2849,8 +2845,8 @@
     valueProducer->onConditionChanged(false, bucketStartTimeNs + 12);
 
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    auto curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    auto curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(2, curInterval.value.long_value);
 
@@ -2867,10 +2863,10 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // First condition change.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 1));
@@ -2878,7 +2874,7 @@
             }))
             // 2nd condition change.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 8);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1));
@@ -2886,7 +2882,7 @@
             }))
             // 3rd condition change.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs, 1));
@@ -2920,29 +2916,25 @@
 TEST(ValueMetricProducerTest, TestPullNeededFastDump) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
 
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
             // Initial pull.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     ProtoOutputStream output;
@@ -2958,29 +2950,25 @@
 TEST(ValueMetricProducerTest, TestFastDumpWithoutCurrentBucket) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
 
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs, _))
             // Initial pull.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     vector<shared_ptr<LogEvent>> allData;
@@ -3002,28 +2990,24 @@
 TEST(ValueMetricProducerTest, TestPullNeededNoTimeConstraints) {
     ValueMetric metric = ValueMetricProducerTestHelper::createMetric();
 
-    UidMap uidMap;
-    SimpleAtomMatcher atomMatcher;
-    atomMatcher.set_atom_id(tagId);
     sp<EventMatcherWizard> eventMatcherWizard =
-            new EventMatcherWizard({new SimpleLogMatchingTracker(
-                    atomMatcherId, logEventMatcherIndex, atomMatcher, uidMap)});
+            createEventMatcherWizard(tagId, logEventMatcherIndex);
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, kConfigKey, _, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, kConfigKey, _)).WillRepeatedly(Return());
 
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // Initial pull.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
                 data->clear();
                 data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs, tagId, 1, 1));
                 return true;
             }))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
                 data->clear();
                 data->push_back(
@@ -3031,9 +3015,9 @@
                 return true;
             }));
 
-    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, logEventMatcherIndex,
-                                      eventMatcherWizard, tagId, bucketStartTimeNs,
-                                      bucketStartTimeNs, pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, {}, wizard, protoHash,
+                                      logEventMatcherIndex, eventMatcherWizard, tagId,
+                                      bucketStartTimeNs, bucketStartTimeNs, pullerManager);
     valueProducer.prepareFirstBucket();
 
     ProtoOutputStream output;
@@ -3069,10 +3053,10 @@
     metric.set_use_diff(false);
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // condition becomes true
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
@@ -3080,7 +3064,7 @@
             }))
             // condition becomes false
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 20));
@@ -3095,8 +3079,9 @@
     // has one slice
     ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(true, curInterval.hasValue);
     EXPECT_EQ(20, curInterval.value.long_value);
@@ -3108,8 +3093,8 @@
 
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {20}, {50 - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
-    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+    curInterval = valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
 }
@@ -3119,10 +3104,10 @@
     metric.set_use_diff(false);
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 8, _))
             // condition becomes true
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
                 return true;
@@ -3141,8 +3126,9 @@
     assertPastBucketValuesSingleKey(valueProducer->mPastBuckets, {30}, {bucketSizeNs - 8},
                                     {bucketStartTimeNs}, {bucket2StartTimeNs});
     ValueMetricProducer::Interval curInterval =
-            valueProducer->mCurrentSlicedBucket.begin()->second[0];
-    ValueMetricProducer::BaseInfo curBaseInfo = valueProducer->mCurrentBaseInfo.begin()->second[0];
+            valueProducer->mCurrentSlicedBucket.begin()->second.intervals[0];
+    ValueMetricProducer::BaseInfo curBaseInfo =
+            valueProducer->mCurrentBaseInfo.begin()->second.baseInfos[0];
     EXPECT_EQ(false, curBaseInfo.hasBase);
     EXPECT_EQ(false, curInterval.hasValue);
 }
@@ -3170,10 +3156,10 @@
     metric.set_use_diff(false);
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // condition becomes true
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 8);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30, 10));
@@ -3210,10 +3196,10 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 20, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 20, _))
             // Condition change to true.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20, 10));
                 return true;
@@ -3256,10 +3242,10 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 50, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 50, _))
             // Condition change to true.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
                 return true;
@@ -3314,10 +3300,10 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // Condition change to true.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
@@ -3325,7 +3311,7 @@
             }))
             // Dump report requested.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 100);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket2StartTimeNs + 100, 15));
@@ -3380,10 +3366,10 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // Condition change to true.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
@@ -3391,7 +3377,7 @@
             }))
             // Dump report requested.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10000);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 100, 15));
@@ -3436,10 +3422,10 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // Condition change to true.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 10));
@@ -3486,10 +3472,10 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // Condition change to true.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
@@ -3497,7 +3483,7 @@
             }))
             // Dump report requested.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucket4StartTimeNs + 10);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucket4StartTimeNs + 1000, 15));
@@ -3560,10 +3546,10 @@
     metric.set_min_bucket_size_nanos(10000000000);  // 10 seconds
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // Condition change to true.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
@@ -3571,7 +3557,7 @@
             }))
             // Dump report requested.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 9000000);
                 data->clear();
                 data->push_back(
@@ -3651,10 +3637,10 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // Condition change to true.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(
@@ -3663,7 +3649,7 @@
             }))
             // Dump report requested.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 15 * NS_PER_SEC);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(
@@ -3740,10 +3726,10 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // Condition change to true.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
@@ -3751,7 +3737,7 @@
             }))
             // App Update.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 1000);
                 data->clear();
                 data->push_back(
@@ -3806,10 +3792,10 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, bucketStartTimeNs + 10, _))
             // Condition change to true.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 10));
                 return true;
@@ -3857,10 +3843,10 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithCondition();
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // First condition change event.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
                 for (int i = 0; i < 2000; i++) {
                     data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 1, i));
@@ -3877,7 +3863,7 @@
             .WillOnce(Return(false))
             .WillOnce(Return(false))
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 220);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 220, 10));
@@ -3976,10 +3962,10 @@
     // Set up ValueMetricProducer.
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE");
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // ValueMetricProducer initialized.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
@@ -3987,34 +3973,38 @@
             }))
             // Screen state change to ON.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
-                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5);
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5 * NS_PER_SEC);
                 data->clear();
-                data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5));
+                data->push_back(
+                        CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5 * NS_PER_SEC, 5));
                 return true;
             }))
             // Screen state change to OFF.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
-                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10);
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
                 data->clear();
-                data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10, 9));
+                data->push_back(
+                        CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 9));
                 return true;
             }))
             // Screen state change to ON.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
-                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15);
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15 * NS_PER_SEC);
                 data->clear();
-                data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21));
+                data->push_back(CreateRepeatedValueLogEvent(
+                        tagId, bucketStartTimeNs + 15 * NS_PER_SEC, 21));
                 return true;
             }))
             // Dump report requested.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
-                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
                 data->clear();
-                data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30));
+                data->push_back(CreateRepeatedValueLogEvent(
+                        tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 30));
                 return true;
             }));
 
@@ -4034,138 +4024,174 @@
     // Base for dimension key {}
     auto it = valueProducer->mCurrentSlicedBucket.begin();
     auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(3, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{}, kStateUnknown}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
 
     // Bucket status after screen state change kStateUnknown->ON.
     auto screenEvent = CreateScreenStateChangedEvent(
-            bucketStartTimeNs + 5, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-    StateManager::getInstance().onLogEvent(*screenEvent);
-    ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    // Base for dimension key {}
-    it = valueProducer->mCurrentSlicedBucket.begin();
-    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(5, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
-    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
-    // Value for dimension, state key {{}, kStateUnknown}
-    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
-    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
-    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
-
-    // Bucket status after screen state change ON->OFF.
-    screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10,
-                                                android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+            bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
     StateManager::getInstance().onLogEvent(*screenEvent);
     ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
     // Base for dimension key {}
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(9, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{}, ON}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(4, it->second[0].value.long_value);
+    EXPECT_EQ(0, it->second.intervals.size());
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
     // Value for dimension, state key {{}, kStateUnknown}
     it++;
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+    assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+                         bucketStartTimeNs + 5 * NS_PER_SEC);
+
+    // Bucket status after screen state change ON->OFF.
+    screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
+                                                android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+    StateManager::getInstance().onLogEvent(*screenEvent);
+    ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension key {}
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for dimension, state key {{}, OFF}
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_EQ(0, it->second.intervals.size());
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+    // Value for dimension, state key {{}, ON}
+    it++;
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+    assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+                         bucketStartTimeNs + 10 * NS_PER_SEC);
+    // Value for dimension, state key {{}, kStateUnknown}
+    it++;
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+    assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+                         bucketStartTimeNs + 5 * NS_PER_SEC);
 
     // Bucket status after screen state change OFF->ON.
-    screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15,
+    screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC,
                                                 android::view::DisplayStateEnum::DISPLAY_STATE_ON);
     StateManager::getInstance().onLogEvent(*screenEvent);
     ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
     // Base for dimension key {}
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(21, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(21, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{}, OFF}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(12, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(12, it->second.intervals[0].value.long_value);
+    assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+                         bucketStartTimeNs + 15 * NS_PER_SEC);
     // Value for dimension, state key {{}, ON}
     it++;
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(4, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+    assertConditionTimer(it->second.conditionTimer, true, 5 * NS_PER_SEC,
+                         bucketStartTimeNs + 15 * NS_PER_SEC);
     // Value for dimension, state key {{}, kStateUnknown}
     it++;
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+    assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+                         bucketStartTimeNs + 5 * NS_PER_SEC);
 
     // Start dump report and check output.
     ProtoOutputStream output;
     std::set<string> strSet;
-    valueProducer->onDumpReport(bucketStartTimeNs + 50, true /* include recent buckets */, true,
-                                NO_TIME_CONSTRAINTS, &strSet, &output);
+    valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
+                                true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+                                &strSet, &output);
 
     StatsLogReport report = outputStreamToProto(&output);
     EXPECT_TRUE(report.has_value_metrics());
     ASSERT_EQ(3, report.value_metrics().data_size());
 
+    // {{}, kStateUnknown}
     auto data = report.value_metrics().data(0);
     ASSERT_EQ(1, data.bucket_info_size());
     EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
     EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
     EXPECT_TRUE(data.slice_by_state(0).has_value());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */, data.slice_by_state(0).value());
+    EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
 
+    // {{}, ON}
     data = report.value_metrics().data(1);
     ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size());
     EXPECT_EQ(13, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
     EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
     EXPECT_TRUE(data.slice_by_state(0).has_value());
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_ON, data.slice_by_state(0).value());
+    EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
 
+    // {{}, OFF}
     data = report.value_metrics().data(2);
     ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size());
     EXPECT_EQ(12, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
     EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
     EXPECT_TRUE(data.slice_by_state(0).has_value());
     EXPECT_EQ(android::view::DisplayStateEnum::DISPLAY_STATE_OFF, data.slice_by_state(0).value());
+    EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
 }
 
 /*
@@ -4178,10 +4204,10 @@
     // Set up ValueMetricProducer.
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithState("SCREEN_STATE_ONOFF");
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // ValueMetricProducer initialized.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
@@ -4189,10 +4215,11 @@
             }))
             // Screen state change to ON.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
-                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5);
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 5 * NS_PER_SEC);
                 data->clear();
-                data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5, 5));
+                data->push_back(
+                        CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 5 * NS_PER_SEC, 5));
                 return true;
             }))
             // Screen state change to VR has no pull because it is in the same
@@ -4203,18 +4230,20 @@
 
             // Screen state change to OFF.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
-                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15);
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 15 * NS_PER_SEC);
                 data->clear();
-                data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 15, 21));
+                data->push_back(CreateRepeatedValueLogEvent(
+                        tagId, bucketStartTimeNs + 15 * NS_PER_SEC, 21));
                 return true;
             }))
             // Dump report requested.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
-                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50);
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
                 data->clear();
-                data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 50, 30));
+                data->push_back(CreateRepeatedValueLogEvent(
+                        tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 30));
                 return true;
             }));
 
@@ -4245,147 +4274,193 @@
     // Base for dimension key {}
     auto it = valueProducer->mCurrentSlicedBucket.begin();
     auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(3, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{}, {kStateUnknown}}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
 
     // Bucket status after screen state change kStateUnknown->ON.
     auto screenEvent = CreateScreenStateChangedEvent(
-            bucketStartTimeNs + 5, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-    StateManager::getInstance().onLogEvent(*screenEvent);
-    ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    // Base for dimension key {}
-    it = valueProducer->mCurrentSlicedBucket.begin();
-    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(5, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
-    EXPECT_EQ(screenOnGroup.group_id(),
-              itBase->second[0].currentState.getValues()[0].mValue.long_value);
-    // Value for dimension, state key {{}, kStateUnknown}
-    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
-    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
-    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
-
-    // Bucket status after screen state change ON->VR.
-    // Both ON and VR are in the same state group, so the base should not change.
-    screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10,
-                                                android::view::DisplayStateEnum::DISPLAY_STATE_VR);
-    StateManager::getInstance().onLogEvent(*screenEvent);
-    ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    // Base for dimension key {}
-    it = valueProducer->mCurrentSlicedBucket.begin();
-    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(5, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
-    EXPECT_EQ(screenOnGroup.group_id(),
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
-    // Value for dimension, state key {{}, kStateUnknown}
-    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
-    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
-    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
-
-    // Bucket status after screen state change VR->ON.
-    // Both ON and VR are in the same state group, so the base should not change.
-    screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 12,
-                                                android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-    StateManager::getInstance().onLogEvent(*screenEvent);
-    ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    // Base for dimension key {}
-    it = valueProducer->mCurrentSlicedBucket.begin();
-    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(5, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
-    EXPECT_EQ(screenOnGroup.group_id(),
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
-    // Value for dimension, state key {{}, kStateUnknown}
-    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
-    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
-    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
-
-    // Bucket status after screen state change VR->OFF.
-    screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15,
-                                                android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+            bucketStartTimeNs + 5 * NS_PER_SEC, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
     StateManager::getInstance().onLogEvent(*screenEvent);
     ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
     // Base for dimension key {}
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(21, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
-    EXPECT_EQ(screenOffGroup.group_id(),
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(screenOnGroup.group_id(),
+              itBase->second.currentState.getValues()[0].mValue.long_value);
     // Value for dimension, state key {{}, ON GROUP}
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(screenOnGroup.group_id(),
-              it->first.getStateValuesKey().getValues()[0].mValue.long_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(16, it->second[0].value.long_value);
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
     // Value for dimension, state key {{}, kStateUnknown}
     it++;
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+    assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+                         bucketStartTimeNs + 5 * NS_PER_SEC);
+
+    // Bucket status after screen state change ON->VR.
+    // Both ON and VR are in the same state group, so the base should not change.
+    screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
+                                                android::view::DisplayStateEnum::DISPLAY_STATE_VR);
+    StateManager::getInstance().onLogEvent(*screenEvent);
+    ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension key {}
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(screenOnGroup.group_id(),
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for dimension, state key {{}, ON GROUP}
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(screenOnGroup.group_id(),
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
+    // Value for dimension, state key {{}, kStateUnknown}
+    it++;
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+    assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+                         bucketStartTimeNs + 5 * NS_PER_SEC);
+
+    // Bucket status after screen state change VR->ON.
+    // Both ON and VR are in the same state group, so the base should not change.
+    screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 12 * NS_PER_SEC,
+                                                android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+    StateManager::getInstance().onLogEvent(*screenEvent);
+    ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension key {}
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(screenOnGroup.group_id(),
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for dimension, state key {{}, ON GROUP}
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(screenOnGroup.group_id(),
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 5 * NS_PER_SEC);
+    // Value for dimension, state key {{}, kStateUnknown}
+    it++;
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+    assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+                         bucketStartTimeNs + 5 * NS_PER_SEC);
+
+    // Bucket status after screen state change VR->OFF.
+    screenEvent = CreateScreenStateChangedEvent(bucketStartTimeNs + 15 * NS_PER_SEC,
+                                                android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
+    StateManager::getInstance().onLogEvent(*screenEvent);
+    ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension key {}
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(21, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(screenOffGroup.group_id(),
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for dimension, state key {{}, OFF GROUP}
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(screenOffGroup.group_id(),
+              it->first.getStateValuesKey().getValues()[0].mValue.long_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 15 * NS_PER_SEC);
+    // Value for dimension, state key {{}, ON GROUP}
+    it++;
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(screenOnGroup.group_id(),
+              it->first.getStateValuesKey().getValues()[0].mValue.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(16, it->second.intervals[0].value.long_value);
+    assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+                         bucketStartTimeNs + 15 * NS_PER_SEC);
+    // Value for dimension, state key {{}, kStateUnknown}
+    it++;
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+    assertConditionTimer(it->second.conditionTimer, false, 5 * NS_PER_SEC,
+                         bucketStartTimeNs + 5 * NS_PER_SEC);
 
     // Start dump report and check output.
     ProtoOutputStream output;
     std::set<string> strSet;
-    valueProducer->onDumpReport(bucketStartTimeNs + 50, true /* include recent buckets */, true,
-                                NO_TIME_CONSTRAINTS, &strSet, &output);
+    valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
+                                true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+                                &strSet, &output);
 
     StatsLogReport report = outputStreamToProto(&output);
     EXPECT_TRUE(report.has_value_metrics());
     ASSERT_EQ(3, report.value_metrics().data_size());
 
+    // {{}, kStateUnknown}
     auto data = report.value_metrics().data(0);
     ASSERT_EQ(1, data.bucket_info_size());
     EXPECT_EQ(2, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
     EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
     EXPECT_TRUE(data.slice_by_state(0).has_value());
     EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value());
+    EXPECT_EQ(5 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
 
+    // {{}, ON GROUP}
     data = report.value_metrics().data(1);
     ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size());
     EXPECT_EQ(16, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
     EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
     EXPECT_TRUE(data.slice_by_state(0).has_group_id());
     EXPECT_EQ(screenOnGroup.group_id(), data.slice_by_state(0).group_id());
+    EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
 
+    // {{}, OFF GROUP}
     data = report.value_metrics().data(2);
     ASSERT_EQ(1, report.value_metrics().data(2).bucket_info_size());
     EXPECT_EQ(9, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
     EXPECT_EQ(SCREEN_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
     EXPECT_TRUE(data.slice_by_state(0).has_group_id());
     EXPECT_EQ(screenOffGroup.group_id(), data.slice_by_state(0).group_id());
+    EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
 }
 
 /*
@@ -4407,11 +4482,40 @@
     auto fieldsInState = stateLink->mutable_fields_in_state();
     *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
 
+    /*
+    NOTE: "1" denotes uid 1 and "2" denotes uid 2.
+                    bucket # 1                            bucket # 2
+    10     20     30     40     50     60     70     80     90    100    110    120 (seconds)
+    |------------------------------------------|---------------------------------|--
+
+                                                                                    (kStateUnknown)
+    1
+    |-------------|
+          20
+
+    2
+    |----------------------------|
+                 40
+
+                                                                                    (FOREGROUND)
+                  1                                                       1
+                  |----------------------------|-------------|            |------|
+                               40                     20                     10
+
+
+                                                                                    (BACKGROUND)
+                                                             1
+                                                             |------------|
+                                                                   20
+                                 2
+                                 |-------------|---------------------------------|
+                                       20                      50
+    */
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // ValueMetricProducer initialized.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
                 data->clear();
                 data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs, 2 /*uid*/, 7));
@@ -4420,65 +4524,65 @@
             }))
             // Uid 1 process state change from kStateUnknown -> Foreground
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
-                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20);
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
                 data->clear();
-                data->push_back(
-                        CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 1 /*uid*/, 6));
+                data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+                                                       1 /*uid*/, 6));
 
                 // This event should be skipped.
-                data->push_back(
-                        CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20, 2 /*uid*/, 8));
+                data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+                                                       2 /*uid*/, 8));
                 return true;
             }))
             // Uid 2 process state change from kStateUnknown -> Background
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
-                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40);
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
                 data->clear();
-                data->push_back(
-                        CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40, 2 /*uid*/, 9));
+                data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+                                                       2 /*uid*/, 9));
 
                 // This event should be skipped.
-                data->push_back(
-                        CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40, 1 /*uid*/, 12));
+                data->push_back(CreateTwoValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+                                                       1 /*uid*/, 12));
                 return true;
             }))
             // Uid 1 process state change from Foreground -> Background
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
-                EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 20);
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 20 * NS_PER_SEC);
                 data->clear();
-                data->push_back(
-                        CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20, 1 /*uid*/, 13));
+                data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20 * NS_PER_SEC,
+                                                       1 /*uid*/, 13));
 
                 // This event should be skipped.
-                data->push_back(
-                        CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20, 2 /*uid*/, 11));
+                data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 20 * NS_PER_SEC,
+                                                       2 /*uid*/, 11));
                 return true;
             }))
             // Uid 1 process state change from Background -> Foreground
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
-                EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 40);
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 40 * NS_PER_SEC);
                 data->clear();
-                data->push_back(
-                        CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40, 1 /*uid*/, 17));
+                data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40 * NS_PER_SEC,
+                                                       1 /*uid*/, 17));
 
                 // This event should be skipped.
-                data->push_back(
-                        CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40, 2 /*uid */, 15));
+                data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 40 * NS_PER_SEC,
+                                                       2 /*uid */, 15));
                 return true;
             }))
             // Dump report pull.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
-                EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50);
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
                 data->clear();
-                data->push_back(
-                        CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50, 2 /*uid*/, 20));
-                data->push_back(
-                        CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50, 1 /*uid*/, 21));
+                data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50 * NS_PER_SEC,
+                                                       2 /*uid*/, 20));
+                data->push_back(CreateTwoValueLogEvent(tagId, bucket2StartTimeNs + 50 * NS_PER_SEC,
+                                                       1 /*uid*/, 21));
                 return true;
             }));
 
@@ -4497,113 +4601,153 @@
     // Base for dimension key {uid 1}.
     auto it = valueProducer->mCurrentSlicedBucket.begin();
     auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(3, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{uid 1}, kStateUnknown}
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
     // Base for dimension key {uid 2}
     it++;
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(7, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(7, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for dimension, state key {{uid 2}, kStateUnknown}
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
 
     // Bucket status after uid 1 process state change kStateUnknown -> Foreground.
-    auto uidProcessEvent = CreateUidProcessStateChangedEvent(
-            bucketStartTimeNs + 20, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+    auto uidProcessEvent =
+            CreateUidProcessStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
+                                              android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
     StateManager::getInstance().onLogEvent(*uidProcessEvent);
-    ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+    ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
     // Base for dimension key {uid 1}.
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(6, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(6, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 1, kStateUnknown}.
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(3, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(3, it->second.intervals[0].value.long_value);
+    assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+                         bucketStartTimeNs + 20 * NS_PER_SEC);
+    // Value for key {uid 1, FOREGROUND}.
+    it++;
+    ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
 
-    // Base for dimension key {uid 2}
+    // Base for dimension key {uid 2}.
     it++;
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(7, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(7, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
-    // Value for key {uid 2, kStateUnknown}
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {uid 2, kStateUnknown}.
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
-    EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
 
     // Bucket status after uid 2 process state change kStateUnknown -> Background.
-    uidProcessEvent = CreateUidProcessStateChangedEvent(
-            bucketStartTimeNs + 40, 2 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+    uidProcessEvent =
+            CreateUidProcessStateChangedEvent(bucketStartTimeNs + 40 * NS_PER_SEC, 2 /* uid */,
+                                              android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
     StateManager::getInstance().onLogEvent(*uidProcessEvent);
-    ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
-    // Base for dimension key {uid 1}.
+    ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension key {uid 2}.
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(6, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(9, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {uid 2, BACKGROUND}.
+    ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
+
+    // Base for dimension key {uid 1}.
+    it++;
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(6, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 1, kStateUnknown}.
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
-    EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(3, it->second[0].value.long_value);
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(3, it->second.intervals[0].value.long_value);
+    assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+                         bucketStartTimeNs + 20 * NS_PER_SEC);
 
-    // Base for dimension key {uid 2}
+    // Value for key {uid 1, FOREGROUND}.
     it++;
-    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(9, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
-    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+    ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
     // Value for key {uid 2, kStateUnknown}
+    it++;
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
-    EXPECT_EQ(-1, it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+    assertConditionTimer(it->second.conditionTimer, false, 40 * NS_PER_SEC,
+                         bucketStartTimeNs + 40 * NS_PER_SEC);
 
     // Pull at end of first bucket.
     vector<shared_ptr<LogEvent>> allData;
@@ -4620,36 +4764,40 @@
     it = valueProducer->mCurrentSlicedBucket.begin();
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(15, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 2, BACKGROUND}.
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+    EXPECT_EQ(20 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
 
     // Base for dimension key {uid 1}
     it++;
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(10, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(10, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 1, kStateUnknown}
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* kStateTracker::kUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
+    assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+    EXPECT_EQ(20 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
 
     // Value for key {uid 1, FOREGROUND}
     it++;
@@ -4658,7 +4806,9 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+    EXPECT_EQ(40 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
 
     // Value for key {uid 2, kStateUnknown}
     it++;
@@ -4667,106 +4817,54 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* kStateTracker::kUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
+    assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
+    EXPECT_EQ(40 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
 
     // Bucket status after uid 1 process state change from Foreground -> Background.
-    uidProcessEvent = CreateUidProcessStateChangedEvent(
-            bucket2StartTimeNs + 20, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+    uidProcessEvent =
+            CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
+                                              android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
     StateManager::getInstance().onLogEvent(*uidProcessEvent);
 
-    ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+    ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
     ASSERT_EQ(4UL, valueProducer->mPastBuckets.size());
     ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
     // Base for dimension key {uid 2}.
     it = valueProducer->mCurrentSlicedBucket.begin();
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(15, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 2, BACKGROUND}.
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+
     // Base for dimension key {uid 1}
     it++;
     itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(13, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(13, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {uid 1, kStateUnknown}
     ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
     EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
-    // Value for key {uid 1, FOREGROUND}
-    it++;
-    ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
-    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
-    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
-    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
-              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(3, it->second[0].value.long_value);
-    // Value for key {uid 2, kStateUnknown}
-    it++;
-    ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
-    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
-    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
-    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
-
-    // Bucket status after uid 1 process state change Background->Foreground.
-    uidProcessEvent = CreateUidProcessStateChangedEvent(
-            bucket2StartTimeNs + 40, 1 /* uid */, android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
-    StateManager::getInstance().onLogEvent(*uidProcessEvent);
-
-    ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
-    ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
-    // Base for dimension key {uid 2}
-    it = valueProducer->mCurrentSlicedBucket.begin();
-    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(15, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
-    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
-    // Value for key {uid 2, BACKGROUND}
-    ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
-    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
-    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
-    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
-              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
-
-    // Base for dimension key {uid 1}
-    it++;
-    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(17, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
-    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
-    // Value for key {uid 1, kStateUnknown}
-    ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
-    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
-    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
-    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
-              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
+    assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
 
     // Value for key {uid 1, BACKGROUND}
     it++;
@@ -4775,8 +4873,7 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(4, it->second[0].value.long_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 20 * NS_PER_SEC);
 
     // Value for key {uid 1, FOREGROUND}
     it++;
@@ -4785,8 +4882,10 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(3, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(3, it->second.intervals[0].value.long_value);
+    assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+                         bucket2StartTimeNs + 20 * NS_PER_SEC);
 
     // Value for key {uid 2, kStateUnknown}
     it++;
@@ -4795,17 +4894,98 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
+    assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
+
+    // Bucket status after uid 1 process state change Background->Foreground.
+    uidProcessEvent =
+            CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 40 * NS_PER_SEC, 1 /* uid */,
+                                              android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+    StateManager::getInstance().onLogEvent(*uidProcessEvent);
+
+    ASSERT_EQ(5UL, valueProducer->mCurrentSlicedBucket.size());
+    ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
+    // Base for dimension key {uid 2}
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(15, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {uid 2, BACKGROUND}
+    ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+
+    // Base for dimension key {uid 1}
+    it++;
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(17, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {uid 1, kStateUnknown}
+    ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
+    assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+    // Value for key {uid 1, BACKGROUND}
+    it++;
+    ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+    assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+                         bucket2StartTimeNs + 40 * NS_PER_SEC);
+
+    // Value for key {uid 1, FOREGROUND}
+    it++;
+    ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(3, it->second.intervals[0].value.long_value);
+    assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
+                         bucket2StartTimeNs + 40 * NS_PER_SEC);
+
+    // Value for key {uid 2, kStateUnknown}
+    it++;
+    ASSERT_EQ(1, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 40 * NS_PER_SEC);
 
     // Start dump report and check output.
     ProtoOutputStream output;
     std::set<string> strSet;
-    valueProducer->onDumpReport(bucket2StartTimeNs + 50, true /* include recent buckets */, true,
-                                NO_TIME_CONSTRAINTS, &strSet, &output);
+    valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
+                                true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+                                &strSet, &output);
 
     StatsLogReport report = outputStreamToProto(&output);
     EXPECT_TRUE(report.has_value_metrics());
     ASSERT_EQ(5, report.value_metrics().data_size());
 
+    // {uid 1, BACKGROUND}
     auto data = report.value_metrics().data(0);
     ASSERT_EQ(1, data.bucket_info_size());
     EXPECT_EQ(4, report.value_metrics().data(0).bucket_info(0).values(0).value_long());
@@ -4813,14 +4993,18 @@
     EXPECT_TRUE(data.slice_by_state(0).has_value());
     EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
               data.slice_by_state(0).value());
+    EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
 
+    // {uid 2, kStateUnknown}
     data = report.value_metrics().data(1);
     ASSERT_EQ(1, report.value_metrics().data(1).bucket_info_size());
     EXPECT_EQ(2, report.value_metrics().data(1).bucket_info(0).values(0).value_long());
     EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
     EXPECT_TRUE(data.slice_by_state(0).has_value());
     EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value());
+    EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
 
+    // {uid 1, FOREGROUND}
     data = report.value_metrics().data(2);
     EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
     EXPECT_TRUE(data.slice_by_state(0).has_value());
@@ -4829,14 +5013,19 @@
     ASSERT_EQ(2, report.value_metrics().data(2).bucket_info_size());
     EXPECT_EQ(4, report.value_metrics().data(2).bucket_info(0).values(0).value_long());
     EXPECT_EQ(7, report.value_metrics().data(2).bucket_info(1).values(0).value_long());
+    EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+    EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
 
+    // {uid 1, kStateUnknown}
     data = report.value_metrics().data(3);
     ASSERT_EQ(1, report.value_metrics().data(3).bucket_info_size());
     EXPECT_EQ(3, report.value_metrics().data(3).bucket_info(0).values(0).value_long());
     EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
     EXPECT_TRUE(data.slice_by_state(0).has_value());
     EXPECT_EQ(-1 /*StateTracker::kStateUnknown*/, data.slice_by_state(0).value());
+    EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
 
+    // {uid 2, BACKGROUND}
     data = report.value_metrics().data(4);
     EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
     EXPECT_TRUE(data.slice_by_state(0).has_value());
@@ -4845,6 +5034,1630 @@
     ASSERT_EQ(2, report.value_metrics().data(4).bucket_info_size());
     EXPECT_EQ(6, report.value_metrics().data(4).bucket_info(0).values(0).value_long());
     EXPECT_EQ(5, report.value_metrics().data(4).bucket_info(1).values(0).value_long());
+    EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+    EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+}
+
+/*
+ * Test slicing condition_true_nanos by state for metric that slices by state when data is not
+ * present in pulled data during a state change.
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataInStateChange) {
+    // Set up ValueMetricProducer.
+    ValueMetric metric =
+            ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    /*
+    NOTE: "-" means that the data was not present in the pulled data.
+
+                             bucket # 1
+    10         20         30         40         50         60   (seconds)
+    |-------------------------------------------------------|--
+    x                                                           (kStateUnknown)
+    |-----------|
+         10
+
+                x                               x               (ON)
+                |---------------------|         |-----------|
+                           20                        10
+
+                                      -                         (OFF)
+    */
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+            // ValueMetricProducer initialized.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+                data->clear();
+                data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+                return true;
+            }))
+            // Battery saver mode state changed to ON.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+                data->clear();
+                data->push_back(
+                        CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 5));
+                return true;
+            }))
+            // Battery saver mode state changed to OFF but data for dimension key {} is not present
+            // in the pulled data.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
+                data->clear();
+                return true;
+            }))
+            // Battery saver mode state changed to ON.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
+                data->clear();
+                data->push_back(
+                        CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC, 7));
+                return true;
+            }))
+            // Dump report pull.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
+                data->clear();
+                data->push_back(CreateRepeatedValueLogEvent(
+                        tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 15));
+                return true;
+            }));
+
+    StateManager::getInstance().clear();
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithState(
+                    pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
+    EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+    // Set up StateManager and check that StateTrackers are initialized.
+    StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
+                                                 valueProducer);
+    EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+                         util::BATTERY_SAVER_MODE_STATE_CHANGED));
+
+    // Bucket status after metric initialized.
+    ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension key {}
+    auto it = valueProducer->mCurrentSlicedBucket.begin();
+    auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for dimension, state key {{}, kStateUnknown}
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
+
+    // Bucket status after battery saver mode ON event.
+    unique_ptr<LogEvent> batterySaverOnEvent =
+            CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+    StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+    // Base for dimension key {}
+
+    ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+    ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{}, ON}
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+
+    // Value for key {{}, -1}
+    it++;
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+                         bucketStartTimeNs + 10 * NS_PER_SEC);
+
+    // Bucket status after battery saver mode OFF event which is not present
+    // in the pulled data.
+    unique_ptr<LogEvent> batterySaverOffEvent =
+            CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 30 * NS_PER_SEC);
+    StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
+
+    // Base for dimension key {}
+    ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+    ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_FALSE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{}, ON}
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+                         bucketStartTimeNs + 30 * NS_PER_SEC);
+
+    // Value for key {{}, -1}
+    it++;
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+                         bucketStartTimeNs + 10 * NS_PER_SEC);
+
+    // Bucket status after battery saver mode ON event.
+    batterySaverOnEvent =
+            CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 40 * NS_PER_SEC);
+    StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+    // Base for dimension key {}
+    ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+    ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{}, ON}
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
+                         bucketStartTimeNs + 40 * NS_PER_SEC);
+
+    // Value for key {{}, -1}
+    it++;
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+                         bucketStartTimeNs + 10 * NS_PER_SEC);
+
+    // Start dump report and check output.
+    ProtoOutputStream output;
+    std::set<string> strSet;
+    valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
+                                true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+                                &strSet, &output);
+
+    StatsLogReport report = outputStreamToProto(&output);
+    EXPECT_TRUE(report.has_value_metrics());
+    ASSERT_EQ(2, report.value_metrics().data_size());
+
+    // {{}, kStateUnknown}
+    ValueMetricData data = report.value_metrics().data(0);
+    EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+    EXPECT_EQ(-1 /*StateTracker::kUnknown*/, data.slice_by_state(0).value());
+    ASSERT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+    // {{}, ON}
+    data = report.value_metrics().data(1);
+    EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
+    ASSERT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+}
+
+/*
+ * Test for metric that slices by state when data is not present in pulled data
+ * during an event and then a flush occurs for the current bucket. With the new
+ * condition timer behavior, a "new" MetricDimensionKey is inserted into
+ * `mCurrentSlicedBucket` before intervals are closed/added to that new
+ * MetricDimensionKey.
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithMissingDataThenFlushBucket) {
+    // Set up ValueMetricProducer.
+    ValueMetric metric =
+            ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    /*
+    NOTE: "-" means that the data was not present in the pulled data.
+
+                             bucket # 1
+    10         20         30         40         50         60   (seconds)
+    |-------------------------------------------------------|--
+    -                                                           (kStateUnknown)
+
+                -                                               (ON)
+    */
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+            // ValueMetricProducer initialized  but data for dimension key {} is not present
+            // in the pulled data..
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+                data->clear();
+                return true;
+            }))
+            // Battery saver mode state changed to ON but data for dimension key {} is not present
+            // in the pulled data.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+                data->clear();
+                return true;
+            }))
+            // Dump report pull.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
+                data->clear();
+                data->push_back(CreateRepeatedValueLogEvent(
+                        tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 15));
+                return true;
+            }));
+
+    StateManager::getInstance().clear();
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithState(
+                    pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
+    EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+    // Set up StateManager and check that StateTrackers are initialized.
+    StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
+                                                 valueProducer);
+    EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+                         util::BATTERY_SAVER_MODE_STATE_CHANGED));
+
+    // Bucket status after metric initialized.
+    ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+    ASSERT_EQ(0UL, valueProducer->mCurrentBaseInfo.size());
+
+    // Bucket status after battery saver mode ON event which is not present
+    // in the pulled data.
+    unique_ptr<LogEvent> batterySaverOnEvent =
+            CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+    StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+    ASSERT_EQ(0UL, valueProducer->mCurrentBaseInfo.size());
+    ASSERT_EQ(0UL, valueProducer->mCurrentSlicedBucket.size());
+
+    // Start dump report and check output.
+    ProtoOutputStream output;
+    std::set<string> strSet;
+    valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
+                                true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+                                &strSet, &output);
+
+    StatsLogReport report = outputStreamToProto(&output);
+    EXPECT_TRUE(report.has_value_metrics());
+    ASSERT_EQ(0, report.value_metrics().data_size());
+    ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+    ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+}
+
+TEST(ValueMetricProducerTest, TestSlicedStateWithNoPullOnBucketBoundary) {
+    // Set up ValueMetricProducer.
+    ValueMetric metric =
+            ValueMetricProducerTestHelper::createMetricWithState("BATTERY_SAVER_MODE_STATE");
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    /*
+                 bucket # 1                         bucket # 2
+    10    20    30    40    50    60    70    80   90   100   110   120  (seconds)
+    |------------------------------------|---------------------------|--
+    x                                                                    (kStateUnknown)
+    |-----|
+      10
+          x                                              x               (ON)
+          |-----|                                        |-----------|
+             10                                               20
+                x                                                        (OFF)
+                |------------------------|
+                          40
+    */
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+            // ValueMetricProducer initialized.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs);
+                data->clear();
+                data->push_back(CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs, 3));
+                return true;
+            }))
+            // Battery saver mode state changed to ON.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+                data->clear();
+                data->push_back(
+                        CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 5));
+                return true;
+            }))
+            // Battery saver mode state changed to OFF.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
+                data->clear();
+                data->push_back(
+                        CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC, 7));
+                return true;
+            }))
+            // Battery saver mode state changed to ON.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 30 * NS_PER_SEC);
+                data->clear();
+                data->push_back(CreateRepeatedValueLogEvent(
+                        tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 10));
+                return true;
+            }))
+            // Dump report pull.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
+                data->clear();
+                data->push_back(CreateRepeatedValueLogEvent(
+                        tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 15));
+                return true;
+            }));
+
+    StateManager::getInstance().clear();
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithState(
+                    pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {});
+    EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+    // Set up StateManager and check that StateTrackers are initialized.
+    StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
+                                                 valueProducer);
+    EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+                         util::BATTERY_SAVER_MODE_STATE_CHANGED));
+
+    // Bucket status after metric initialized.
+    ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
+    ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+    auto it = valueProducer->mCurrentSlicedBucket.begin();
+    auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for dimension, state key {{}, kStateUnknown}
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs);
+
+    // Bucket status after battery saver mode ON event.
+    unique_ptr<LogEvent> batterySaverOnEvent =
+            CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+    StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+    ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+    ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{}, ON}
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+
+    // Value for key {{}, -1}
+    it++;
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+                         bucketStartTimeNs + 10 * NS_PER_SEC);
+
+    // Bucket status after battery saver mode OFF event.
+    unique_ptr<LogEvent> batterySaverOffEvent =
+            CreateBatterySaverOffEvent(/*timestamp=*/bucketStartTimeNs + 20 * NS_PER_SEC);
+    StateManager::getInstance().onLogEvent(*batterySaverOffEvent);
+
+    ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+    ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{}, OFF}
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+    // Value for key {{}, ON}
+    it++;
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+                         bucketStartTimeNs + 20 * NS_PER_SEC);
+
+    // Value for key {{}, -1}
+    it++;
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+                         bucketStartTimeNs + 10 * NS_PER_SEC);
+
+    // Bucket status after battery saver mode ON event.
+    batterySaverOnEvent =
+            CreateBatterySaverOnEvent(/*timestamp=*/bucket2StartTimeNs + 30 * NS_PER_SEC);
+    StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+
+    ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+    ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{}, OFF}
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 30 * NS_PER_SEC,
+                         bucket2StartTimeNs + 30 * NS_PER_SEC);
+
+    // Value for key {{}, ON}
+    it++;
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 30 * NS_PER_SEC);
+
+    // Value for key {{}, -1}
+    it++;
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+
+    // Start dump report and check output.
+    ProtoOutputStream output;
+    std::set<string> strSet;
+    valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
+                                true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+                                &strSet, &output);
+
+    StatsLogReport report = outputStreamToProto(&output);
+    EXPECT_TRUE(report.has_value_metrics());
+    ASSERT_EQ(3, report.value_metrics().data_size());
+
+    // {{}, kStateUnknown}
+    ValueMetricData data = report.value_metrics().data(0);
+    EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+    EXPECT_EQ(-1 /*StateTracker::kUnknown*/, data.slice_by_state(0).value());
+    ASSERT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+    // {{}, ON}
+    data = report.value_metrics().data(1);
+    EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
+    ASSERT_EQ(2, data.bucket_info_size());
+    EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+    EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+
+    // {{}, OFF}
+    data = report.value_metrics().data(2);
+    EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+    EXPECT_EQ(BatterySaverModeStateChanged::OFF, data.slice_by_state(0).value());
+    ASSERT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(40 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+}
+
+/*
+ * Test slicing condition_true_nanos by state for metric that slices by state when data is not
+ * present in pulled data during a condition change.
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithDataMissingInConditionChange) {
+    // Set up ValueMetricProducer.
+    ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithConditionAndState(
+            "BATTERY_SAVER_MODE_STATE");
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    /*
+    NOTE: "-" means that the data was not present in the pulled data.
+
+                             bucket # 1
+    10         20         30         40         50         60   (seconds)
+    |-------------------------------------------------------|--
+
+    T                                 F         T               (Condition)
+               x                                                (ON)
+               |----------------------|         -
+                         20
+    */
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+            // Battery saver mode state changed to ON.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+                data->clear();
+                data->push_back(
+                        CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC, 3));
+                return true;
+            }))
+            // Condition changed to false.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
+                data->clear();
+                data->push_back(
+                        CreateRepeatedValueLogEvent(tagId, bucketStartTimeNs + 30 * NS_PER_SEC, 5));
+                return true;
+            }))
+            // Condition changed to true but data for dimension key {} is not present in the
+            // pulled data.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
+                data->clear();
+                return true;
+            }))
+            // Dump report pull.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 50 * NS_PER_SEC);
+                data->clear();
+                data->push_back(CreateRepeatedValueLogEvent(
+                        tagId, bucketStartTimeNs + 50 * NS_PER_SEC, 20));
+                return true;
+            }));
+
+    StateManager::getInstance().clear();
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithConditionAndState(
+                    pullerManager, metric, {util::BATTERY_SAVER_MODE_STATE_CHANGED}, {},
+                    ConditionState::kTrue);
+    EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+    // Set up StateManager and check that StateTrackers are initialized.
+    StateManager::getInstance().registerListener(util::BATTERY_SAVER_MODE_STATE_CHANGED,
+                                                 valueProducer);
+    EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(
+                         util::BATTERY_SAVER_MODE_STATE_CHANGED));
+
+    // Bucket status after battery saver mode ON event.
+    unique_ptr<LogEvent> batterySaverOnEvent =
+            CreateBatterySaverOnEvent(/*timestamp=*/bucketStartTimeNs + 10 * NS_PER_SEC);
+    StateManager::getInstance().onLogEvent(*batterySaverOnEvent);
+    // Base for dimension key {}
+    ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+    ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+    auto it = valueProducer->mCurrentSlicedBucket.begin();
+    auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{}, ON}
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+
+    // Value for key {{}, -1}
+    it++;
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Bucket status after condition change to false.
+    valueProducer->onConditionChanged(false, bucketStartTimeNs + 30 * NS_PER_SEC);
+    // Base for dimension key {}
+    ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+    ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{}, ON}
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+                         bucketStartTimeNs + 30 * NS_PER_SEC);
+
+    // Value for key {{}, -1}
+    it++;
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Bucket status after condition change to true.
+    valueProducer->onConditionChanged(true, bucketStartTimeNs + 40 * NS_PER_SEC);
+    // Base for dimension key {}
+    ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
+    ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_FALSE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{}, ON}
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+                         bucketStartTimeNs + 30 * NS_PER_SEC);
+
+    // Value for key {{}, -1}
+    it++;
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Start dump report and check output.
+    ProtoOutputStream output;
+    std::set<string> strSet;
+    valueProducer->onDumpReport(bucketStartTimeNs + 50 * NS_PER_SEC,
+                                true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+                                &strSet, &output);
+
+    StatsLogReport report = outputStreamToProto(&output);
+    EXPECT_TRUE(report.has_value_metrics());
+    ASSERT_EQ(1, report.value_metrics().data_size());
+
+    // {{}, ON}
+    ValueMetricData data = report.value_metrics().data(0);
+    EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
+    ASSERT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+}
+
+/*
+ * Test slicing condition_true_nanos by state for metric that slices by state with a primary field,
+ * condition, and has multiple dimensions.
+ */
+TEST(ValueMetricProducerTest, TestSlicedStateWithMultipleDimensions) {
+    // Set up ValueMetricProducer.
+    ValueMetric metric =
+            ValueMetricProducerTestHelper::createMetricWithConditionAndState("UID_PROCESS_STATE");
+    metric.mutable_dimensions_in_what()->set_field(tagId);
+    metric.mutable_dimensions_in_what()->add_child()->set_field(1);
+    metric.mutable_dimensions_in_what()->add_child()->set_field(3);
+
+    MetricStateLink* stateLink = metric.add_state_link();
+    stateLink->set_state_atom_id(UID_PROCESS_STATE_ATOM_ID);
+    auto fieldsInWhat = stateLink->mutable_fields_in_what();
+    *fieldsInWhat = CreateDimensions(tagId, {1 /* uid */});
+    auto fieldsInState = stateLink->mutable_fields_in_state();
+    *fieldsInState = CreateDimensions(UID_PROCESS_STATE_ATOM_ID, {1 /* uid */});
+
+    /*
+                    bucket # 1                            bucket # 2
+    10     20     30     40     50     60     70     80     90    100    110    120 (seconds)
+    |------------------------------------------|---------------------------------|--
+
+    T                           F   T                                               (Condition)
+                                                                                    (FOREGROUND)
+           x                                                                        {1, 14}
+           |------|
+              10
+
+           x                                                                        {1, 16}
+           |------|
+              10
+                                                                   x                {2, 8}
+                                                                   |-------------|
+                                                                         20
+
+                                                                                    (BACKGROUND)
+                  x                                                                 {1, 14}
+                  |-------------|   |----------|---------------------------------|
+                        20              15                     50
+
+                  x                                                                 {1, 16}
+                  |-------------|   |----------|---------------------------------|
+                        20              15                     50
+
+                     x                                                              {2, 8}
+                     |----------|   |----------|-------------------|
+                         15             15              30
+    */
+    sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
+            // Uid 1 process state change from kStateUnknown -> Foreground
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 10 * NS_PER_SEC);
+                data->clear();
+                data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
+                                                         1 /*uid*/, 3, 14 /*tag*/));
+                data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
+                                                         1 /*uid*/, 3, 16 /*tag*/));
+
+                // This event should be skipped.
+                data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 10 * NS_PER_SEC,
+                                                         2 /*uid*/, 5, 8 /*tag*/));
+                return true;
+            }))
+            // Uid 1 process state change from Foreground -> Background
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
+                data->clear();
+                data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+                                                         1 /*uid*/, 5, 14 /*tag*/));
+                data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+                                                         1 /*uid*/, 5, 16 /*tag*/));
+
+                // This event should be skipped.
+                data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 20 * NS_PER_SEC,
+                                                         2 /*uid*/, 7, 8 /*tag*/));
+
+                return true;
+            }))
+            // Uid 2 process state change from kStateUnknown -> Background
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 25 * NS_PER_SEC);
+                data->clear();
+                data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
+                                                         2 /*uid*/, 9, 8 /*tag*/));
+
+                // This event should be skipped.
+                data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
+                                                         1 /*uid*/, 9, 14 /* tag */));
+
+                // This event should be skipped.
+                data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 25 * NS_PER_SEC,
+                                                         1 /*uid*/, 9, 16 /* tag */));
+
+                return true;
+            }))
+            // Condition changed to false.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 40 * NS_PER_SEC);
+                data->clear();
+                data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+                                                         1 /*uid*/, 11, 14 /* tag */));
+                data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+                                                         1 /*uid*/, 11, 16 /* tag */));
+                data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 40 * NS_PER_SEC,
+                                                         2 /*uid*/, 11, 8 /*tag*/));
+
+                return true;
+            }))
+            // Condition changed to true.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 45 * NS_PER_SEC);
+                data->clear();
+                data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
+                                                         1 /*uid*/, 13, 14 /* tag */));
+                data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
+                                                         1 /*uid*/, 13, 16 /* tag */));
+                data->push_back(CreateThreeValueLogEvent(tagId, bucketStartTimeNs + 45 * NS_PER_SEC,
+                                                         2 /*uid*/, 13, 8 /*tag*/));
+                return true;
+            }))
+            // Uid 2 process state change from Background -> Foreground
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 30 * NS_PER_SEC);
+                data->clear();
+                data->push_back(CreateThreeValueLogEvent(
+                        tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /*uid*/, 18, 8 /*tag*/));
+
+                // This event should be skipped.
+                data->push_back(CreateThreeValueLogEvent(
+                        tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 14 /* tag */));
+                // This event should be skipped.
+                data->push_back(CreateThreeValueLogEvent(
+                        tagId, bucket2StartTimeNs + 30 * NS_PER_SEC, 1 /*uid*/, 18, 16 /* tag */));
+
+                return true;
+            }))
+            // Dump report pull.
+            .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
+                                vector<std::shared_ptr<LogEvent>>* data) {
+                EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 50 * NS_PER_SEC);
+                data->clear();
+                data->push_back(CreateThreeValueLogEvent(
+                        tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 21, 14 /* tag */));
+                data->push_back(CreateThreeValueLogEvent(
+                        tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 1 /*uid*/, 21, 16 /* tag */));
+                data->push_back(CreateThreeValueLogEvent(
+                        tagId, bucket2StartTimeNs + 50 * NS_PER_SEC, 2 /*uid*/, 21, 8 /*tag*/));
+                return true;
+            }));
+
+    StateManager::getInstance().clear();
+    sp<ValueMetricProducer> valueProducer =
+            ValueMetricProducerTestHelper::createValueProducerWithConditionAndState(
+                    pullerManager, metric, {UID_PROCESS_STATE_ATOM_ID}, {}, ConditionState::kTrue);
+    EXPECT_EQ(1, valueProducer->mSlicedStateAtoms.size());
+
+    // Set up StateManager and check that StateTrackers are initialized.
+    StateManager::getInstance().registerListener(UID_PROCESS_STATE_ATOM_ID, valueProducer);
+    EXPECT_EQ(1, StateManager::getInstance().getStateTrackersCount());
+    EXPECT_EQ(1, StateManager::getInstance().getListenersCount(UID_PROCESS_STATE_ATOM_ID));
+
+    // Condition is true.
+    // Bucket status after uid 1 process state change kStateUnknown -> Foreground.
+    auto uidProcessEvent =
+            CreateUidProcessStateChangedEvent(bucketStartTimeNs + 10 * NS_PER_SEC, 1 /* uid */,
+                                              android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+    StateManager::getInstance().onLogEvent(*uidProcessEvent);
+    ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
+    ASSERT_EQ(4UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension {uid 1, tag 16}.
+    auto it = valueProducer->mCurrentSlicedBucket.begin();
+    auto itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{uid 1, uid 16}, FOREGROUND}.
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+    // Value for key {{uid 1, tag 16}, kStateUnknown}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Base for dimension key {uid 1, tag 14}.
+    it++;
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{uid 1, tag 14}, FOREGROUND}.
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 10 * NS_PER_SEC);
+    // Value for key {{uid 1, tag 14}, kStateUnknown}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Bucket status after uid 1 process state change Foreground -> Background.
+    uidProcessEvent =
+            CreateUidProcessStateChangedEvent(bucketStartTimeNs + 20 * NS_PER_SEC, 1 /* uid */,
+                                              android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+    StateManager::getInstance().onLogEvent(*uidProcessEvent);
+    ASSERT_EQ(2UL, valueProducer->mCurrentBaseInfo.size());
+    ASSERT_EQ(6UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension {uid 1, tag 16}.
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{uid 1, uid 16}, BACKGROUND}.
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+    // Base for dimension key {uid 1, tag 14}.
+    it++;
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{uid 1, tag 14}, BACKGROUND}.
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+    // Value for key {{uid 1, uid 16}, FOREGROUND}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+                         bucketStartTimeNs + 20 * NS_PER_SEC);
+
+    // Value for key {{uid 1, tag 16}, kStateUnknown}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Value for key {{uid 1, tag 14}, FOREGROUND}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+                         bucketStartTimeNs + 20 * NS_PER_SEC);
+
+    // Value for key {{uid 1, tag 14}, kStateUnknown}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Bucket status after uid 2 process state change kStateUnknown -> Background.
+    uidProcessEvent =
+            CreateUidProcessStateChangedEvent(bucketStartTimeNs + 25 * NS_PER_SEC, 2 /* uid */,
+                                              android::app::PROCESS_STATE_IMPORTANT_BACKGROUND);
+    StateManager::getInstance().onLogEvent(*uidProcessEvent);
+    ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+    ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension {uid 2, tag 8}.
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{uid 2, uid 8}, BACKGROUND}.
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 25 * NS_PER_SEC);
+
+    // Value for key {{uid 2, uid 8}, kStateUnknown}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Base for dimension {uid 1, tag 16}.
+    it++;
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{uid 1, uid 16}, BACKGROUND}.
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+    // Base for dimension key {uid 1, tag 14}.
+    it++;
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{uid 1, tag 14}, BACKGROUND}.
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+    // Value for key {{uid 1, uid 16}, FOREGROUND}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+                         bucketStartTimeNs + 20 * NS_PER_SEC);
+
+    // Value for key {{uid 1, tag 16}, kStateUnknown}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Value for key {{uid 1, tag 14}, FOREGROUND}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+                         bucketStartTimeNs + 20 * NS_PER_SEC);
+
+    // Value for key {{uid 1, tag 14}, kStateUnknown}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Bucket 1 status after condition change to false.
+    // All condition timers should be turned off.
+    valueProducer->onConditionChanged(false, bucketStartTimeNs + 40 * NS_PER_SEC);
+    ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+    ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension {uid 2, tag 8}.
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{uid 2, uid 8}, BACKGROUND}.
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 15 * NS_PER_SEC,
+                         bucketStartTimeNs + 40 * NS_PER_SEC);
+
+    // Value for key {{uid 2, uid 8}, kStateUnknown}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Base for dimension {uid 1, tag 16}.
+    it++;
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{uid 1, uid 16}, BACKGROUND}.
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+                         bucketStartTimeNs + 40 * NS_PER_SEC);
+
+    // Base for dimension key {uid 1, tag 14}.
+    it++;
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{uid 1, tag 14}, BACKGROUND}.
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 20 * NS_PER_SEC,
+                         bucketStartTimeNs + 40 * NS_PER_SEC);
+
+    // Value for key {{uid 1, uid 16}, FOREGROUND}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+                         bucketStartTimeNs + 20 * NS_PER_SEC);
+
+    // Value for key {{uid 1, tag 16}, kStateUnknown}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Value for key {{uid 1, tag 14}, FOREGROUND}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+                         bucketStartTimeNs + 20 * NS_PER_SEC);
+
+    // Value for key {{uid 1, tag 14}, kStateUnknown}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Bucket 1 status after condition change to true.
+    valueProducer->onConditionChanged(true, bucketStartTimeNs + 45 * NS_PER_SEC);
+    ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+    ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+    // Base for dimension {uid 2, tag 8}.
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{uid 2, uid 8}, BACKGROUND}.
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 15 * NS_PER_SEC,
+                         bucketStartTimeNs + 45 * NS_PER_SEC);
+
+    // Value for key {{uid 2, uid 8}, kStateUnknown}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Base for dimension {uid 1, tag 16}.
+    it++;
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{uid 1, uid 16}, BACKGROUND}.
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
+                         bucketStartTimeNs + 45 * NS_PER_SEC);
+
+    // Base for dimension key {uid 1, tag 14}.
+    it++;
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{uid 1, tag 14}, BACKGROUND}.
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 20 * NS_PER_SEC,
+                         bucketStartTimeNs + 45 * NS_PER_SEC);
+
+    // Value for key {{uid 1, uid 16}, FOREGROUND}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+                         bucketStartTimeNs + 20 * NS_PER_SEC);
+
+    // Value for key {{uid 1, tag 16}, kStateUnknown}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Value for key {{uid 1, tag 14}, FOREGROUND}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+                         bucketStartTimeNs + 20 * NS_PER_SEC);
+
+    // Value for key {{uid 1, tag 14}, kStateUnknown}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Pull at end of first bucket.
+    vector<shared_ptr<LogEvent>> allData;
+    allData.push_back(
+            CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 14 /* tag */));
+    allData.push_back(
+            CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 1 /*uid*/, 13, 16 /* tag */));
+    allData.push_back(
+            CreateThreeValueLogEvent(tagId, bucket2StartTimeNs, 2 /*uid*/, 13, 8 /*tag*/));
+    valueProducer->onDataPulled(allData, /** succeeds */ true, bucket2StartTimeNs + 1);
+
+    // Buckets flushed after end of first bucket.
+    // All condition timers' behavior should rollover to bucket 2.
+    ASSERT_EQ(8UL, valueProducer->mCurrentSlicedBucket.size());
+    ASSERT_EQ(5UL, valueProducer->mPastBuckets.size());
+    ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+    // Base for dimension {uid 2, tag 8}.
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{uid 2, uid 8}, BACKGROUND}.
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+    ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+    EXPECT_EQ(30 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+    // Value for key {{uid 2, uid 8}, kStateUnknown}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Base for dimension {uid 1, tag 16}.
+    it++;
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{uid 1, uid 16}, BACKGROUND}.
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+    ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+    EXPECT_EQ(35 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+    // Base for dimension key {uid 1, tag 14}.
+    it++;
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{uid 1, tag 14}, BACKGROUND}.
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+    ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+    EXPECT_EQ(35 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+    // Value for key {{uid 1, uid 16}, FOREGROUND}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+    ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+    EXPECT_EQ(10 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+    // Value for key {{uid 1, tag 16}, kStateUnknown}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Value for key {{uid 1, tag 14}, FOREGROUND}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+    ASSERT_EQ(1, valueProducer->mPastBuckets[it->first].size());
+    EXPECT_EQ(10 * NS_PER_SEC, valueProducer->mPastBuckets[it->first][0].mConditionTrueNs);
+
+    // Value for key {{uid 1, tag 14}, kStateUnknown}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Bucket 2 status after uid 2 process state change Background->Foreground.
+    uidProcessEvent =
+            CreateUidProcessStateChangedEvent(bucket2StartTimeNs + 30 * NS_PER_SEC, 2 /* uid */,
+                                              android::app::PROCESS_STATE_IMPORTANT_FOREGROUND);
+    StateManager::getInstance().onLogEvent(*uidProcessEvent);
+
+    ASSERT_EQ(9UL, valueProducer->mCurrentSlicedBucket.size());
+    ASSERT_EQ(3UL, valueProducer->mCurrentBaseInfo.size());
+    // Base for dimension {uid 2, tag 8}.
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{uid 2, uid 8}, FOREGROUND}.
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs + 30 * NS_PER_SEC);
+
+    // Value for key {{uid 2, uid 8}, BACKGROUND}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 30 * NS_PER_SEC,
+                         bucket2StartTimeNs + 30 * NS_PER_SEC);
+
+    // Value for key {{uid 2, uid 8}, kStateUnknown}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(2, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(8, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Base for dimension {uid 1, tag 16}.
+    it++;
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{uid 1, uid 16}, BACKGROUND}.
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+
+    // Base for dimension key {uid 1, tag 14}.
+    it++;
+    itBase = valueProducer->mCurrentBaseInfo.find(it->first.getDimensionKeyInWhat());
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{uid 1, tag 14}, BACKGROUND}.
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+
+    // Value for key {{uid 1, uid 16}, FOREGROUND}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+    // Value for key {{uid 1, tag 16}, kStateUnknown}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(16, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Value for key {{uid 1, tag 14}, FOREGROUND}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(android::app::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
+
+    // Value for key {{uid 1, tag 14}, kStateUnknown}.
+    it++;
+    ASSERT_EQ(2, it->first.getDimensionKeyInWhat().getValues().size());
+    EXPECT_EQ(1, it->first.getDimensionKeyInWhat().getValues()[0].mValue.int_value);
+    EXPECT_EQ(14, it->first.getDimensionKeyInWhat().getValues()[1].mValue.int_value);
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(-1 /* StateTracker::kStateUnknown */,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
+
+    // Start dump report and check output.
+    ProtoOutputStream output;
+    std::set<string> strSet;
+    valueProducer->onDumpReport(bucket2StartTimeNs + 50 * NS_PER_SEC,
+                                true /* include recent buckets */, true, NO_TIME_CONSTRAINTS,
+                                &strSet, &output);
+
+    StatsLogReport report = outputStreamToProto(&output);
+    EXPECT_TRUE(report.has_value_metrics());
+    ASSERT_EQ(6, report.value_metrics().data_size());
+
+    // {{uid 1, tag 14}, FOREGROUND}.
+    auto data = report.value_metrics().data(0);
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              data.slice_by_state(0).value());
+    ASSERT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+    // {{uid 1, tag 16}, BACKGROUND}.
+    data = report.value_metrics().data(1);
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              data.slice_by_state(0).value());
+    ASSERT_EQ(2, data.bucket_info_size());
+    EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+    EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+
+    // {{uid 1, tag 14}, BACKGROUND}.
+    data = report.value_metrics().data(2);
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              data.slice_by_state(0).value());
+    ASSERT_EQ(2, data.bucket_info_size());
+    EXPECT_EQ(35 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+    EXPECT_EQ(50 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
+
+    // {{uid 1, tag 16}, FOREGROUND}.
+    data = report.value_metrics().data(3);
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              data.slice_by_state(0).value());
+    ASSERT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+    // {{uid 2, tag 8}, FOREGROUND}.
+    data = report.value_metrics().data(4);
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_FOREGROUND,
+              data.slice_by_state(0).value());
+    ASSERT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(20 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+
+    // {{uid 2, tag 8}, BACKGROUND}.
+    data = report.value_metrics().data(5);
+    EXPECT_EQ(UID_PROCESS_STATE_ATOM_ID, data.slice_by_state(0).atom_id());
+    EXPECT_EQ(android::app::ProcessStateEnum::PROCESS_STATE_IMPORTANT_BACKGROUND,
+              data.slice_by_state(0).value());
+    ASSERT_EQ(2, data.bucket_info_size());
+    EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+    EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
 }
 
 TEST(ValueMetricProducerTest, TestSlicedStateWithCondition) {
@@ -4852,10 +6665,10 @@
     ValueMetric metric = ValueMetricProducerTestHelper::createMetricWithConditionAndState(
             "BATTERY_SAVER_MODE_STATE");
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
-    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(tagId, kConfigKey, _, _))
             // Condition changed to true.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 20 * NS_PER_SEC);
                 data->clear();
                 data->push_back(
@@ -4864,7 +6677,7 @@
             }))
             // Battery saver mode state changed to OFF.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucketStartTimeNs + 30 * NS_PER_SEC);
                 data->clear();
                 data->push_back(
@@ -4873,7 +6686,7 @@
             }))
             // Condition changed to false.
             .WillOnce(Invoke([](int tagId, const ConfigKey&, const int64_t eventTimeNs,
-                                vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                vector<std::shared_ptr<LogEvent>>* data) {
                 EXPECT_EQ(eventTimeNs, bucket2StartTimeNs + 10 * NS_PER_SEC);
                 data->clear();
                 data->push_back(CreateRepeatedValueLogEvent(
@@ -4907,23 +6720,31 @@
     valueProducer->onConditionChanged(true, bucketStartTimeNs + 20 * NS_PER_SEC);
     // Base for dimension key {}
     ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
-    std::unordered_map<HashableDimensionKey, std::vector<ValueMetricProducer::BaseInfo>>::iterator
+    std::unordered_map<HashableDimensionKey, ValueMetricProducer::DimensionsInWhatInfo>::iterator
             itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(3, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(3, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(BatterySaverModeStateChanged::ON,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{}, ON}
+    ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+    std::unordered_map<MetricDimensionKey, ValueMetricProducer::CurrentValueBucket>::iterator it =
+            valueProducer->mCurrentSlicedBucket.begin();
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 20 * NS_PER_SEC);
     // Value for key {{}, -1}
-    ASSERT_EQ(1UL, valueProducer->mCurrentSlicedBucket.size());
-    std::unordered_map<MetricDimensionKey, std::vector<ValueMetricProducer::Interval>>::iterator
-            it = valueProducer->mCurrentSlicedBucket.begin();
+    it++;
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(-1 /*StateTracker::kUnknown*/,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_FALSE(it->second[0].hasValue);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
 
     // Bucket status after battery saver mode OFF event.
     unique_ptr<LogEvent> batterySaverOffEvent =
@@ -4932,21 +6753,33 @@
     // Base for dimension key {}
     ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
     itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(5, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(5, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(BatterySaverModeStateChanged::OFF,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
-    // Value for key {{}, ON}
-    ASSERT_EQ(2UL, valueProducer->mCurrentSlicedBucket.size());
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{}, OFF}
+    ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
     it = valueProducer->mCurrentSlicedBucket.begin();
     EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::OFF,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
+    // Value for key {{}, ON}
+    it++;
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(BatterySaverModeStateChanged::ON,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(2, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(2, it->second.intervals[0].value.long_value);
+    assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+                         bucketStartTimeNs + 30 * NS_PER_SEC);
+    // Value for key {{}, -1}
+    it++;
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
 
     // Pull at end of first bucket.
     vector<shared_ptr<LogEvent>> allData;
@@ -4959,23 +6792,32 @@
     // Base for dimension key {}
     ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
     itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
-    EXPECT_TRUE(itBase->second[0].hasBase);
-    EXPECT_EQ(11, itBase->second[0].base.long_value);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_TRUE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_EQ(11, itBase->second.baseInfos[0].base.long_value);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(BatterySaverModeStateChanged::OFF,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
+    // Value for key {{}, OFF}
+    it = valueProducer->mCurrentSlicedBucket.begin();
+    assertConditionTimer(it->second.conditionTimer, true, 0, bucket2StartTimeNs);
+    // Value for key {{}, ON}
+    it++;
+    assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
+    // Value for key {{}, -1}
+    it++;
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
 
     // Bucket 2 status after condition change to false.
     valueProducer->onConditionChanged(false, bucket2StartTimeNs + 10 * NS_PER_SEC);
     // Base for dimension key {}
     ASSERT_EQ(1UL, valueProducer->mCurrentBaseInfo.size());
     itBase = valueProducer->mCurrentBaseInfo.find(DEFAULT_DIMENSION_KEY);
-    EXPECT_FALSE(itBase->second[0].hasBase);
-    EXPECT_TRUE(itBase->second[0].hasCurrentState);
-    ASSERT_EQ(1, itBase->second[0].currentState.getValues().size());
+    EXPECT_FALSE(itBase->second.baseInfos[0].hasBase);
+    EXPECT_TRUE(itBase->second.hasCurrentState);
+    ASSERT_EQ(1, itBase->second.currentState.getValues().size());
     EXPECT_EQ(BatterySaverModeStateChanged::OFF,
-              itBase->second[0].currentState.getValues()[0].mValue.int_value);
+              itBase->second.currentState.getValues()[0].mValue.int_value);
     // Value for key {{}, OFF}
     ASSERT_EQ(3UL, valueProducer->mCurrentSlicedBucket.size());
     it = valueProducer->mCurrentSlicedBucket.begin();
@@ -4983,8 +6825,21 @@
     ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
     EXPECT_EQ(BatterySaverModeStateChanged::OFF,
               it->first.getStateValuesKey().getValues()[0].mValue.int_value);
-    EXPECT_TRUE(it->second[0].hasValue);
-    EXPECT_EQ(4, it->second[0].value.long_value);
+    EXPECT_TRUE(it->second.intervals[0].hasValue);
+    EXPECT_EQ(4, it->second.intervals[0].value.long_value);
+    assertConditionTimer(it->second.conditionTimer, false, 10 * NS_PER_SEC,
+                         bucket2StartTimeNs + 10 * NS_PER_SEC);
+    // Value for key {{}, ON}
+    it++;
+    EXPECT_EQ(0, it->first.getDimensionKeyInWhat().getValues().size());
+    ASSERT_EQ(1, it->first.getStateValuesKey().getValues().size());
+    EXPECT_EQ(BatterySaverModeStateChanged::ON,
+              it->first.getStateValuesKey().getValues()[0].mValue.int_value);
+    EXPECT_FALSE(it->second.intervals[0].hasValue);
+    assertConditionTimer(it->second.conditionTimer, false, 0, bucketStartTimeNs + 30 * NS_PER_SEC);
+    // Value for key {{}, -1}
+    it++;
+    assertConditionTimer(it->second.conditionTimer, false, 0, 0);
 
     // Start dump report and check output.
     ProtoOutputStream output;
@@ -5003,6 +6858,7 @@
     EXPECT_EQ(BatterySaverModeStateChanged::ON, data.slice_by_state(0).value());
     ASSERT_EQ(1, data.bucket_info_size());
     EXPECT_EQ(2, data.bucket_info(0).values(0).value_long());
+    EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
 
     data = report.value_metrics().data(1);
     EXPECT_EQ(util::BATTERY_SAVER_MODE_STATE_CHANGED, data.slice_by_state(0).atom_id());
@@ -5011,6 +6867,8 @@
     ASSERT_EQ(2, data.bucket_info_size());
     EXPECT_EQ(6, data.bucket_info(0).values(0).value_long());
     EXPECT_EQ(4, data.bucket_info(1).values(0).value_long());
+    EXPECT_EQ(30 * NS_PER_SEC, data.bucket_info(0).condition_true_nanos());
+    EXPECT_EQ(10 * NS_PER_SEC, data.bucket_info(1).condition_true_nanos());
 }
 
 /*
diff --git a/bin/tests/metrics/metrics_test_helper.h b/bin/tests/metrics/metrics_test_helper.h
index eeb38a4..39232c1 100644
--- a/bin/tests/metrics/metrics_test_helper.h
+++ b/bin/tests/metrics/metrics_test_helper.h
@@ -38,11 +38,10 @@
                       int64_t nextPulltimeNs, int64_t intervalNs));
     MOCK_METHOD3(UnRegisterReceiver,
                  void(int tagId, const ConfigKey& key, wp<PullDataReceiver> receiver));
-    MOCK_METHOD5(Pull, bool(const int pullCode, const ConfigKey& key, const int64_t eventTimeNs,
-                            vector<std::shared_ptr<LogEvent>>* data, bool useUids));
-    MOCK_METHOD5(Pull,
-                 bool(const int pullCode, const vector<int32_t>& uids, const int64_t eventTimeNs,
-                      vector<std::shared_ptr<LogEvent>>* data, bool useUids));
+    MOCK_METHOD4(Pull, bool(const int pullCode, const ConfigKey& key, const int64_t eventTimeNs,
+                            vector<std::shared_ptr<LogEvent>>* data));
+    MOCK_METHOD4(Pull, bool(const int pullCode, const vector<int32_t>& uids,
+                            const int64_t eventTimeNs, vector<std::shared_ptr<LogEvent>>* data));
     MOCK_METHOD2(RegisterPullUidProvider,
                  void(const ConfigKey& configKey, wp<PullUidProvider> provider));
     MOCK_METHOD2(UnregisterPullUidProvider,
diff --git a/bin/tests/metrics/parsing_utils/config_update_utils_test.cpp b/bin/tests/metrics/parsing_utils/config_update_utils_test.cpp
new file mode 100644
index 0000000..d06f84f
--- /dev/null
+++ b/bin/tests/metrics/parsing_utils/config_update_utils_test.cpp
@@ -0,0 +1,3632 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/metrics/parsing_utils/config_update_utils.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <private/android_filesystem_config.h>
+#include <stdio.h>
+
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
+#include "src/condition/CombinationConditionTracker.h"
+#include "src/condition/SimpleConditionTracker.h"
+#include "src/matchers/CombinationAtomMatchingTracker.h"
+#include "src/metrics/DurationMetricProducer.h"
+#include "src/metrics/GaugeMetricProducer.h"
+#include "src/metrics/ValueMetricProducer.h"
+#include "src/metrics/parsing_utils/metrics_manager_util.h"
+#include "tests/statsd_test_util.h"
+
+using namespace testing;
+using android::sp;
+using android::os::statsd::Predicate;
+using std::map;
+using std::nullopt;
+using std::optional;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+namespace {
+
+ConfigKey key(123, 456);
+const int64_t timeBaseNs = 1000 * NS_PER_SEC;
+
+sp<UidMap> uidMap = new UidMap();
+sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+sp<AlarmMonitor> anomalyAlarmMonitor;
+sp<AlarmMonitor> periodicAlarmMonitor = new AlarmMonitor(
+        /*minDiffToUpdateRegisteredAlarmTimeSec=*/0,
+        [](const shared_ptr<IStatsCompanionService>&, int64_t) {},
+        [](const shared_ptr<IStatsCompanionService>&) {});
+set<int> allTagIds;
+vector<sp<AtomMatchingTracker>> oldAtomMatchingTrackers;
+unordered_map<int64_t, int> oldAtomMatchingTrackerMap;
+vector<sp<ConditionTracker>> oldConditionTrackers;
+unordered_map<int64_t, int> oldConditionTrackerMap;
+vector<sp<MetricProducer>> oldMetricProducers;
+unordered_map<int64_t, int> oldMetricProducerMap;
+std::vector<sp<AnomalyTracker>> oldAnomalyTrackers;
+unordered_map<int64_t, int> oldAlertTrackerMap;
+std::vector<sp<AlarmTracker>> oldAlarmTrackers;
+unordered_map<int, std::vector<int>> tmpConditionToMetricMap;
+unordered_map<int, std::vector<int>> tmpTrackerToMetricMap;
+unordered_map<int, std::vector<int>> tmpTrackerToConditionMap;
+unordered_map<int, std::vector<int>> tmpActivationAtomTrackerToMetricMap;
+unordered_map<int, std::vector<int>> tmpDeactivationAtomTrackerToMetricMap;
+vector<int> metricsWithActivation;
+map<int64_t, uint64_t> oldStateHashes;
+std::set<int64_t> noReportMetricIds;
+
+class ConfigUpdateTest : public ::testing::Test {
+public:
+    ConfigUpdateTest() {
+    }
+
+    void SetUp() override {
+        allTagIds.clear();
+        oldAtomMatchingTrackers.clear();
+        oldAtomMatchingTrackerMap.clear();
+        oldConditionTrackers.clear();
+        oldConditionTrackerMap.clear();
+        oldMetricProducers.clear();
+        oldMetricProducerMap.clear();
+        oldAnomalyTrackers.clear();
+        oldAlarmTrackers.clear();
+        tmpConditionToMetricMap.clear();
+        tmpTrackerToMetricMap.clear();
+        tmpTrackerToConditionMap.clear();
+        tmpActivationAtomTrackerToMetricMap.clear();
+        tmpDeactivationAtomTrackerToMetricMap.clear();
+        oldAlertTrackerMap.clear();
+        metricsWithActivation.clear();
+        oldStateHashes.clear();
+        noReportMetricIds.clear();
+        StateManager::getInstance().clear();
+    }
+};
+
+bool initConfig(const StatsdConfig& config) {
+    return initStatsdConfig(
+            key, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+            timeBaseNs, timeBaseNs, allTagIds, oldAtomMatchingTrackers, oldAtomMatchingTrackerMap,
+            oldConditionTrackers, oldConditionTrackerMap, oldMetricProducers, oldMetricProducerMap,
+            oldAnomalyTrackers, oldAlarmTrackers, tmpConditionToMetricMap, tmpTrackerToMetricMap,
+            tmpTrackerToConditionMap, tmpActivationAtomTrackerToMetricMap,
+            tmpDeactivationAtomTrackerToMetricMap, oldAlertTrackerMap, metricsWithActivation,
+            oldStateHashes, noReportMetricIds);
+}
+
+EventMetric createEventMetric(string name, int64_t what, optional<int64_t> condition) {
+    EventMetric metric;
+    metric.set_id(StringToId(name));
+    metric.set_what(what);
+    if (condition) {
+        metric.set_condition(condition.value());
+    }
+    return metric;
+}
+
+CountMetric createCountMetric(string name, int64_t what, optional<int64_t> condition,
+                              vector<int64_t> states) {
+    CountMetric metric;
+    metric.set_id(StringToId(name));
+    metric.set_what(what);
+    metric.set_bucket(TEN_MINUTES);
+    if (condition) {
+        metric.set_condition(condition.value());
+    }
+    for (const int64_t state : states) {
+        metric.add_slice_by_state(state);
+    }
+    return metric;
+}
+
+GaugeMetric createGaugeMetric(string name, int64_t what, GaugeMetric::SamplingType samplingType,
+                              optional<int64_t> condition, optional<int64_t> triggerEvent) {
+    GaugeMetric metric;
+    metric.set_id(StringToId(name));
+    metric.set_what(what);
+    metric.set_bucket(TEN_MINUTES);
+    metric.set_sampling_type(samplingType);
+    if (condition) {
+        metric.set_condition(condition.value());
+    }
+    if (triggerEvent) {
+        metric.set_trigger_event(triggerEvent.value());
+    }
+    metric.mutable_gauge_fields_filter()->set_include_all(true);
+    return metric;
+}
+
+DurationMetric createDurationMetric(string name, int64_t what, optional<int64_t> condition,
+                                    vector<int64_t> states) {
+    DurationMetric metric;
+    metric.set_id(StringToId(name));
+    metric.set_what(what);
+    metric.set_bucket(TEN_MINUTES);
+    if (condition) {
+        metric.set_condition(condition.value());
+    }
+    for (const int64_t state : states) {
+        metric.add_slice_by_state(state);
+    }
+    return metric;
+}
+
+ValueMetric createValueMetric(string name, const AtomMatcher& what, optional<int64_t> condition,
+                              vector<int64_t> states) {
+    ValueMetric metric;
+    metric.set_id(StringToId(name));
+    metric.set_what(what.id());
+    metric.set_bucket(TEN_MINUTES);
+    metric.mutable_value_field()->set_field(what.simple_atom_matcher().atom_id());
+    metric.mutable_value_field()->add_child()->set_field(2);
+    if (condition) {
+        metric.set_condition(condition.value());
+    }
+    for (const int64_t state : states) {
+        metric.add_slice_by_state(state);
+    }
+    return metric;
+}
+
+Alert createAlert(string name, int64_t metricId, int buckets, int64_t triggerSum) {
+    Alert alert;
+    alert.set_id(StringToId(name));
+    alert.set_metric_id(metricId);
+    alert.set_num_buckets(buckets);
+    alert.set_trigger_if_sum_gt(triggerSum);
+    return alert;
+}
+
+Subscription createSubscription(string name, Subscription_RuleType type, int64_t ruleId) {
+    Subscription subscription;
+    subscription.set_id(StringToId(name));
+    subscription.set_rule_type(type);
+    subscription.set_rule_id(ruleId);
+    subscription.mutable_broadcast_subscriber_details();
+    return subscription;
+}
+
+Alarm createAlarm(string name, int64_t offsetMillis, int64_t periodMillis) {
+    Alarm alarm;
+    alarm.set_id(StringToId(name));
+    alarm.set_offset_millis(offsetMillis);
+    alarm.set_period_millis(periodMillis);
+    return alarm;
+}
+}  // anonymous namespace
+
+TEST_F(ConfigUpdateTest, TestSimpleMatcherPreserve) {
+    StatsdConfig config;
+    AtomMatcher matcher = CreateSimpleAtomMatcher("TEST", /*atom=*/10);
+    int64_t matcherId = matcher.id();
+    *config.add_atom_matcher() = matcher;
+
+    // Create an initial config.
+    EXPECT_TRUE(initConfig(config));
+
+    vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(1, false);
+    unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    newAtomMatchingTrackerMap[matcherId] = 0;
+    EXPECT_TRUE(determineMatcherUpdateStatus(config, 0, oldAtomMatchingTrackerMap,
+                                             oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                             matchersToUpdate, cycleTracker));
+    EXPECT_EQ(matchersToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestSimpleMatcherReplace) {
+    StatsdConfig config;
+    AtomMatcher matcher = CreateSimpleAtomMatcher("TEST", /*atom=*/10);
+    *config.add_atom_matcher() = matcher;
+
+    EXPECT_TRUE(initConfig(config));
+
+    StatsdConfig newConfig;
+    // Same id, different atom, so should be replaced.
+    AtomMatcher newMatcher = CreateSimpleAtomMatcher("TEST", /*atom=*/11);
+    int64_t matcherId = newMatcher.id();
+    EXPECT_EQ(matcherId, matcher.id());
+    *newConfig.add_atom_matcher() = newMatcher;
+
+    vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(1, false);
+    unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    newAtomMatchingTrackerMap[matcherId] = 0;
+    EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 0, oldAtomMatchingTrackerMap,
+                                             oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                             matchersToUpdate, cycleTracker));
+    EXPECT_EQ(matchersToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestSimpleMatcherNew) {
+    StatsdConfig config;
+    AtomMatcher matcher = CreateSimpleAtomMatcher("TEST", /*atom=*/10);
+    *config.add_atom_matcher() = matcher;
+
+    EXPECT_TRUE(initConfig(config));
+
+    StatsdConfig newConfig;
+    // Different id, so should be a new matcher.
+    AtomMatcher newMatcher = CreateSimpleAtomMatcher("DIFFERENT_NAME", /*atom=*/10);
+    int64_t matcherId = newMatcher.id();
+    EXPECT_NE(matcherId, matcher.id());
+    *newConfig.add_atom_matcher() = newMatcher;
+
+    vector<UpdateStatus> matchersToUpdate(1, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(1, false);
+    unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    newAtomMatchingTrackerMap[matcherId] = 0;
+    EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 0, oldAtomMatchingTrackerMap,
+                                             oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                             matchersToUpdate, cycleTracker));
+    EXPECT_EQ(matchersToUpdate[0], UPDATE_NEW);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationMatcherPreserve) {
+    StatsdConfig config;
+    AtomMatcher matcher1 = CreateSimpleAtomMatcher("TEST1", /*atom=*/10);
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateSimpleAtomMatcher("TEST2", /*atom=*/11);
+    *config.add_atom_matcher() = matcher2;
+    int64_t matcher2Id = matcher2.id();
+
+    AtomMatcher matcher3;
+    matcher3.set_id(StringToId("TEST3"));
+    AtomMatcher_Combination* combination = matcher3.mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_matcher(matcher1Id);
+    combination->add_matcher(matcher2Id);
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    EXPECT_TRUE(initConfig(config));
+
+    StatsdConfig newConfig;
+    unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    // Same matchers, different order, all should be preserved.
+    *newConfig.add_atom_matcher() = matcher2;
+    newAtomMatchingTrackerMap[matcher2Id] = 0;
+    *newConfig.add_atom_matcher() = matcher3;
+    newAtomMatchingTrackerMap[matcher3Id] = 1;
+    *newConfig.add_atom_matcher() = matcher1;
+    newAtomMatchingTrackerMap[matcher1Id] = 2;
+
+    vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(3, false);
+    // Only update the combination. It should recurse the two child matchers and preserve all 3.
+    EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap,
+                                             oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                             matchersToUpdate, cycleTracker));
+    EXPECT_EQ(matchersToUpdate[0], UPDATE_PRESERVE);
+    EXPECT_EQ(matchersToUpdate[1], UPDATE_PRESERVE);
+    EXPECT_EQ(matchersToUpdate[2], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationMatcherReplace) {
+    StatsdConfig config;
+    AtomMatcher matcher1 = CreateSimpleAtomMatcher("TEST1", /*atom=*/10);
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateSimpleAtomMatcher("TEST2", /*atom=*/11);
+    *config.add_atom_matcher() = matcher2;
+    int64_t matcher2Id = matcher2.id();
+
+    AtomMatcher matcher3;
+    matcher3.set_id(StringToId("TEST3"));
+    AtomMatcher_Combination* combination = matcher3.mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_matcher(matcher1Id);
+    combination->add_matcher(matcher2Id);
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Change the logical operation of the combination matcher, causing a replacement.
+    matcher3.mutable_combination()->set_operation(LogicalOperation::AND);
+
+    StatsdConfig newConfig;
+    unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    *newConfig.add_atom_matcher() = matcher2;
+    newAtomMatchingTrackerMap[matcher2Id] = 0;
+    *newConfig.add_atom_matcher() = matcher3;
+    newAtomMatchingTrackerMap[matcher3Id] = 1;
+    *newConfig.add_atom_matcher() = matcher1;
+    newAtomMatchingTrackerMap[matcher1Id] = 2;
+
+    vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(3, false);
+    // Only update the combination. The simple matchers should not be evaluated.
+    EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap,
+                                             oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                             matchersToUpdate, cycleTracker));
+    EXPECT_EQ(matchersToUpdate[0], UPDATE_UNKNOWN);
+    EXPECT_EQ(matchersToUpdate[1], UPDATE_REPLACE);
+    EXPECT_EQ(matchersToUpdate[2], UPDATE_UNKNOWN);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationMatcherDepsChange) {
+    StatsdConfig config;
+    AtomMatcher matcher1 = CreateSimpleAtomMatcher("TEST1", /*atom=*/10);
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateSimpleAtomMatcher("TEST2", /*atom=*/11);
+    *config.add_atom_matcher() = matcher2;
+    int64_t matcher2Id = matcher2.id();
+
+    AtomMatcher matcher3;
+    matcher3.set_id(StringToId("TEST3"));
+    AtomMatcher_Combination* combination = matcher3.mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_matcher(matcher1Id);
+    combination->add_matcher(matcher2Id);
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Change a dependency of matcher 3.
+    matcher2.mutable_simple_atom_matcher()->set_atom_id(12);
+
+    StatsdConfig newConfig;
+    unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    *newConfig.add_atom_matcher() = matcher2;
+    newAtomMatchingTrackerMap[matcher2Id] = 0;
+    *newConfig.add_atom_matcher() = matcher3;
+    newAtomMatchingTrackerMap[matcher3Id] = 1;
+    *newConfig.add_atom_matcher() = matcher1;
+    newAtomMatchingTrackerMap[matcher1Id] = 2;
+
+    vector<UpdateStatus> matchersToUpdate(3, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(3, false);
+    // Only update the combination.
+    EXPECT_TRUE(determineMatcherUpdateStatus(newConfig, 1, oldAtomMatchingTrackerMap,
+                                             oldAtomMatchingTrackers, newAtomMatchingTrackerMap,
+                                             matchersToUpdate, cycleTracker));
+    // Matcher 2 and matcher3 must be reevaluated. Matcher 1 might, but does not need to be.
+    EXPECT_EQ(matchersToUpdate[0], UPDATE_REPLACE);
+    EXPECT_EQ(matchersToUpdate[1], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateMatchers) {
+    StatsdConfig config;
+    // Will be preserved.
+    AtomMatcher simple1 = CreateSimpleAtomMatcher("SIMPLE1", /*atom=*/10);
+    int64_t simple1Id = simple1.id();
+    *config.add_atom_matcher() = simple1;
+
+    // Will be replaced.
+    AtomMatcher simple2 = CreateSimpleAtomMatcher("SIMPLE2", /*atom=*/11);
+    *config.add_atom_matcher() = simple2;
+    int64_t simple2Id = simple2.id();
+
+    // Will be removed.
+    AtomMatcher simple3 = CreateSimpleAtomMatcher("SIMPLE3", /*atom=*/12);
+    *config.add_atom_matcher() = simple3;
+    int64_t simple3Id = simple3.id();
+
+    // Will be preserved.
+    AtomMatcher combination1;
+    combination1.set_id(StringToId("combination1"));
+    AtomMatcher_Combination* combination = combination1.mutable_combination();
+    combination->set_operation(LogicalOperation::NOT);
+    combination->add_matcher(simple1Id);
+    int64_t combination1Id = combination1.id();
+    *config.add_atom_matcher() = combination1;
+
+    // Will be replaced since it depends on simple2.
+    AtomMatcher combination2;
+    combination2.set_id(StringToId("combination2"));
+    combination = combination2.mutable_combination();
+    combination->set_operation(LogicalOperation::AND);
+    combination->add_matcher(simple1Id);
+    combination->add_matcher(simple2Id);
+    int64_t combination2Id = combination2.id();
+    *config.add_atom_matcher() = combination2;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Change simple2, causing simple2 and combination2 to be replaced.
+    simple2.mutable_simple_atom_matcher()->set_atom_id(111);
+
+    // 2 new matchers: simple4 and combination3:
+    AtomMatcher simple4 = CreateSimpleAtomMatcher("SIMPLE4", /*atom=*/13);
+    int64_t simple4Id = simple4.id();
+
+    AtomMatcher combination3;
+    combination3.set_id(StringToId("combination3"));
+    combination = combination3.mutable_combination();
+    combination->set_operation(LogicalOperation::AND);
+    combination->add_matcher(simple4Id);
+    combination->add_matcher(simple2Id);
+    int64_t combination3Id = combination3.id();
+
+    StatsdConfig newConfig;
+    *newConfig.add_atom_matcher() = combination3;
+    *newConfig.add_atom_matcher() = simple2;
+    *newConfig.add_atom_matcher() = combination2;
+    *newConfig.add_atom_matcher() = simple1;
+    *newConfig.add_atom_matcher() = simple4;
+    *newConfig.add_atom_matcher() = combination1;
+
+    set<int> newTagIds;
+    unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers;
+    set<int64_t> replacedMatchers;
+    EXPECT_TRUE(updateAtomMatchingTrackers(
+            newConfig, uidMap, oldAtomMatchingTrackerMap, oldAtomMatchingTrackers, newTagIds,
+            newAtomMatchingTrackerMap, newAtomMatchingTrackers, replacedMatchers));
+
+    ASSERT_EQ(newTagIds.size(), 3);
+    EXPECT_EQ(newTagIds.count(10), 1);
+    EXPECT_EQ(newTagIds.count(111), 1);
+    EXPECT_EQ(newTagIds.count(13), 1);
+
+    ASSERT_EQ(newAtomMatchingTrackerMap.size(), 6);
+    EXPECT_EQ(newAtomMatchingTrackerMap.at(combination3Id), 0);
+    EXPECT_EQ(newAtomMatchingTrackerMap.at(simple2Id), 1);
+    EXPECT_EQ(newAtomMatchingTrackerMap.at(combination2Id), 2);
+    EXPECT_EQ(newAtomMatchingTrackerMap.at(simple1Id), 3);
+    EXPECT_EQ(newAtomMatchingTrackerMap.at(simple4Id), 4);
+    EXPECT_EQ(newAtomMatchingTrackerMap.at(combination1Id), 5);
+
+    ASSERT_EQ(newAtomMatchingTrackers.size(), 6);
+    // Make sure all atom matchers are initialized:
+    for (const sp<AtomMatchingTracker>& tracker : newAtomMatchingTrackers) {
+        EXPECT_TRUE(tracker->mInitialized);
+    }
+    // Make sure preserved atom matchers are the same.
+    EXPECT_EQ(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(simple1Id)],
+              newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(simple1Id)]);
+    EXPECT_EQ(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(combination1Id)],
+              newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(combination1Id)]);
+    // Make sure replaced matchers are different.
+    EXPECT_NE(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(simple2Id)],
+              newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(simple2Id)]);
+    EXPECT_NE(oldAtomMatchingTrackers[oldAtomMatchingTrackerMap.at(combination2Id)],
+              newAtomMatchingTrackers[newAtomMatchingTrackerMap.at(combination2Id)]);
+
+    // Validation, make sure the matchers have the proper ids/indices. Could do more checks here.
+    EXPECT_EQ(newAtomMatchingTrackers[0]->getId(), combination3Id);
+    EXPECT_EQ(newAtomMatchingTrackers[0]->mIndex, 0);
+    EXPECT_EQ(newAtomMatchingTrackers[1]->getId(), simple2Id);
+    EXPECT_EQ(newAtomMatchingTrackers[1]->mIndex, 1);
+    EXPECT_EQ(newAtomMatchingTrackers[2]->getId(), combination2Id);
+    EXPECT_EQ(newAtomMatchingTrackers[2]->mIndex, 2);
+    EXPECT_EQ(newAtomMatchingTrackers[3]->getId(), simple1Id);
+    EXPECT_EQ(newAtomMatchingTrackers[3]->mIndex, 3);
+    EXPECT_EQ(newAtomMatchingTrackers[4]->getId(), simple4Id);
+    EXPECT_EQ(newAtomMatchingTrackers[4]->mIndex, 4);
+    EXPECT_EQ(newAtomMatchingTrackers[5]->getId(), combination1Id);
+    EXPECT_EQ(newAtomMatchingTrackers[5]->mIndex, 5);
+
+    // Verify child indices of Combination Matchers are correct.
+    CombinationAtomMatchingTracker* combinationTracker1 =
+            static_cast<CombinationAtomMatchingTracker*>(newAtomMatchingTrackers[5].get());
+    vector<int>* childMatchers = &combinationTracker1->mChildren;
+    EXPECT_EQ(childMatchers->size(), 1);
+    EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 3), childMatchers->end());
+
+    CombinationAtomMatchingTracker* combinationTracker2 =
+            static_cast<CombinationAtomMatchingTracker*>(newAtomMatchingTrackers[2].get());
+    childMatchers = &combinationTracker2->mChildren;
+    EXPECT_EQ(childMatchers->size(), 2);
+    EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 1), childMatchers->end());
+    EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 3), childMatchers->end());
+
+    CombinationAtomMatchingTracker* combinationTracker3 =
+            static_cast<CombinationAtomMatchingTracker*>(newAtomMatchingTrackers[0].get());
+    childMatchers = &combinationTracker3->mChildren;
+    EXPECT_EQ(childMatchers->size(), 2);
+    EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 1), childMatchers->end());
+    EXPECT_NE(std::find(childMatchers->begin(), childMatchers->end(), 4), childMatchers->end());
+
+    // Expect replacedMatchers to have simple2 and combination2
+    ASSERT_EQ(replacedMatchers.size(), 2);
+    EXPECT_NE(replacedMatchers.find(simple2Id), replacedMatchers.end());
+    EXPECT_NE(replacedMatchers.find(combination2Id), replacedMatchers.end());
+}
+
+TEST_F(ConfigUpdateTest, TestSimpleConditionPreserve) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    // Create an initial config.
+    EXPECT_TRUE(initConfig(config));
+
+    set<int64_t> replacedMatchers;
+    vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(1, false);
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    newConditionTrackerMap[predicate.id()] = 0;
+    EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap,
+                                               oldConditionTrackers, newConditionTrackerMap,
+                                               replacedMatchers, conditionsToUpdate, cycleTracker));
+    EXPECT_EQ(conditionsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestSimpleConditionReplace) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Modify the predicate.
+    config.mutable_predicate(0)->mutable_simple_predicate()->set_count_nesting(true);
+
+    set<int64_t> replacedMatchers;
+    vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(1, false);
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    newConditionTrackerMap[predicate.id()] = 0;
+    EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap,
+                                               oldConditionTrackers, newConditionTrackerMap,
+                                               replacedMatchers, conditionsToUpdate, cycleTracker));
+    EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestSimpleConditionDepsChange) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    int64_t startMatcherId = startMatcher.id();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Start matcher was replaced.
+    set<int64_t> replacedMatchers;
+    replacedMatchers.insert(startMatcherId);
+
+    vector<UpdateStatus> conditionsToUpdate(1, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(1, false);
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    newConditionTrackerMap[predicate.id()] = 0;
+    EXPECT_TRUE(determineConditionUpdateStatus(config, 0, oldConditionTrackerMap,
+                                               oldConditionTrackers, newConditionTrackerMap,
+                                               replacedMatchers, conditionsToUpdate, cycleTracker));
+    EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationConditionPreserve) {
+    StatsdConfig config;
+    AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = screenOnMatcher;
+    AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = screenOffMatcher;
+
+    Predicate simple1 = CreateScreenIsOnPredicate();
+    *config.add_predicate() = simple1;
+    Predicate simple2 = CreateScreenIsOffPredicate();
+    *config.add_predicate() = simple2;
+
+    Predicate combination1;
+    combination1.set_id(StringToId("COMBINATION1"));
+    Predicate_Combination* combinationInternal = combination1.mutable_combination();
+    combinationInternal->set_operation(LogicalOperation::NAND);
+    combinationInternal->add_predicate(simple1.id());
+    combinationInternal->add_predicate(simple2.id());
+    *config.add_predicate() = combination1;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Same predicates, different order
+    StatsdConfig newConfig;
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    *newConfig.add_predicate() = combination1;
+    newConditionTrackerMap[combination1.id()] = 0;
+    *newConfig.add_predicate() = simple2;
+    newConditionTrackerMap[simple2.id()] = 1;
+    *newConfig.add_predicate() = simple1;
+    newConditionTrackerMap[simple1.id()] = 2;
+
+    set<int64_t> replacedMatchers;
+    vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(3, false);
+    // Only update the combination. It should recurse the two child predicates and preserve all 3.
+    EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap,
+                                               oldConditionTrackers, newConditionTrackerMap,
+                                               replacedMatchers, conditionsToUpdate, cycleTracker));
+    EXPECT_EQ(conditionsToUpdate[0], UPDATE_PRESERVE);
+    EXPECT_EQ(conditionsToUpdate[1], UPDATE_PRESERVE);
+    EXPECT_EQ(conditionsToUpdate[2], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationConditionReplace) {
+    StatsdConfig config;
+    AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = screenOnMatcher;
+    AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = screenOffMatcher;
+
+    Predicate simple1 = CreateScreenIsOnPredicate();
+    *config.add_predicate() = simple1;
+    Predicate simple2 = CreateScreenIsOffPredicate();
+    *config.add_predicate() = simple2;
+
+    Predicate combination1;
+    combination1.set_id(StringToId("COMBINATION1"));
+    Predicate_Combination* combinationInternal = combination1.mutable_combination();
+    combinationInternal->set_operation(LogicalOperation::NAND);
+    combinationInternal->add_predicate(simple1.id());
+    combinationInternal->add_predicate(simple2.id());
+    *config.add_predicate() = combination1;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Changing the logical operation changes the predicate definition, so it should be replaced.
+    combination1.mutable_combination()->set_operation(LogicalOperation::OR);
+
+    StatsdConfig newConfig;
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    *newConfig.add_predicate() = combination1;
+    newConditionTrackerMap[combination1.id()] = 0;
+    *newConfig.add_predicate() = simple2;
+    newConditionTrackerMap[simple2.id()] = 1;
+    *newConfig.add_predicate() = simple1;
+    newConditionTrackerMap[simple1.id()] = 2;
+
+    set<int64_t> replacedMatchers;
+    vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(3, false);
+    // Only update the combination. The simple conditions should not be evaluated.
+    EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap,
+                                               oldConditionTrackers, newConditionTrackerMap,
+                                               replacedMatchers, conditionsToUpdate, cycleTracker));
+    EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE);
+    EXPECT_EQ(conditionsToUpdate[1], UPDATE_UNKNOWN);
+    EXPECT_EQ(conditionsToUpdate[2], UPDATE_UNKNOWN);
+}
+
+TEST_F(ConfigUpdateTest, TestCombinationConditionDepsChange) {
+    StatsdConfig config;
+    AtomMatcher screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = screenOnMatcher;
+    AtomMatcher screenOffMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = screenOffMatcher;
+
+    Predicate simple1 = CreateScreenIsOnPredicate();
+    *config.add_predicate() = simple1;
+    Predicate simple2 = CreateScreenIsOffPredicate();
+    *config.add_predicate() = simple2;
+
+    Predicate combination1;
+    combination1.set_id(StringToId("COMBINATION1"));
+    Predicate_Combination* combinationInternal = combination1.mutable_combination();
+    combinationInternal->set_operation(LogicalOperation::NAND);
+    combinationInternal->add_predicate(simple1.id());
+    combinationInternal->add_predicate(simple2.id());
+    *config.add_predicate() = combination1;
+
+    EXPECT_TRUE(initConfig(config));
+
+    simple2.mutable_simple_predicate()->set_count_nesting(false);
+
+    StatsdConfig newConfig;
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    *newConfig.add_predicate() = combination1;
+    newConditionTrackerMap[combination1.id()] = 0;
+    *newConfig.add_predicate() = simple2;
+    newConditionTrackerMap[simple2.id()] = 1;
+    *newConfig.add_predicate() = simple1;
+    newConditionTrackerMap[simple1.id()] = 2;
+
+    set<int64_t> replacedMatchers;
+    vector<UpdateStatus> conditionsToUpdate(3, UPDATE_UNKNOWN);
+    vector<bool> cycleTracker(3, false);
+    // Only update the combination. Simple2 and combination1 must be evaluated.
+    EXPECT_TRUE(determineConditionUpdateStatus(newConfig, 0, oldConditionTrackerMap,
+                                               oldConditionTrackers, newConditionTrackerMap,
+                                               replacedMatchers, conditionsToUpdate, cycleTracker));
+    EXPECT_EQ(conditionsToUpdate[0], UPDATE_REPLACE);
+    EXPECT_EQ(conditionsToUpdate[1], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateConditions) {
+    StatsdConfig config;
+    // Add atom matchers. These are mostly needed for initStatsdConfig
+    AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+    int64_t matcher2Id = matcher2.id();
+    *config.add_atom_matcher() = matcher2;
+
+    AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher();
+    int64_t matcher4Id = matcher4.id();
+    *config.add_atom_matcher() = matcher4;
+
+    AtomMatcher matcher5 = CreateBatterySaverModeStartAtomMatcher();
+    int64_t matcher5Id = matcher5.id();
+    *config.add_atom_matcher() = matcher5;
+
+    AtomMatcher matcher6 = CreateBatterySaverModeStopAtomMatcher();
+    int64_t matcher6Id = matcher6.id();
+    *config.add_atom_matcher() = matcher6;
+
+    // Add the predicates.
+    // Will be preserved.
+    Predicate simple1 = CreateScreenIsOnPredicate();
+    int64_t simple1Id = simple1.id();
+    *config.add_predicate() = simple1;
+
+    // Will be preserved.
+    Predicate simple2 = CreateScheduledJobPredicate();
+    int64_t simple2Id = simple2.id();
+    *config.add_predicate() = simple2;
+
+    // Will be replaced.
+    Predicate simple3 = CreateBatterySaverModePredicate();
+    int64_t simple3Id = simple3.id();
+    *config.add_predicate() = simple3;
+
+    // Will be preserved
+    Predicate combination1;
+    combination1.set_id(StringToId("COMBINATION1"));
+    combination1.mutable_combination()->set_operation(LogicalOperation::AND);
+    combination1.mutable_combination()->add_predicate(simple1Id);
+    combination1.mutable_combination()->add_predicate(simple2Id);
+    int64_t combination1Id = combination1.id();
+    *config.add_predicate() = combination1;
+
+    // Will be replaced since simple3 will be replaced.
+    Predicate combination2;
+    combination2.set_id(StringToId("COMBINATION2"));
+    combination2.mutable_combination()->set_operation(LogicalOperation::OR);
+    combination2.mutable_combination()->add_predicate(simple1Id);
+    combination2.mutable_combination()->add_predicate(simple3Id);
+    int64_t combination2Id = combination2.id();
+    *config.add_predicate() = combination2;
+
+    // Will be removed.
+    Predicate combination3;
+    combination3.set_id(StringToId("COMBINATION3"));
+    combination3.mutable_combination()->set_operation(LogicalOperation::NOT);
+    combination3.mutable_combination()->add_predicate(simple2Id);
+    int64_t combination3Id = combination3.id();
+    *config.add_predicate() = combination3;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Mark marcher 5 as replaced. Causes simple3, and therefore combination2 to be replaced.
+    set<int64_t> replacedMatchers;
+    replacedMatchers.insert(matcher6Id);
+
+    // Change the condition of simple1 to false.
+    ASSERT_EQ(oldConditionTrackers[0]->getConditionId(), simple1Id);
+    LogEvent event(/*uid=*/0, /*pid=*/0);  // Empty event is fine since there are no dimensions.
+    // Mark the stop matcher as matched, condition should be false.
+    vector<MatchingState> eventMatcherValues(6, MatchingState::kNotMatched);
+    eventMatcherValues[1] = MatchingState::kMatched;
+    vector<ConditionState> tmpConditionCache(6, ConditionState::kNotEvaluated);
+    vector<bool> conditionChangeCache(6, false);
+    oldConditionTrackers[0]->evaluateCondition(event, eventMatcherValues, oldConditionTrackers,
+                                               tmpConditionCache, conditionChangeCache);
+    EXPECT_EQ(tmpConditionCache[0], ConditionState::kFalse);
+    EXPECT_EQ(conditionChangeCache[0], true);
+
+    // New combination predicate. Should have an initial condition of true since it is NOT(simple1).
+    Predicate combination4;
+    combination4.set_id(StringToId("COMBINATION4"));
+    combination4.mutable_combination()->set_operation(LogicalOperation::NOT);
+    combination4.mutable_combination()->add_predicate(simple1Id);
+    int64_t combination4Id = combination4.id();
+    *config.add_predicate() = combination4;
+
+    // Map the matchers in reverse order to force the indices to change.
+    std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    const int matcher6Index = 0;
+    newAtomMatchingTrackerMap[matcher6Id] = 0;
+    const int matcher5Index = 1;
+    newAtomMatchingTrackerMap[matcher5Id] = 1;
+    const int matcher4Index = 2;
+    newAtomMatchingTrackerMap[matcher4Id] = 2;
+    const int matcher3Index = 3;
+    newAtomMatchingTrackerMap[matcher3Id] = 3;
+    const int matcher2Index = 4;
+    newAtomMatchingTrackerMap[matcher2Id] = 4;
+    const int matcher1Index = 5;
+    newAtomMatchingTrackerMap[matcher1Id] = 5;
+
+    StatsdConfig newConfig;
+    *newConfig.add_predicate() = simple3;
+    const int simple3Index = 0;
+    *newConfig.add_predicate() = combination2;
+    const int combination2Index = 1;
+    *newConfig.add_predicate() = combination4;
+    const int combination4Index = 2;
+    *newConfig.add_predicate() = simple2;
+    const int simple2Index = 3;
+    *newConfig.add_predicate() = combination1;
+    const int combination1Index = 4;
+    *newConfig.add_predicate() = simple1;
+    const int simple1Index = 5;
+
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    vector<sp<ConditionTracker>> newConditionTrackers;
+    unordered_map<int, vector<int>> trackerToConditionMap;
+    std::vector<ConditionState> conditionCache;
+    std::set<int64_t> replacedConditions;
+    EXPECT_TRUE(updateConditions(key, newConfig, newAtomMatchingTrackerMap, replacedMatchers,
+                                 oldConditionTrackerMap, oldConditionTrackers,
+                                 newConditionTrackerMap, newConditionTrackers,
+                                 trackerToConditionMap, conditionCache, replacedConditions));
+
+    unordered_map<int64_t, int> expectedConditionTrackerMap = {
+            {simple1Id, simple1Index},           {simple2Id, simple2Index},
+            {simple3Id, simple3Index},           {combination1Id, combination1Index},
+            {combination2Id, combination2Index}, {combination4Id, combination4Index},
+    };
+    EXPECT_THAT(newConditionTrackerMap, ContainerEq(expectedConditionTrackerMap));
+
+    ASSERT_EQ(newConditionTrackers.size(), 6);
+    // Make sure all conditions are initialized:
+    for (const sp<ConditionTracker>& tracker : newConditionTrackers) {
+        EXPECT_TRUE(tracker->mInitialized);
+    }
+
+    // Make sure preserved conditions are the same.
+    EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(simple1Id)],
+              newConditionTrackers[newConditionTrackerMap.at(simple1Id)]);
+    EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(simple2Id)],
+              newConditionTrackers[newConditionTrackerMap.at(simple2Id)]);
+    EXPECT_EQ(oldConditionTrackers[oldConditionTrackerMap.at(combination1Id)],
+              newConditionTrackers[newConditionTrackerMap.at(combination1Id)]);
+
+    // Make sure replaced conditions are different and included in replacedConditions.
+    EXPECT_NE(oldConditionTrackers[oldConditionTrackerMap.at(simple3Id)],
+              newConditionTrackers[newConditionTrackerMap.at(simple3Id)]);
+    EXPECT_NE(oldConditionTrackers[oldConditionTrackerMap.at(combination2Id)],
+              newConditionTrackers[newConditionTrackerMap.at(combination2Id)]);
+    EXPECT_THAT(replacedConditions, ContainerEq(set({simple3Id, combination2Id})));
+
+    // Verify the trackerToConditionMap
+    ASSERT_EQ(trackerToConditionMap.size(), 6);
+    const vector<int>& matcher1Conditions = trackerToConditionMap[matcher1Index];
+    EXPECT_THAT(matcher1Conditions, UnorderedElementsAre(simple1Index, combination1Index,
+                                                         combination2Index, combination4Index));
+    const vector<int>& matcher2Conditions = trackerToConditionMap[matcher2Index];
+    EXPECT_THAT(matcher2Conditions, UnorderedElementsAre(simple1Index, combination1Index,
+                                                         combination2Index, combination4Index));
+    const vector<int>& matcher3Conditions = trackerToConditionMap[matcher3Index];
+    EXPECT_THAT(matcher3Conditions, UnorderedElementsAre(simple2Index, combination1Index));
+    const vector<int>& matcher4Conditions = trackerToConditionMap[matcher4Index];
+    EXPECT_THAT(matcher4Conditions, UnorderedElementsAre(simple2Index, combination1Index));
+    const vector<int>& matcher5Conditions = trackerToConditionMap[matcher5Index];
+    EXPECT_THAT(matcher5Conditions, UnorderedElementsAre(simple3Index, combination2Index));
+    const vector<int>& matcher6Conditions = trackerToConditionMap[matcher6Index];
+    EXPECT_THAT(matcher6Conditions, UnorderedElementsAre(simple3Index, combination2Index));
+
+    // Verify the conditionCache. Specifically, simple1 is false and combination4 is true.
+    ASSERT_EQ(conditionCache.size(), 6);
+    EXPECT_EQ(conditionCache[simple1Index], ConditionState::kFalse);
+    EXPECT_EQ(conditionCache[simple2Index], ConditionState::kUnknown);
+    EXPECT_EQ(conditionCache[simple3Index], ConditionState::kUnknown);
+    EXPECT_EQ(conditionCache[combination1Index], ConditionState::kUnknown);
+    EXPECT_EQ(conditionCache[combination2Index], ConditionState::kUnknown);
+    EXPECT_EQ(conditionCache[combination4Index], ConditionState::kTrue);
+
+    // Verify tracker indices/ids are correct.
+    EXPECT_EQ(newConditionTrackers[simple1Index]->getConditionId(), simple1Id);
+    EXPECT_EQ(newConditionTrackers[simple1Index]->mIndex, simple1Index);
+    EXPECT_TRUE(newConditionTrackers[simple1Index]->IsSimpleCondition());
+    EXPECT_EQ(newConditionTrackers[simple2Index]->getConditionId(), simple2Id);
+    EXPECT_EQ(newConditionTrackers[simple2Index]->mIndex, simple2Index);
+    EXPECT_TRUE(newConditionTrackers[simple2Index]->IsSimpleCondition());
+    EXPECT_EQ(newConditionTrackers[simple3Index]->getConditionId(), simple3Id);
+    EXPECT_EQ(newConditionTrackers[simple3Index]->mIndex, simple3Index);
+    EXPECT_TRUE(newConditionTrackers[simple3Index]->IsSimpleCondition());
+    EXPECT_EQ(newConditionTrackers[combination1Index]->getConditionId(), combination1Id);
+    EXPECT_EQ(newConditionTrackers[combination1Index]->mIndex, combination1Index);
+    EXPECT_FALSE(newConditionTrackers[combination1Index]->IsSimpleCondition());
+    EXPECT_EQ(newConditionTrackers[combination2Index]->getConditionId(), combination2Id);
+    EXPECT_EQ(newConditionTrackers[combination2Index]->mIndex, combination2Index);
+    EXPECT_FALSE(newConditionTrackers[combination2Index]->IsSimpleCondition());
+    EXPECT_EQ(newConditionTrackers[combination4Index]->getConditionId(), combination4Id);
+    EXPECT_EQ(newConditionTrackers[combination4Index]->mIndex, combination4Index);
+    EXPECT_FALSE(newConditionTrackers[combination4Index]->IsSimpleCondition());
+
+    // Verify preserved trackers have indices updated.
+    SimpleConditionTracker* simpleTracker1 =
+            static_cast<SimpleConditionTracker*>(newConditionTrackers[simple1Index].get());
+    EXPECT_EQ(simpleTracker1->mStartLogMatcherIndex, matcher1Index);
+    EXPECT_EQ(simpleTracker1->mStopLogMatcherIndex, matcher2Index);
+    EXPECT_EQ(simpleTracker1->mStopAllLogMatcherIndex, -1);
+
+    SimpleConditionTracker* simpleTracker2 =
+            static_cast<SimpleConditionTracker*>(newConditionTrackers[simple2Index].get());
+    EXPECT_EQ(simpleTracker2->mStartLogMatcherIndex, matcher3Index);
+    EXPECT_EQ(simpleTracker2->mStopLogMatcherIndex, matcher4Index);
+    EXPECT_EQ(simpleTracker2->mStopAllLogMatcherIndex, -1);
+
+    CombinationConditionTracker* combinationTracker1 = static_cast<CombinationConditionTracker*>(
+            newConditionTrackers[combination1Index].get());
+    EXPECT_THAT(combinationTracker1->mChildren, UnorderedElementsAre(simple1Index, simple2Index));
+    EXPECT_THAT(combinationTracker1->mUnSlicedChildren,
+                UnorderedElementsAre(simple1Index, simple2Index));
+    EXPECT_THAT(combinationTracker1->mSlicedChildren, IsEmpty());
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateStates) {
+    StatsdConfig config;
+    // Add states.
+    // Will be replaced because we add a state map.
+    State state1 = CreateScreenState();
+    int64_t state1Id = state1.id();
+    *config.add_state() = state1;
+
+    // Will be preserved.
+    State state2 = CreateUidProcessState();
+    int64_t state2Id = state2.id();
+    *config.add_state() = state2;
+
+    // Will be replaced since the atom changes from overlay to screen.
+    State state3 = CreateOverlayState();
+    int64_t state3Id = state3.id();
+    *config.add_state() = state3;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Change definitions of state1 and state3.
+    int64_t screenOnId = 0x4321, screenOffId = 0x1234;
+    *state1.mutable_map() = CreateScreenStateSimpleOnOffMap(screenOnId, screenOffId);
+    state3.set_atom_id(util::SCREEN_STATE_CHANGED);
+
+    StatsdConfig newConfig;
+    *newConfig.add_state() = state3;
+    *newConfig.add_state() = state1;
+    *newConfig.add_state() = state2;
+
+    unordered_map<int64_t, int> stateAtomIdMap;
+    unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
+    map<int64_t, uint64_t> newStateProtoHashes;
+    set<int64_t> replacedStates;
+    EXPECT_TRUE(updateStates(newConfig, oldStateHashes, stateAtomIdMap, allStateGroupMaps,
+                             newStateProtoHashes, replacedStates));
+    EXPECT_THAT(replacedStates, ContainerEq(set({state1Id, state3Id})));
+
+    unordered_map<int64_t, int> expectedStateAtomIdMap = {
+            {state1Id, util::SCREEN_STATE_CHANGED},
+            {state2Id, util::UID_PROCESS_STATE_CHANGED},
+            {state3Id, util::SCREEN_STATE_CHANGED}};
+    EXPECT_THAT(stateAtomIdMap, ContainerEq(expectedStateAtomIdMap));
+
+    unordered_map<int64_t, unordered_map<int, int64_t>> expectedStateGroupMaps = {
+            {state1Id,
+             {{android::view::DisplayStateEnum::DISPLAY_STATE_OFF, screenOffId},
+              {android::view::DisplayStateEnum::DISPLAY_STATE_ON, screenOnId}}}};
+    EXPECT_THAT(allStateGroupMaps, ContainerEq(expectedStateGroupMaps));
+}
+
+TEST_F(ConfigUpdateTest, TestEventMetricPreserve) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    EventMetric* metric = config.add_event_metric();
+    metric->set_id(12345);
+    metric->set_what(whatMatcher.id());
+    metric->set_condition(predicate.id());
+
+    // Create an initial config.
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+                                                 metricToActivationMap,
+                                                 /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+                                                 /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestEventMetricActivationAdded) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    EventMetric* metric = config.add_event_metric();
+    metric->set_id(12345);
+    metric->set_what(whatMatcher.id());
+    metric->set_condition(predicate.id());
+
+    // Create an initial config.
+    EXPECT_TRUE(initConfig(config));
+
+    // Add a metric activation, which should change the proto, causing replacement.
+    MetricActivation* activation = config.add_metric_activation();
+    activation->set_metric_id(12345);
+    EventActivation* eventActivation = activation->add_event_activation();
+    eventActivation->set_atom_matcher_id(startMatcher.id());
+    eventActivation->set_ttl_seconds(5);
+
+    unordered_map<int64_t, int> metricToActivationMap = {{12345, 0}};
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+                                                 metricToActivationMap,
+                                                 /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+                                                 /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestEventMetricWhatChanged) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    EventMetric* metric = config.add_event_metric();
+    metric->set_id(12345);
+    metric->set_what(whatMatcher.id());
+    metric->set_condition(predicate.id());
+
+    // Create an initial config.
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestEventMetricConditionChanged) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    EventMetric* metric = config.add_event_metric();
+    metric->set_id(12345);
+    metric->set_what(whatMatcher.id());
+    metric->set_condition(predicate.id());
+
+    // Create an initial config.
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestMetricConditionLinkDepsChanged) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    Predicate linkPredicate = CreateScreenIsOffPredicate();
+    *config.add_predicate() = linkPredicate;
+
+    EventMetric* metric = config.add_event_metric();
+    metric->set_id(12345);
+    metric->set_what(whatMatcher.id());
+    metric->set_condition(predicate.id());
+    // Doesn't make sense as a real metric definition, but suffices as a separate predicate
+    // From the one in the condition.
+    MetricConditionLink* link = metric->add_links();
+    link->set_condition(linkPredicate.id());
+
+    // Create an initial config.
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {}, /*replacedConditions=*/{linkPredicate.id()},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestEventMetricActivationDepsChange) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    EventMetric* metric = config.add_event_metric();
+    metric->set_id(12345);
+    metric->set_what(whatMatcher.id());
+    metric->set_condition(predicate.id());
+
+    MetricActivation* activation = config.add_metric_activation();
+    activation->set_metric_id(12345);
+    EventActivation* eventActivation = activation->add_event_activation();
+    eventActivation->set_atom_matcher_id(startMatcher.id());
+    eventActivation->set_ttl_seconds(5);
+
+    // Create an initial config.
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap = {{12345, 0}};
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {startMatcher.id()}, /*replacedConditions=*/{},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestCountMetricPreserve) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+    State sliceState = CreateScreenState();
+    *config.add_state() = sliceState;
+
+    CountMetric* metric = config.add_count_metric();
+    metric->set_id(12345);
+    metric->set_what(whatMatcher.id());
+    metric->set_condition(predicate.id());
+    metric->add_slice_by_state(sliceState.id());
+    metric->set_bucket(ONE_HOUR);
+
+    // Create an initial config.
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+                                                 metricToActivationMap,
+                                                 /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+                                                 /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestCountMetricDefinitionChange) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    CountMetric* metric = config.add_count_metric();
+    metric->set_id(12345);
+    metric->set_what(whatMatcher.id());
+    metric->set_condition(predicate.id());
+    metric->set_bucket(ONE_HOUR);
+
+    // Create an initial config.
+    EXPECT_TRUE(initConfig(config));
+
+    // Change bucket size, which should change the proto, causing replacement.
+    metric->set_bucket(TEN_MINUTES);
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+                                                 metricToActivationMap,
+                                                 /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+                                                 /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestCountMetricWhatChanged) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    CountMetric* metric = config.add_count_metric();
+    metric->set_id(12345);
+    metric->set_what(whatMatcher.id());
+    metric->set_condition(predicate.id());
+    metric->set_bucket(ONE_HOUR);
+
+    // Create an initial config.
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestCountMetricConditionChanged) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    CountMetric* metric = config.add_count_metric();
+    metric->set_id(12345);
+    metric->set_what(whatMatcher.id());
+    metric->set_condition(predicate.id());
+    metric->set_bucket(ONE_HOUR);
+
+    // Create an initial config.
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestCountMetricStateChanged) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    State sliceState = CreateScreenState();
+    *config.add_state() = sliceState;
+
+    CountMetric* metric = config.add_count_metric();
+    metric->set_id(12345);
+    metric->set_what(whatMatcher.id());
+    metric->add_slice_by_state(sliceState.id());
+    metric->set_bucket(ONE_HOUR);
+
+    // Create an initial config.
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+            /*replacedStates=*/{sliceState.id()}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestGaugeMetricPreserve) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    *config.add_gauge_metric() = createGaugeMetric(
+            "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, predicate.id(), nullopt);
+
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+                                                 metricToActivationMap,
+                                                 /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+                                                 /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestGaugeMetricDefinitionChange) {
+    StatsdConfig config;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    *config.add_gauge_metric() = createGaugeMetric(
+            "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt);
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Change split bucket on app upgrade, which should change the proto, causing replacement.
+    config.mutable_gauge_metric(0)->set_split_bucket_for_app_upgrade(false);
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+                                                 metricToActivationMap,
+                                                 /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+                                                 /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestGaugeMetricWhatChanged) {
+    StatsdConfig config;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    *config.add_gauge_metric() = createGaugeMetric(
+            "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt);
+
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestGaugeMetricConditionChanged) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    *config.add_gauge_metric() = createGaugeMetric(
+            "GAUGE1", whatMatcher.id(), GaugeMetric::RANDOM_ONE_SAMPLE, predicate.id(), nullopt);
+
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestGaugeMetricTriggerEventChanged) {
+    StatsdConfig config;
+    AtomMatcher triggerEvent = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = triggerEvent;
+    AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    *config.add_gauge_metric() = createGaugeMetric(
+            "GAUGE1", whatMatcher.id(), GaugeMetric::FIRST_N_SAMPLES, nullopt, triggerEvent.id());
+
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {triggerEvent.id()}, /*replacedConditions=*/{},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestDurationMetricPreserve) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+
+    Predicate what = CreateScreenIsOnPredicate();
+    *config.add_predicate() = what;
+    Predicate condition = CreateScreenIsOffPredicate();
+    *config.add_predicate() = condition;
+
+    State sliceState = CreateScreenState();
+    *config.add_state() = sliceState;
+
+    *config.add_duration_metric() =
+            createDurationMetric("DURATION1", what.id(), condition.id(), {sliceState.id()});
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+                                                 metricToActivationMap,
+                                                 /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+                                                 /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestDurationMetricDefinitionChange) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+
+    Predicate what = CreateScreenIsOnPredicate();
+    *config.add_predicate() = what;
+
+    *config.add_duration_metric() = createDurationMetric("DURATION1", what.id(), nullopt, {});
+    EXPECT_TRUE(initConfig(config));
+
+    config.mutable_duration_metric(0)->set_aggregation_type(DurationMetric::MAX_SPARSE);
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+                                                 metricToActivationMap, /*replacedMatchers*/ {},
+                                                 /*replacedConditions=*/{}, /*replacedStates=*/{},
+                                                 metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestDurationMetricWhatChanged) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+
+    Predicate what = CreateScreenIsOnPredicate();
+    *config.add_predicate() = what;
+
+    *config.add_duration_metric() = createDurationMetric("DURATION1", what.id(), nullopt, {});
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {}, /*replacedConditions=*/{what.id()},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestDurationMetricConditionChanged) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+
+    Predicate what = CreateScreenIsOnPredicate();
+    *config.add_predicate() = what;
+    Predicate condition = CreateScreenIsOffPredicate();
+    *config.add_predicate() = condition;
+
+    *config.add_duration_metric() = createDurationMetric("DURATION", what.id(), condition.id(), {});
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {}, /*replacedConditions=*/{condition.id()},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestDurationMetricStateChange) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+
+    Predicate what = CreateScreenIsOnPredicate();
+    *config.add_predicate() = what;
+
+    State sliceState = CreateScreenState();
+    *config.add_state() = sliceState;
+
+    *config.add_duration_metric() =
+            createDurationMetric("DURATION1", what.id(), nullopt, {sliceState.id()});
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+            /*replacedStates=*/{sliceState.id()}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricPreserve) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+    State sliceState = CreateScreenState();
+    *config.add_state() = sliceState;
+
+    *config.add_value_metric() =
+            createValueMetric("VALUE1", whatMatcher, predicate.id(), {sliceState.id()});
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+                                                 metricToActivationMap,
+                                                 /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+                                                 /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricDefinitionChange) {
+    StatsdConfig config;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, nullopt, {});
+    EXPECT_TRUE(initConfig(config));
+
+    // Change skip zero diff output, which should change the proto, causing replacement.
+    config.mutable_value_metric(0)->set_skip_zero_diff_output(true);
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(config, oldMetricProducerMap, oldMetricProducers,
+                                                 metricToActivationMap,
+                                                 /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+                                                 /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricWhatChanged) {
+    StatsdConfig config;
+    AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, nullopt, {});
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {whatMatcher.id()}, /*replacedConditions=*/{},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricConditionChanged) {
+    StatsdConfig config;
+    AtomMatcher startMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = startMatcher;
+    AtomMatcher stopMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = stopMatcher;
+    AtomMatcher whatMatcher = CreateTemperatureAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    Predicate predicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = predicate;
+
+    *config.add_value_metric() = createValueMetric("VALUE1", whatMatcher, predicate.id(), {});
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {}, /*replacedConditions=*/{predicate.id()},
+            /*replacedStates=*/{}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestValueMetricStateChanged) {
+    StatsdConfig config;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    State sliceState = CreateScreenState();
+    *config.add_state() = sliceState;
+
+    *config.add_value_metric() =
+            createValueMetric("VALUE1", whatMatcher, nullopt, {sliceState.id()});
+    EXPECT_TRUE(initConfig(config));
+
+    unordered_map<int64_t, int> metricToActivationMap;
+    vector<UpdateStatus> metricsToUpdate(1, UPDATE_UNKNOWN);
+    EXPECT_TRUE(determineAllMetricUpdateStatuses(
+            config, oldMetricProducerMap, oldMetricProducers, metricToActivationMap,
+            /*replacedMatchers*/ {}, /*replacedConditions=*/{},
+            /*replacedStates=*/{sliceState.id()}, metricsToUpdate));
+    EXPECT_EQ(metricsToUpdate[0], UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateEventMetrics) {
+    StatsdConfig config;
+
+    // Add atom matchers/predicates. These are mostly needed for initStatsdConfig
+    AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+    int64_t matcher2Id = matcher2.id();
+    *config.add_atom_matcher() = matcher2;
+
+    AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher();
+    int64_t matcher4Id = matcher4.id();
+    *config.add_atom_matcher() = matcher4;
+
+    AtomMatcher matcher5 = CreateBatterySaverModeStartAtomMatcher();
+    int64_t matcher5Id = matcher5.id();
+    *config.add_atom_matcher() = matcher5;
+
+    Predicate predicate1 = CreateScreenIsOnPredicate();
+    int64_t predicate1Id = predicate1.id();
+    *config.add_predicate() = predicate1;
+
+    Predicate predicate2 = CreateScheduledJobPredicate();
+    int64_t predicate2Id = predicate2.id();
+    *config.add_predicate() = predicate2;
+
+    // Add a few event metrics.
+    // Will be preserved.
+    EventMetric event1 = createEventMetric("EVENT1", matcher1Id, predicate2Id);
+    int64_t event1Id = event1.id();
+    *config.add_event_metric() = event1;
+
+    // Will be replaced.
+    EventMetric event2 = createEventMetric("EVENT2", matcher2Id, nullopt);
+    int64_t event2Id = event2.id();
+    *config.add_event_metric() = event2;
+
+    // Will be replaced.
+    EventMetric event3 = createEventMetric("EVENT3", matcher3Id, nullopt);
+    int64_t event3Id = event3.id();
+    *config.add_event_metric() = event3;
+
+    MetricActivation event3Activation;
+    event3Activation.set_metric_id(event3Id);
+    EventActivation* eventActivation = event3Activation.add_event_activation();
+    eventActivation->set_atom_matcher_id(matcher5Id);
+    eventActivation->set_ttl_seconds(5);
+    *config.add_metric_activation() = event3Activation;
+
+    // Will be replaced.
+    EventMetric event4 = createEventMetric("EVENT4", matcher4Id, predicate1Id);
+    int64_t event4Id = event4.id();
+    *config.add_event_metric() = event4;
+
+    // Will be deleted.
+    EventMetric event5 = createEventMetric("EVENT5", matcher5Id, nullopt);
+    int64_t event5Id = event5.id();
+    *config.add_event_metric() = event5;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Used later to ensure the condition wizard is replaced. Get it before doing the update.
+    sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard;
+    EXPECT_EQ(oldConditionWizard->getStrongCount(), oldMetricProducers.size() + 1);
+
+    // Add a condition to event2, causing it to be replaced.
+    event2.set_condition(predicate1Id);
+
+    // Mark matcher 5 as replaced. Causes event3 to be replaced.
+    set<int64_t> replacedMatchers;
+    replacedMatchers.insert(matcher5Id);
+
+    // Mark predicate 1 as replaced. Causes event4 to be replaced.
+    set<int64_t> replacedConditions;
+    replacedConditions.insert(predicate1Id);
+
+    // Fake that predicate 2 is true.
+    ASSERT_EQ(oldMetricProducers[0]->getMetricId(), event1Id);
+    oldMetricProducers[0]->onConditionChanged(true, /*timestamp=*/0);
+    EXPECT_EQ(oldMetricProducers[0]->mCondition, ConditionState::kTrue);
+
+    // New event metric. Should have an initial condition of true since it depends on predicate2.
+    EventMetric event6 = createEventMetric("EVENT6", matcher3Id, predicate2Id);
+    int64_t event6Id = event6.id();
+    MetricActivation event6Activation;
+    event6Activation.set_metric_id(event6Id);
+    eventActivation = event6Activation.add_event_activation();
+    eventActivation->set_atom_matcher_id(matcher5Id);
+    eventActivation->set_ttl_seconds(20);
+
+    // Map the matchers and predicates in reverse order to force the indices to change.
+    std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    const int matcher5Index = 0;
+    newAtomMatchingTrackerMap[matcher5Id] = 0;
+    const int matcher4Index = 1;
+    newAtomMatchingTrackerMap[matcher4Id] = 1;
+    const int matcher3Index = 2;
+    newAtomMatchingTrackerMap[matcher3Id] = 2;
+    const int matcher2Index = 3;
+    newAtomMatchingTrackerMap[matcher2Id] = 3;
+    const int matcher1Index = 4;
+    newAtomMatchingTrackerMap[matcher1Id] = 4;
+    // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5);
+    std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+                      newAtomMatchingTrackers.begin());
+
+    std::unordered_map<int64_t, int> newConditionTrackerMap;
+    const int predicate2Index = 0;
+    newConditionTrackerMap[predicate2Id] = 0;
+    const int predicate1Index = 1;
+    newConditionTrackerMap[predicate1Id] = 1;
+    // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<ConditionTracker>> newConditionTrackers(2);
+    std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+                      newConditionTrackers.begin());
+    // Fake that predicate2 is true.
+    vector<ConditionState> conditionCache = {ConditionState::kTrue, ConditionState::kUnknown};
+
+    StatsdConfig newConfig;
+    *newConfig.add_event_metric() = event6;
+    const int event6Index = 0;
+    *newConfig.add_event_metric() = event3;
+    const int event3Index = 1;
+    *newConfig.add_event_metric() = event1;
+    const int event1Index = 2;
+    *newConfig.add_event_metric() = event4;
+    const int event4Index = 3;
+    *newConfig.add_event_metric() = event2;
+    const int event2Index = 4;
+    *newConfig.add_metric_activation() = event3Activation;
+    *newConfig.add_metric_activation() = event6Activation;
+
+    // Output data structures to validate.
+    unordered_map<int64_t, int> newMetricProducerMap;
+    vector<sp<MetricProducer>> newMetricProducers;
+    unordered_map<int, vector<int>> conditionToMetricMap;
+    unordered_map<int, vector<int>> trackerToMetricMap;
+    set<int64_t> noReportMetricIds;
+    unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+    vector<int> metricsWithActivation;
+    set<int64_t> replacedMetrics;
+    EXPECT_TRUE(updateMetrics(
+            key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+            oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
+            newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
+            newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{},
+            /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
+            newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+            metricsWithActivation, replacedMetrics));
+
+    unordered_map<int64_t, int> expectedMetricProducerMap = {
+            {event1Id, event1Index}, {event2Id, event2Index}, {event3Id, event3Index},
+            {event4Id, event4Index}, {event6Id, event6Index},
+    };
+    EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+    EXPECT_EQ(replacedMetrics, set<int64_t>({event2Id, event3Id, event4Id}));
+
+    // Make sure preserved metrics are the same.
+    ASSERT_EQ(newMetricProducers.size(), 5);
+    EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(event1Id)],
+              newMetricProducers[newMetricProducerMap.at(event1Id)]);
+
+    // Make sure replaced metrics are different.
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event2Id)],
+              newMetricProducers[newMetricProducerMap.at(event2Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event3Id)],
+              newMetricProducers[newMetricProducerMap.at(event3Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(event4Id)],
+              newMetricProducers[newMetricProducerMap.at(event4Id)]);
+
+    // Verify the conditionToMetricMap.
+    ASSERT_EQ(conditionToMetricMap.size(), 2);
+    const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
+    EXPECT_THAT(condition1Metrics, UnorderedElementsAre(event2Index, event4Index));
+    const vector<int>& condition2Metrics = conditionToMetricMap[predicate2Index];
+    EXPECT_THAT(condition2Metrics, UnorderedElementsAre(event1Index, event6Index));
+
+    // Verify the trackerToMetricMap.
+    ASSERT_EQ(trackerToMetricMap.size(), 4);
+    const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+    EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(event1Index));
+    const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
+    EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(event2Index));
+    const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+    EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(event3Index, event6Index));
+    const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
+    EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(event4Index));
+
+    // Verify event activation/deactivation maps.
+    ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 1);
+    EXPECT_THAT(activationAtomTrackerToMetricMap[matcher5Index],
+                UnorderedElementsAre(event3Index, event6Index));
+    ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(metricsWithActivation.size(), 2);
+    EXPECT_THAT(metricsWithActivation, UnorderedElementsAre(event3Index, event6Index));
+
+    // Verify tracker indices/ids/conditions are correct.
+    EXPECT_EQ(newMetricProducers[event1Index]->getMetricId(), event1Id);
+    EXPECT_EQ(newMetricProducers[event1Index]->mConditionTrackerIndex, predicate2Index);
+    EXPECT_EQ(newMetricProducers[event1Index]->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(newMetricProducers[event2Index]->getMetricId(), event2Id);
+    EXPECT_EQ(newMetricProducers[event2Index]->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(newMetricProducers[event2Index]->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(newMetricProducers[event3Index]->getMetricId(), event3Id);
+    EXPECT_EQ(newMetricProducers[event3Index]->mConditionTrackerIndex, -1);
+    EXPECT_EQ(newMetricProducers[event3Index]->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(newMetricProducers[event4Index]->getMetricId(), event4Id);
+    EXPECT_EQ(newMetricProducers[event4Index]->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(newMetricProducers[event4Index]->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(newMetricProducers[event6Index]->getMetricId(), event6Id);
+    EXPECT_EQ(newMetricProducers[event6Index]->mConditionTrackerIndex, predicate2Index);
+    EXPECT_EQ(newMetricProducers[event6Index]->mCondition, ConditionState::kTrue);
+
+    sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard;
+    EXPECT_NE(newConditionWizard, oldConditionWizard);
+    EXPECT_EQ(newConditionWizard->getStrongCount(), newMetricProducers.size() + 1);
+    oldMetricProducers.clear();
+    // Only reference to the old wizard should be the one in the test.
+    EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateCountMetrics) {
+    StatsdConfig config;
+
+    // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig.
+    AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+    int64_t matcher2Id = matcher2.id();
+    *config.add_atom_matcher() = matcher2;
+
+    AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher();
+    int64_t matcher4Id = matcher4.id();
+    *config.add_atom_matcher() = matcher4;
+
+    AtomMatcher matcher5 = CreateBatterySaverModeStartAtomMatcher();
+    int64_t matcher5Id = matcher5.id();
+    *config.add_atom_matcher() = matcher5;
+
+    Predicate predicate1 = CreateScreenIsOnPredicate();
+    int64_t predicate1Id = predicate1.id();
+    *config.add_predicate() = predicate1;
+
+    State state1 = CreateScreenStateWithOnOffMap(0x123, 0x321);
+    int64_t state1Id = state1.id();
+    *config.add_state() = state1;
+
+    State state2 = CreateScreenState();
+    int64_t state2Id = state2.id();
+    *config.add_state() = state2;
+
+    // Add a few count metrics.
+    // Will be preserved.
+    CountMetric count1 = createCountMetric("COUNT1", matcher1Id, predicate1Id, {state1Id});
+    int64_t count1Id = count1.id();
+    *config.add_count_metric() = count1;
+
+    // Will be replaced.
+    CountMetric count2 = createCountMetric("COUNT2", matcher2Id, nullopt, {});
+    int64_t count2Id = count2.id();
+    *config.add_count_metric() = count2;
+
+    // Will be replaced.
+    CountMetric count3 = createCountMetric("COUNT3", matcher3Id, nullopt, {});
+    int64_t count3Id = count3.id();
+    *config.add_count_metric() = count3;
+
+    // Will be replaced.
+    CountMetric count4 = createCountMetric("COUNT4", matcher4Id, nullopt, {state2Id});
+    int64_t count4Id = count4.id();
+    *config.add_count_metric() = count4;
+
+    // Will be deleted.
+    CountMetric count5 = createCountMetric("COUNT5", matcher5Id, nullopt, {});
+    int64_t count5Id = count5.id();
+    *config.add_count_metric() = count5;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Change bucket size of count2, causing it to be replaced.
+    count2.set_bucket(ONE_HOUR);
+
+    // Mark matcher 3 as replaced. Causes count3 to be replaced.
+    set<int64_t> replacedMatchers;
+    replacedMatchers.insert(matcher3Id);
+
+    // Mark state 2 as replaced and change the state to be about a different atom.
+    // Causes count4 to be replaced.
+    set<int64_t> replacedStates;
+    replacedStates.insert(state2Id);
+    state2.set_atom_id(util::BATTERY_SAVER_MODE_STATE_CHANGED);
+
+    // Fake that predicate 1 is true for count metric 1.
+    ASSERT_EQ(oldMetricProducers[0]->getMetricId(), count1Id);
+    oldMetricProducers[0]->onConditionChanged(true, /*timestamp=*/0);
+    EXPECT_EQ(oldMetricProducers[0]->mCondition, ConditionState::kTrue);
+
+    EXPECT_EQ(StateManager::getInstance().getStateTrackersCount(), 1);
+    // Tell the StateManager that the screen is on.
+    unique_ptr<LogEvent> event =
+            CreateScreenStateChangedEvent(0, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+    StateManager::getInstance().onLogEvent(*event);
+
+    // New count metric. Should have an initial condition of true since it depends on predicate1.
+    CountMetric count6 = createCountMetric("EVENT6", matcher2Id, predicate1Id, {state1Id});
+    int64_t count6Id = count6.id();
+
+    // Map the matchers and predicates in reverse order to force the indices to change.
+    std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    const int matcher5Index = 0;
+    newAtomMatchingTrackerMap[matcher5Id] = 0;
+    const int matcher4Index = 1;
+    newAtomMatchingTrackerMap[matcher4Id] = 1;
+    const int matcher3Index = 2;
+    newAtomMatchingTrackerMap[matcher3Id] = 2;
+    const int matcher2Index = 3;
+    newAtomMatchingTrackerMap[matcher2Id] = 3;
+    const int matcher1Index = 4;
+    newAtomMatchingTrackerMap[matcher1Id] = 4;
+    // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5);
+    std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+                      newAtomMatchingTrackers.begin());
+
+    std::unordered_map<int64_t, int> newConditionTrackerMap;
+    const int predicate1Index = 0;
+    newConditionTrackerMap[predicate1Id] = 0;
+    // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<ConditionTracker>> newConditionTrackers(1);
+    std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+                      newConditionTrackers.begin());
+    // Fake that predicate1 is true for all new metrics.
+    vector<ConditionState> conditionCache = {ConditionState::kTrue};
+
+    StatsdConfig newConfig;
+    *newConfig.add_count_metric() = count6;
+    const int count6Index = 0;
+    *newConfig.add_count_metric() = count3;
+    const int count3Index = 1;
+    *newConfig.add_count_metric() = count1;
+    const int count1Index = 2;
+    *newConfig.add_count_metric() = count4;
+    const int count4Index = 3;
+    *newConfig.add_count_metric() = count2;
+    const int count2Index = 4;
+
+    *newConfig.add_state() = state1;
+    *newConfig.add_state() = state2;
+
+    unordered_map<int64_t, int> stateAtomIdMap;
+    unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
+    map<int64_t, uint64_t> stateProtoHashes;
+    EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes));
+    EXPECT_EQ(stateAtomIdMap[state2Id], util::BATTERY_SAVER_MODE_STATE_CHANGED);
+
+    // Output data structures to validate.
+    unordered_map<int64_t, int> newMetricProducerMap;
+    vector<sp<MetricProducer>> newMetricProducers;
+    unordered_map<int, vector<int>> conditionToMetricMap;
+    unordered_map<int, vector<int>> trackerToMetricMap;
+    set<int64_t> noReportMetricIds;
+    unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+    vector<int> metricsWithActivation;
+    set<int64_t> replacedMetrics;
+    EXPECT_TRUE(updateMetrics(
+            key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+            oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
+            newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{},
+            newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates,
+            oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers,
+            conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+            metricsWithActivation, replacedMetrics));
+
+    unordered_map<int64_t, int> expectedMetricProducerMap = {
+            {count1Id, count1Index}, {count2Id, count2Index}, {count3Id, count3Index},
+            {count4Id, count4Index}, {count6Id, count6Index},
+    };
+    EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+    EXPECT_EQ(replacedMetrics, set<int64_t>({count2Id, count3Id, count4Id}));
+
+    // Make sure preserved metrics are the same.
+    ASSERT_EQ(newMetricProducers.size(), 5);
+    EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(count1Id)],
+              newMetricProducers[newMetricProducerMap.at(count1Id)]);
+
+    // Make sure replaced metrics are different.
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count2Id)],
+              newMetricProducers[newMetricProducerMap.at(count2Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count3Id)],
+              newMetricProducers[newMetricProducerMap.at(count3Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(count4Id)],
+              newMetricProducers[newMetricProducerMap.at(count4Id)]);
+
+    // Verify the conditionToMetricMap.
+    ASSERT_EQ(conditionToMetricMap.size(), 1);
+    const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
+    EXPECT_THAT(condition1Metrics, UnorderedElementsAre(count1Index, count6Index));
+
+    // Verify the trackerToMetricMap.
+    ASSERT_EQ(trackerToMetricMap.size(), 4);
+    const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+    EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(count1Index));
+    const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
+    EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(count2Index, count6Index));
+    const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+    EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(count3Index));
+    const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
+    EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(count4Index));
+
+    // Verify event activation/deactivation maps.
+    ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(metricsWithActivation.size(), 0);
+
+    // Verify tracker indices/ids/conditions/states are correct.
+    EXPECT_EQ(newMetricProducers[count1Index]->getMetricId(), count1Id);
+    EXPECT_EQ(newMetricProducers[count1Index]->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(newMetricProducers[count1Index]->mCondition, ConditionState::kTrue);
+    EXPECT_THAT(newMetricProducers[count1Index]->getSlicedStateAtoms(),
+                UnorderedElementsAre(util::SCREEN_STATE_CHANGED));
+    EXPECT_EQ(newMetricProducers[count2Index]->getMetricId(), count2Id);
+    EXPECT_EQ(newMetricProducers[count2Index]->mConditionTrackerIndex, -1);
+    EXPECT_EQ(newMetricProducers[count2Index]->mCondition, ConditionState::kTrue);
+    EXPECT_TRUE(newMetricProducers[count2Index]->getSlicedStateAtoms().empty());
+    EXPECT_EQ(newMetricProducers[count3Index]->getMetricId(), count3Id);
+    EXPECT_EQ(newMetricProducers[count3Index]->mConditionTrackerIndex, -1);
+    EXPECT_EQ(newMetricProducers[count3Index]->mCondition, ConditionState::kTrue);
+    EXPECT_TRUE(newMetricProducers[count3Index]->getSlicedStateAtoms().empty());
+    EXPECT_EQ(newMetricProducers[count4Index]->getMetricId(), count4Id);
+    EXPECT_EQ(newMetricProducers[count4Index]->mConditionTrackerIndex, -1);
+    EXPECT_EQ(newMetricProducers[count4Index]->mCondition, ConditionState::kTrue);
+    EXPECT_THAT(newMetricProducers[count4Index]->getSlicedStateAtoms(),
+                UnorderedElementsAre(util::BATTERY_SAVER_MODE_STATE_CHANGED));
+    EXPECT_EQ(newMetricProducers[count6Index]->getMetricId(), count6Id);
+    EXPECT_EQ(newMetricProducers[count6Index]->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(newMetricProducers[count6Index]->mCondition, ConditionState::kTrue);
+    EXPECT_THAT(newMetricProducers[count6Index]->getSlicedStateAtoms(),
+                UnorderedElementsAre(util::SCREEN_STATE_CHANGED));
+
+    oldMetricProducers.clear();
+    // Ensure that the screen state StateTracker did not get deleted and replaced.
+    EXPECT_EQ(StateManager::getInstance().getStateTrackersCount(), 2);
+    FieldValue screenState;
+    StateManager::getInstance().getStateValue(util::SCREEN_STATE_CHANGED, DEFAULT_DIMENSION_KEY,
+                                              &screenState);
+    EXPECT_EQ(screenState.mValue.int_value, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateGaugeMetrics) {
+    StatsdConfig config;
+
+    // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig.
+    AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+    int64_t matcher2Id = matcher2.id();
+    *config.add_atom_matcher() = matcher2;
+
+    AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    AtomMatcher matcher4 = CreateTemperatureAtomMatcher();
+    int64_t matcher4Id = matcher4.id();
+    *config.add_atom_matcher() = matcher4;
+
+    AtomMatcher matcher5 = CreateSimpleAtomMatcher("SubsystemSleep", util::SUBSYSTEM_SLEEP_STATE);
+    int64_t matcher5Id = matcher5.id();
+    *config.add_atom_matcher() = matcher5;
+
+    Predicate predicate1 = CreateScreenIsOnPredicate();
+    int64_t predicate1Id = predicate1.id();
+    *config.add_predicate() = predicate1;
+
+    // Add a few gauge metrics.
+    // Will be preserved.
+    GaugeMetric gauge1 = createGaugeMetric("GAUGE1", matcher4Id, GaugeMetric::FIRST_N_SAMPLES,
+                                           predicate1Id, matcher1Id);
+    int64_t gauge1Id = gauge1.id();
+    *config.add_gauge_metric() = gauge1;
+
+    // Will be replaced.
+    GaugeMetric gauge2 =
+            createGaugeMetric("GAUGE2", matcher1Id, GaugeMetric::FIRST_N_SAMPLES, nullopt, nullopt);
+    int64_t gauge2Id = gauge2.id();
+    *config.add_gauge_metric() = gauge2;
+
+    // Will be replaced.
+    GaugeMetric gauge3 = createGaugeMetric("GAUGE3", matcher5Id, GaugeMetric::FIRST_N_SAMPLES,
+                                           nullopt, matcher3Id);
+    int64_t gauge3Id = gauge3.id();
+    *config.add_gauge_metric() = gauge3;
+
+    // Will be replaced.
+    GaugeMetric gauge4 = createGaugeMetric("GAUGE4", matcher3Id, GaugeMetric::RANDOM_ONE_SAMPLE,
+                                           predicate1Id, nullopt);
+    int64_t gauge4Id = gauge4.id();
+    *config.add_gauge_metric() = gauge4;
+
+    // Will be deleted.
+    GaugeMetric gauge5 =
+            createGaugeMetric("GAUGE5", matcher2Id, GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, {});
+    int64_t gauge5Id = gauge5.id();
+    *config.add_gauge_metric() = gauge5;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Used later to ensure the condition wizard is replaced. Get it before doing the update.
+    sp<EventMatcherWizard> oldMatcherWizard =
+            static_cast<GaugeMetricProducer*>(oldMetricProducers[0].get())->mEventMatcherWizard;
+    EXPECT_EQ(oldMatcherWizard->getStrongCount(), 6);
+
+    // Change gauge2, causing it to be replaced.
+    gauge2.set_max_num_gauge_atoms_per_bucket(50);
+
+    // Mark matcher 3 as replaced. Causes gauge3 and gauge4 to be replaced.
+    set<int64_t> replacedMatchers = {matcher3Id};
+
+    // New gauge metric.
+    GaugeMetric gauge6 = createGaugeMetric("GAUGE6", matcher5Id, GaugeMetric::FIRST_N_SAMPLES,
+                                           predicate1Id, matcher3Id);
+    int64_t gauge6Id = gauge6.id();
+
+    // Map the matchers and predicates in reverse order to force the indices to change.
+    std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    const int matcher5Index = 0;
+    newAtomMatchingTrackerMap[matcher5Id] = 0;
+    const int matcher4Index = 1;
+    newAtomMatchingTrackerMap[matcher4Id] = 1;
+    const int matcher3Index = 2;
+    newAtomMatchingTrackerMap[matcher3Id] = 2;
+    const int matcher2Index = 3;
+    newAtomMatchingTrackerMap[matcher2Id] = 3;
+    const int matcher1Index = 4;
+    newAtomMatchingTrackerMap[matcher1Id] = 4;
+    // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5);
+    std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+                      newAtomMatchingTrackers.begin());
+
+    std::unordered_map<int64_t, int> newConditionTrackerMap;
+    const int predicate1Index = 0;
+    newConditionTrackerMap[predicate1Id] = 0;
+    // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<ConditionTracker>> newConditionTrackers(1);
+    std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+                      newConditionTrackers.begin());
+    // Say that predicate1 is unknown since the initial condition never changed.
+    vector<ConditionState> conditionCache = {ConditionState::kUnknown};
+
+    StatsdConfig newConfig;
+    *newConfig.add_gauge_metric() = gauge6;
+    const int gauge6Index = 0;
+    *newConfig.add_gauge_metric() = gauge3;
+    const int gauge3Index = 1;
+    *newConfig.add_gauge_metric() = gauge1;
+    const int gauge1Index = 2;
+    *newConfig.add_gauge_metric() = gauge4;
+    const int gauge4Index = 3;
+    *newConfig.add_gauge_metric() = gauge2;
+    const int gauge2Index = 4;
+
+    // Output data structures to validate.
+    unordered_map<int64_t, int> newMetricProducerMap;
+    vector<sp<MetricProducer>> newMetricProducers;
+    unordered_map<int, vector<int>> conditionToMetricMap;
+    unordered_map<int, vector<int>> trackerToMetricMap;
+    set<int64_t> noReportMetricIds;
+    unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+    vector<int> metricsWithActivation;
+    set<int64_t> replacedMetrics;
+    EXPECT_TRUE(updateMetrics(
+            key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+            oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
+            newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{},
+            newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{},
+            /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
+            newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+            metricsWithActivation, replacedMetrics));
+
+    unordered_map<int64_t, int> expectedMetricProducerMap = {
+            {gauge1Id, gauge1Index}, {gauge2Id, gauge2Index}, {gauge3Id, gauge3Index},
+            {gauge4Id, gauge4Index}, {gauge6Id, gauge6Index},
+    };
+    EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+    EXPECT_EQ(replacedMetrics, set<int64_t>({gauge2Id, gauge3Id, gauge4Id}));
+
+    // Make sure preserved metrics are the same.
+    ASSERT_EQ(newMetricProducers.size(), 5);
+    EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(gauge1Id)],
+              newMetricProducers[newMetricProducerMap.at(gauge1Id)]);
+
+    // Make sure replaced metrics are different.
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge2Id)],
+              newMetricProducers[newMetricProducerMap.at(gauge2Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge3Id)],
+              newMetricProducers[newMetricProducerMap.at(gauge3Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gauge4Id)],
+              newMetricProducers[newMetricProducerMap.at(gauge4Id)]);
+
+    // Verify the conditionToMetricMap.
+    ASSERT_EQ(conditionToMetricMap.size(), 1);
+    const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
+    EXPECT_THAT(condition1Metrics, UnorderedElementsAre(gauge1Index, gauge4Index, gauge6Index));
+
+    // Verify the trackerToMetricMap.
+    ASSERT_EQ(trackerToMetricMap.size(), 4);
+    const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+    EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(gauge1Index, gauge2Index));
+    const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+    EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(gauge3Index, gauge4Index, gauge6Index));
+    const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
+    EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(gauge1Index));
+    const vector<int>& matcher5Metrics = trackerToMetricMap[matcher5Index];
+    EXPECT_THAT(matcher5Metrics, UnorderedElementsAre(gauge3Index, gauge6Index));
+
+    // Verify event activation/deactivation maps.
+    ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(metricsWithActivation.size(), 0);
+
+    // Verify tracker indices/ids/conditions/states are correct.
+    GaugeMetricProducer* gaugeProducer1 =
+            static_cast<GaugeMetricProducer*>(newMetricProducers[gauge1Index].get());
+    EXPECT_EQ(gaugeProducer1->getMetricId(), gauge1Id);
+    EXPECT_EQ(gaugeProducer1->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(gaugeProducer1->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(gaugeProducer1->mWhatMatcherIndex, matcher4Index);
+    GaugeMetricProducer* gaugeProducer2 =
+            static_cast<GaugeMetricProducer*>(newMetricProducers[gauge2Index].get());
+    EXPECT_EQ(gaugeProducer2->getMetricId(), gauge2Id);
+    EXPECT_EQ(gaugeProducer2->mConditionTrackerIndex, -1);
+    EXPECT_EQ(gaugeProducer2->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(gaugeProducer2->mWhatMatcherIndex, matcher1Index);
+    GaugeMetricProducer* gaugeProducer3 =
+            static_cast<GaugeMetricProducer*>(newMetricProducers[gauge3Index].get());
+    EXPECT_EQ(gaugeProducer3->getMetricId(), gauge3Id);
+    EXPECT_EQ(gaugeProducer3->mConditionTrackerIndex, -1);
+    EXPECT_EQ(gaugeProducer3->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(gaugeProducer3->mWhatMatcherIndex, matcher5Index);
+    GaugeMetricProducer* gaugeProducer4 =
+            static_cast<GaugeMetricProducer*>(newMetricProducers[gauge4Index].get());
+    EXPECT_EQ(gaugeProducer4->getMetricId(), gauge4Id);
+    EXPECT_EQ(gaugeProducer4->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(gaugeProducer4->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(gaugeProducer4->mWhatMatcherIndex, matcher3Index);
+    GaugeMetricProducer* gaugeProducer6 =
+            static_cast<GaugeMetricProducer*>(newMetricProducers[gauge6Index].get());
+    EXPECT_EQ(gaugeProducer6->getMetricId(), gauge6Id);
+    EXPECT_EQ(gaugeProducer6->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(gaugeProducer6->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(gaugeProducer6->mWhatMatcherIndex, matcher5Index);
+
+    sp<EventMatcherWizard> newMatcherWizard = gaugeProducer1->mEventMatcherWizard;
+    EXPECT_NE(newMatcherWizard, oldMatcherWizard);
+    EXPECT_EQ(newMatcherWizard->getStrongCount(), 6);
+    oldMetricProducers.clear();
+    // Only reference to the old wizard should be the one in the test.
+    EXPECT_EQ(oldMatcherWizard->getStrongCount(), 1);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateDurationMetrics) {
+    StatsdConfig config;
+    // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig.
+    AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+    int64_t matcher2Id = matcher2.id();
+    *config.add_atom_matcher() = matcher2;
+
+    AtomMatcher matcher3 = CreateAcquireWakelockAtomMatcher();
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    AtomMatcher matcher4 = CreateReleaseWakelockAtomMatcher();
+    int64_t matcher4Id = matcher4.id();
+    *config.add_atom_matcher() = matcher4;
+
+    AtomMatcher matcher5 = CreateMoveToForegroundAtomMatcher();
+    int64_t matcher5Id = matcher5.id();
+    *config.add_atom_matcher() = matcher5;
+
+    AtomMatcher matcher6 = CreateMoveToBackgroundAtomMatcher();
+    int64_t matcher6Id = matcher6.id();
+    *config.add_atom_matcher() = matcher6;
+
+    AtomMatcher matcher7 = CreateBatteryStateNoneMatcher();
+    int64_t matcher7Id = matcher7.id();
+    *config.add_atom_matcher() = matcher7;
+
+    AtomMatcher matcher8 = CreateBatteryStateUsbMatcher();
+    int64_t matcher8Id = matcher8.id();
+    *config.add_atom_matcher() = matcher8;
+
+    Predicate predicate1 = CreateScreenIsOnPredicate();
+    int64_t predicate1Id = predicate1.id();
+    *config.add_predicate() = predicate1;
+
+    Predicate predicate2 = CreateScreenIsOffPredicate();
+    int64_t predicate2Id = predicate2.id();
+    *config.add_predicate() = predicate2;
+
+    Predicate predicate3 = CreateDeviceUnpluggedPredicate();
+    int64_t predicate3Id = predicate3.id();
+    *config.add_predicate() = predicate3;
+
+    Predicate predicate4 = CreateIsInBackgroundPredicate();
+    *predicate4.mutable_simple_predicate()->mutable_dimensions() =
+            CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1});
+    int64_t predicate4Id = predicate4.id();
+    *config.add_predicate() = predicate4;
+
+    Predicate predicate5 = CreateHoldingWakelockPredicate();
+    *predicate5.mutable_simple_predicate()->mutable_dimensions() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    predicate5.mutable_simple_predicate()->set_stop_all(matcher7Id);
+    int64_t predicate5Id = predicate5.id();
+    *config.add_predicate() = predicate5;
+
+    State state1 = CreateScreenStateWithOnOffMap(0x123, 0x321);
+    int64_t state1Id = state1.id();
+    *config.add_state() = state1;
+
+    State state2 = CreateScreenState();
+    int64_t state2Id = state2.id();
+    *config.add_state() = state2;
+
+    // Add a few duration metrics.
+    // Will be preserved.
+    DurationMetric duration1 =
+            createDurationMetric("DURATION1", predicate5Id, predicate4Id, {state2Id});
+    *duration1.mutable_dimensions_in_what() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    MetricConditionLink* link = duration1.add_links();
+    link->set_condition(predicate4Id);
+    *link->mutable_fields_in_what() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    *link->mutable_fields_in_condition() =
+            CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/);
+    int64_t duration1Id = duration1.id();
+    *config.add_duration_metric() = duration1;
+
+    // Will be replaced.
+    DurationMetric duration2 = createDurationMetric("DURATION2", predicate1Id, nullopt, {});
+    int64_t duration2Id = duration2.id();
+    *config.add_duration_metric() = duration2;
+
+    // Will be replaced.
+    DurationMetric duration3 = createDurationMetric("DURATION3", predicate3Id, nullopt, {state1Id});
+    int64_t duration3Id = duration3.id();
+    *config.add_duration_metric() = duration3;
+
+    // Will be replaced.
+    DurationMetric duration4 = createDurationMetric("DURATION4", predicate3Id, predicate2Id, {});
+    int64_t duration4Id = duration4.id();
+    *config.add_duration_metric() = duration4;
+
+    // Will be deleted.
+    DurationMetric duration5 = createDurationMetric("DURATION5", predicate2Id, nullopt, {});
+    int64_t duration5Id = duration5.id();
+    *config.add_duration_metric() = duration5;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Make some sliced conditions true.
+    int uid1 = 10;
+    int uid2 = 11;
+    vector<MatchingState> matchingStates(8, MatchingState::kNotMatched);
+    matchingStates[2] = kMatched;
+    vector<ConditionState> conditionCache(5, ConditionState::kNotEvaluated);
+    vector<bool> changedCache(5, false);
+    unique_ptr<LogEvent> event = CreateAcquireWakelockEvent(timeBaseNs + 3, {uid1}, {"tag"}, "wl1");
+    oldConditionTrackers[4]->evaluateCondition(*event.get(), matchingStates, oldConditionTrackers,
+                                               conditionCache, changedCache);
+    EXPECT_TRUE(oldConditionTrackers[4]->isSliced());
+    EXPECT_TRUE(changedCache[4]);
+    EXPECT_EQ(conditionCache[4], ConditionState::kTrue);
+    oldMetricProducers[0]->onMatchedLogEvent(2, *event.get());
+
+    fill(conditionCache.begin(), conditionCache.end(), ConditionState::kNotEvaluated);
+    fill(changedCache.begin(), changedCache.end(), false);
+    event = CreateAcquireWakelockEvent(timeBaseNs + 3, {uid2}, {"tag"}, "wl2");
+    oldConditionTrackers[4]->evaluateCondition(*event.get(), matchingStates, oldConditionTrackers,
+                                               conditionCache, changedCache);
+    EXPECT_TRUE(changedCache[4]);
+    EXPECT_EQ(conditionCache[4], ConditionState::kTrue);
+    oldMetricProducers[0]->onMatchedLogEvent(2, *event.get());
+
+    // Used later to ensure the condition wizard is replaced. Get it before doing the update.
+    // The duration trackers have a pointer to the wizard, and 2 trackers were created above.
+    sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard;
+    EXPECT_EQ(oldConditionWizard->getStrongCount(), 8);
+
+    // Replace predicate1, predicate3, and state1. Causes duration2/3/4 to be replaced.
+    set<int64_t> replacedConditions({predicate1Id, predicate2Id});
+    set<int64_t> replacedStates({state1Id});
+
+    // New duration metric.
+    DurationMetric duration6 = createDurationMetric("DURATION6", predicate4Id, predicate5Id, {});
+    *duration6.mutable_dimensions_in_what() =
+            CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/);
+    link = duration6.add_links();
+    link->set_condition(predicate5Id);
+    *link->mutable_fields_in_what() =
+            CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1} /*uid field*/);
+    *link->mutable_fields_in_condition() =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    int64_t duration6Id = duration6.id();
+
+    // Map the matchers and predicates in reverse order to force the indices to change.
+    const int matcher8Index = 0, matcher7Index = 1, matcher6Index = 2, matcher5Index = 3,
+              matcher4Index = 4, matcher3Index = 5, matcher2Index = 6, matcher1Index = 7;
+    std::unordered_map<int64_t, int> newAtomMatchingTrackerMap({{matcher8Id, matcher8Index},
+                                                                {matcher7Id, matcher7Index},
+                                                                {matcher6Id, matcher6Index},
+                                                                {matcher5Id, matcher5Index},
+                                                                {matcher4Id, matcher4Index},
+                                                                {matcher3Id, matcher3Index},
+                                                                {matcher2Id, matcher2Index},
+                                                                {matcher1Id, matcher1Index}});
+    // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(8);
+    reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+                 newAtomMatchingTrackers.begin());
+
+    const int predicate5Index = 0, predicate4Index = 1, predicate3Index = 2, predicate2Index = 3,
+              predicate1Index = 4;
+    std::unordered_map<int64_t, int> newConditionTrackerMap({
+            {predicate5Id, predicate5Index},
+            {predicate4Id, predicate4Index},
+            {predicate3Id, predicate3Index},
+            {predicate2Id, predicate2Index},
+            {predicate1Id, predicate1Index},
+    });
+    // Use the existing conditionTrackers and reinitialize them to get the initial condition cache.
+    vector<sp<ConditionTracker>> newConditionTrackers(5);
+    reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+                 newConditionTrackers.begin());
+    vector<Predicate> conditionProtos(5);
+    reverse_copy(config.predicate().begin(), config.predicate().end(), conditionProtos.begin());
+    for (int i = 0; i < newConditionTrackers.size(); i++) {
+        EXPECT_TRUE(newConditionTrackers[i]->onConfigUpdated(
+                conditionProtos, i, newConditionTrackers, newAtomMatchingTrackerMap,
+                newConditionTrackerMap));
+    }
+    vector<bool> cycleTracker(5, false);
+    fill(conditionCache.begin(), conditionCache.end(), ConditionState::kNotEvaluated);
+    for (int i = 0; i < newConditionTrackers.size(); i++) {
+        EXPECT_TRUE(newConditionTrackers[i]->init(conditionProtos, newConditionTrackers,
+                                                  newConditionTrackerMap, cycleTracker,
+                                                  conditionCache));
+    }
+    // Predicate5 should be true since 2 uids have wakelocks
+    EXPECT_EQ(conditionCache, vector({kTrue, kUnknown, kUnknown, kUnknown, kUnknown}));
+
+    StatsdConfig newConfig;
+    *newConfig.add_duration_metric() = duration6;
+    const int duration6Index = 0;
+    *newConfig.add_duration_metric() = duration3;
+    const int duration3Index = 1;
+    *newConfig.add_duration_metric() = duration1;
+    const int duration1Index = 2;
+    *newConfig.add_duration_metric() = duration4;
+    const int duration4Index = 3;
+    *newConfig.add_duration_metric() = duration2;
+    const int duration2Index = 4;
+
+    for (const Predicate& predicate : conditionProtos) {
+        *newConfig.add_predicate() = predicate;
+    }
+    *newConfig.add_state() = state1;
+    *newConfig.add_state() = state2;
+    unordered_map<int64_t, int> stateAtomIdMap;
+    unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
+    map<int64_t, uint64_t> stateProtoHashes;
+    EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes));
+
+    // Output data structures to validate.
+    unordered_map<int64_t, int> newMetricProducerMap;
+    vector<sp<MetricProducer>> newMetricProducers;
+    unordered_map<int, vector<int>> conditionToMetricMap;
+    unordered_map<int, vector<int>> trackerToMetricMap;
+    set<int64_t> noReportMetricIds;
+    unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+    vector<int> metricsWithActivation;
+    set<int64_t> replacedMetrics;
+    EXPECT_TRUE(updateMetrics(
+            key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+            oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, /*replacedMatchers=*/{},
+            newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
+            newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates,
+            oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers,
+            conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+            metricsWithActivation, replacedMetrics));
+
+    unordered_map<int64_t, int> expectedMetricProducerMap = {
+            {duration1Id, duration1Index}, {duration2Id, duration2Index},
+            {duration3Id, duration3Index}, {duration4Id, duration4Index},
+            {duration6Id, duration6Index},
+    };
+    EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+    EXPECT_EQ(replacedMetrics, set<int64_t>({duration2Id, duration3Id, duration4Id}));
+    // Make sure preserved metrics are the same.
+    ASSERT_EQ(newMetricProducers.size(), 5);
+    EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(duration1Id)],
+              newMetricProducers[newMetricProducerMap.at(duration1Id)]);
+
+    // Make sure replaced metrics are different.
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration2Id)],
+              newMetricProducers[newMetricProducerMap.at(duration2Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration3Id)],
+              newMetricProducers[newMetricProducerMap.at(duration3Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(duration4Id)],
+              newMetricProducers[newMetricProducerMap.at(duration4Id)]);
+
+    // Verify the conditionToMetricMap. Note that the "what" is not in this map.
+    ASSERT_EQ(conditionToMetricMap.size(), 3);
+    const vector<int>& condition2Metrics = conditionToMetricMap[predicate2Index];
+    EXPECT_THAT(condition2Metrics, UnorderedElementsAre(duration4Index));
+    const vector<int>& condition4Metrics = conditionToMetricMap[predicate4Index];
+    EXPECT_THAT(condition4Metrics, UnorderedElementsAre(duration1Index));
+    const vector<int>& condition5Metrics = conditionToMetricMap[predicate5Index];
+    EXPECT_THAT(condition5Metrics, UnorderedElementsAre(duration6Index));
+
+    // Verify the trackerToMetricMap. The start/stop/stopall indices from the "what" should be here.
+    ASSERT_EQ(trackerToMetricMap.size(), 8);
+    const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+    EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(duration2Index));
+    const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
+    EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(duration2Index));
+    const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+    EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(duration1Index));
+    const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
+    EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(duration1Index));
+    const vector<int>& matcher5Metrics = trackerToMetricMap[matcher5Index];
+    EXPECT_THAT(matcher5Metrics, UnorderedElementsAre(duration6Index));
+    const vector<int>& matcher6Metrics = trackerToMetricMap[matcher6Index];
+    EXPECT_THAT(matcher6Metrics, UnorderedElementsAre(duration6Index));
+    const vector<int>& matcher7Metrics = trackerToMetricMap[matcher7Index];
+    EXPECT_THAT(matcher7Metrics,
+                UnorderedElementsAre(duration1Index, duration3Index, duration4Index));
+    const vector<int>& matcher8Metrics = trackerToMetricMap[matcher8Index];
+    EXPECT_THAT(matcher8Metrics, UnorderedElementsAre(duration3Index, duration4Index));
+
+    // Verify event activation/deactivation maps.
+    ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(metricsWithActivation.size(), 0);
+
+    // Verify tracker indices/ids/conditions are correct.
+    DurationMetricProducer* durationProducer1 =
+            static_cast<DurationMetricProducer*>(newMetricProducers[duration1Index].get());
+    EXPECT_EQ(durationProducer1->getMetricId(), duration1Id);
+    EXPECT_EQ(durationProducer1->mConditionTrackerIndex, predicate4Index);
+    EXPECT_EQ(durationProducer1->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(durationProducer1->mStartIndex, matcher3Index);
+    EXPECT_EQ(durationProducer1->mStopIndex, matcher4Index);
+    EXPECT_EQ(durationProducer1->mStopAllIndex, matcher7Index);
+    EXPECT_EQ(durationProducer1->mCurrentSlicedDurationTrackerMap.size(), 2);
+    for (const auto& durationTrackerIt : durationProducer1->mCurrentSlicedDurationTrackerMap) {
+        EXPECT_EQ(durationTrackerIt.second->mConditionTrackerIndex, predicate4Index);
+    }
+    DurationMetricProducer* durationProducer2 =
+            static_cast<DurationMetricProducer*>(newMetricProducers[duration2Index].get());
+    EXPECT_EQ(durationProducer2->getMetricId(), duration2Id);
+    EXPECT_EQ(durationProducer2->mConditionTrackerIndex, -1);
+    EXPECT_EQ(durationProducer2->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(durationProducer2->mStartIndex, matcher1Index);
+    EXPECT_EQ(durationProducer2->mStopIndex, matcher2Index);
+    EXPECT_EQ(durationProducer2->mStopAllIndex, -1);
+    DurationMetricProducer* durationProducer3 =
+            static_cast<DurationMetricProducer*>(newMetricProducers[duration3Index].get());
+    EXPECT_EQ(durationProducer3->getMetricId(), duration3Id);
+    EXPECT_EQ(durationProducer3->mConditionTrackerIndex, -1);
+    EXPECT_EQ(durationProducer3->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(durationProducer3->mStartIndex, matcher7Index);
+    EXPECT_EQ(durationProducer3->mStopIndex, matcher8Index);
+    EXPECT_EQ(durationProducer3->mStopAllIndex, -1);
+    DurationMetricProducer* durationProducer4 =
+            static_cast<DurationMetricProducer*>(newMetricProducers[duration4Index].get());
+    EXPECT_EQ(durationProducer4->getMetricId(), duration4Id);
+    EXPECT_EQ(durationProducer4->mConditionTrackerIndex, predicate2Index);
+    EXPECT_EQ(durationProducer4->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(durationProducer4->mStartIndex, matcher7Index);
+    EXPECT_EQ(durationProducer4->mStopIndex, matcher8Index);
+    EXPECT_EQ(durationProducer4->mStopAllIndex, -1);
+    DurationMetricProducer* durationProducer6 =
+            static_cast<DurationMetricProducer*>(newMetricProducers[duration6Index].get());
+    EXPECT_EQ(durationProducer6->getMetricId(), duration6Id);
+    EXPECT_EQ(durationProducer6->mConditionTrackerIndex, predicate5Index);
+    // TODO(b/167491517): should this be unknown since the condition is sliced?
+    EXPECT_EQ(durationProducer6->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(durationProducer6->mStartIndex, matcher6Index);
+    EXPECT_EQ(durationProducer6->mStopIndex, matcher5Index);
+    EXPECT_EQ(durationProducer6->mStopAllIndex, -1);
+
+    sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard;
+    EXPECT_NE(newConditionWizard, oldConditionWizard);
+    EXPECT_EQ(newConditionWizard->getStrongCount(), 8);
+    oldMetricProducers.clear();
+    // Only reference to the old wizard should be the one in the test.
+    EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateValueMetrics) {
+    StatsdConfig config;
+
+    // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig.
+    AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+    int64_t matcher2Id = matcher2.id();
+    *config.add_atom_matcher() = matcher2;
+
+    AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    AtomMatcher matcher4 = CreateTemperatureAtomMatcher();
+    int64_t matcher4Id = matcher4.id();
+    *config.add_atom_matcher() = matcher4;
+
+    AtomMatcher matcher5 = CreateSimpleAtomMatcher("SubsystemSleep", util::SUBSYSTEM_SLEEP_STATE);
+    int64_t matcher5Id = matcher5.id();
+    *config.add_atom_matcher() = matcher5;
+
+    Predicate predicate1 = CreateScreenIsOnPredicate();
+    int64_t predicate1Id = predicate1.id();
+    *config.add_predicate() = predicate1;
+
+    Predicate predicate2 = CreateScreenIsOffPredicate();
+    int64_t predicate2Id = predicate2.id();
+    *config.add_predicate() = predicate2;
+
+    State state1 = CreateScreenStateWithOnOffMap(0x123, 0x321);
+    int64_t state1Id = state1.id();
+    *config.add_state() = state1;
+
+    State state2 = CreateScreenState();
+    int64_t state2Id = state2.id();
+    *config.add_state() = state2;
+
+    // Add a few value metrics.
+    // Note that these will not work as "real" metrics since the value field is always 2.
+    // Will be preserved.
+    ValueMetric value1 = createValueMetric("VALUE1", matcher4, predicate1Id, {state1Id});
+    int64_t value1Id = value1.id();
+    *config.add_value_metric() = value1;
+
+    // Will be replaced - definition change.
+    ValueMetric value2 = createValueMetric("VALUE2", matcher1, nullopt, {});
+    int64_t value2Id = value2.id();
+    *config.add_value_metric() = value2;
+
+    // Will be replaced - condition change.
+    ValueMetric value3 = createValueMetric("VALUE3", matcher5, predicate2Id, {});
+    int64_t value3Id = value3.id();
+    *config.add_value_metric() = value3;
+
+    // Will be replaced - state change.
+    ValueMetric value4 = createValueMetric("VALUE4", matcher3, nullopt, {state2Id});
+    int64_t value4Id = value4.id();
+    *config.add_value_metric() = value4;
+
+    // Will be deleted.
+    ValueMetric value5 = createValueMetric("VALUE5", matcher2, nullopt, {});
+    int64_t value5Id = value5.id();
+    *config.add_value_metric() = value5;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Used later to ensure the condition wizard is replaced. Get it before doing the update.
+    sp<EventMatcherWizard> oldMatcherWizard =
+            static_cast<ValueMetricProducer*>(oldMetricProducers[0].get())->mEventMatcherWizard;
+    EXPECT_EQ(oldMatcherWizard->getStrongCount(), 6);
+
+    // Change value2, causing it to be replaced.
+    value2.set_aggregation_type(ValueMetric::AVG);
+
+    // Mark predicate 2 as replaced. Causes value3 to be replaced.
+    set<int64_t> replacedConditions = {predicate2Id};
+
+    // Mark state 2 as replaced. Causes value4 to be replaced.
+    set<int64_t> replacedStates = {state2Id};
+
+    // New value metric.
+    ValueMetric value6 = createValueMetric("VALUE6", matcher5, predicate1Id, {state1Id});
+    int64_t value6Id = value6.id();
+
+    // Map the matchers and predicates in reverse order to force the indices to change.
+    std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    const int matcher5Index = 0;
+    newAtomMatchingTrackerMap[matcher5Id] = 0;
+    const int matcher4Index = 1;
+    newAtomMatchingTrackerMap[matcher4Id] = 1;
+    const int matcher3Index = 2;
+    newAtomMatchingTrackerMap[matcher3Id] = 2;
+    const int matcher2Index = 3;
+    newAtomMatchingTrackerMap[matcher2Id] = 3;
+    const int matcher1Index = 4;
+    newAtomMatchingTrackerMap[matcher1Id] = 4;
+    // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(5);
+    std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+                      newAtomMatchingTrackers.begin());
+
+    std::unordered_map<int64_t, int> newConditionTrackerMap;
+    const int predicate2Index = 0;
+    newConditionTrackerMap[predicate2Id] = 0;
+    const int predicate1Index = 1;
+    newConditionTrackerMap[predicate1Id] = 1;
+    // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<ConditionTracker>> newConditionTrackers(2);
+    std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+                      newConditionTrackers.begin());
+    // Say that predicate1 & predicate2 is unknown since the initial condition never changed.
+    vector<ConditionState> conditionCache = {ConditionState::kUnknown, ConditionState::kUnknown};
+
+    StatsdConfig newConfig;
+    *newConfig.add_value_metric() = value6;
+    const int value6Index = 0;
+    *newConfig.add_value_metric() = value3;
+    const int value3Index = 1;
+    *newConfig.add_value_metric() = value1;
+    const int value1Index = 2;
+    *newConfig.add_value_metric() = value4;
+    const int value4Index = 3;
+    *newConfig.add_value_metric() = value2;
+    const int value2Index = 4;
+
+    *newConfig.add_state() = state1;
+    *newConfig.add_state() = state2;
+
+    unordered_map<int64_t, int> stateAtomIdMap;
+    unordered_map<int64_t, unordered_map<int, int64_t>> allStateGroupMaps;
+    map<int64_t, uint64_t> stateProtoHashes;
+    EXPECT_TRUE(initStates(newConfig, stateAtomIdMap, allStateGroupMaps, stateProtoHashes));
+
+    // Output data structures to validate.
+    unordered_map<int64_t, int> newMetricProducerMap;
+    vector<sp<MetricProducer>> newMetricProducers;
+    unordered_map<int, vector<int>> conditionToMetricMap;
+    unordered_map<int, vector<int>> trackerToMetricMap;
+    set<int64_t> noReportMetricIds;
+    unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+    vector<int> metricsWithActivation;
+    set<int64_t> replacedMetrics;
+    EXPECT_TRUE(updateMetrics(
+            key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+            oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, /*replacedMatchers=*/{},
+            newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
+            newConditionTrackers, conditionCache, stateAtomIdMap, allStateGroupMaps, replacedStates,
+            oldMetricProducerMap, oldMetricProducers, newMetricProducerMap, newMetricProducers,
+            conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+            metricsWithActivation, replacedMetrics));
+
+    unordered_map<int64_t, int> expectedMetricProducerMap = {
+            {value1Id, value1Index}, {value2Id, value2Index}, {value3Id, value3Index},
+            {value4Id, value4Index}, {value6Id, value6Index},
+    };
+    EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+    EXPECT_EQ(replacedMetrics, set<int64_t>({value2Id, value3Id, value4Id}));
+
+    // Make sure preserved metrics are the same.
+    ASSERT_EQ(newMetricProducers.size(), 5);
+    EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(value1Id)],
+              newMetricProducers[newMetricProducerMap.at(value1Id)]);
+
+    // Make sure replaced metrics are different.
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(value2Id)],
+              newMetricProducers[newMetricProducerMap.at(value2Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(value3Id)],
+              newMetricProducers[newMetricProducerMap.at(value3Id)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(value4Id)],
+              newMetricProducers[newMetricProducerMap.at(value4Id)]);
+
+    // Verify the conditionToMetricMap.
+    ASSERT_EQ(conditionToMetricMap.size(), 2);
+    const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
+    EXPECT_THAT(condition1Metrics, UnorderedElementsAre(value1Index, value6Index));
+    const vector<int>& condition2Metrics = conditionToMetricMap[predicate2Index];
+    EXPECT_THAT(condition2Metrics, UnorderedElementsAre(value3Index));
+
+    // Verify the trackerToMetricMap.
+    ASSERT_EQ(trackerToMetricMap.size(), 4);
+    const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+    EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(value2Index));
+    const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+    EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(value4Index));
+    const vector<int>& matcher4Metrics = trackerToMetricMap[matcher4Index];
+    EXPECT_THAT(matcher4Metrics, UnorderedElementsAre(value1Index));
+    const vector<int>& matcher5Metrics = trackerToMetricMap[matcher5Index];
+    EXPECT_THAT(matcher5Metrics, UnorderedElementsAre(value3Index, value6Index));
+
+    // Verify event activation/deactivation maps.
+    ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(metricsWithActivation.size(), 0);
+
+    // Verify tracker indices/ids/conditions/states are correct.
+    ValueMetricProducer* valueProducer1 =
+            static_cast<ValueMetricProducer*>(newMetricProducers[value1Index].get());
+    EXPECT_EQ(valueProducer1->getMetricId(), value1Id);
+    EXPECT_EQ(valueProducer1->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(valueProducer1->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(valueProducer1->mWhatMatcherIndex, matcher4Index);
+    ValueMetricProducer* valueProducer2 =
+            static_cast<ValueMetricProducer*>(newMetricProducers[value2Index].get());
+    EXPECT_EQ(valueProducer2->getMetricId(), value2Id);
+    EXPECT_EQ(valueProducer2->mConditionTrackerIndex, -1);
+    EXPECT_EQ(valueProducer2->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(valueProducer2->mWhatMatcherIndex, matcher1Index);
+    ValueMetricProducer* valueProducer3 =
+            static_cast<ValueMetricProducer*>(newMetricProducers[value3Index].get());
+    EXPECT_EQ(valueProducer3->getMetricId(), value3Id);
+    EXPECT_EQ(valueProducer3->mConditionTrackerIndex, predicate2Index);
+    EXPECT_EQ(valueProducer3->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(valueProducer3->mWhatMatcherIndex, matcher5Index);
+    ValueMetricProducer* valueProducer4 =
+            static_cast<ValueMetricProducer*>(newMetricProducers[value4Index].get());
+    EXPECT_EQ(valueProducer4->getMetricId(), value4Id);
+    EXPECT_EQ(valueProducer4->mConditionTrackerIndex, -1);
+    EXPECT_EQ(valueProducer4->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(valueProducer4->mWhatMatcherIndex, matcher3Index);
+    ValueMetricProducer* valueProducer6 =
+            static_cast<ValueMetricProducer*>(newMetricProducers[value6Index].get());
+    EXPECT_EQ(valueProducer6->getMetricId(), value6Id);
+    EXPECT_EQ(valueProducer6->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(valueProducer6->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(valueProducer6->mWhatMatcherIndex, matcher5Index);
+
+    sp<EventMatcherWizard> newMatcherWizard = valueProducer1->mEventMatcherWizard;
+    EXPECT_NE(newMatcherWizard, oldMatcherWizard);
+    EXPECT_EQ(newMatcherWizard->getStrongCount(), 6);
+    oldMetricProducers.clear();
+    // Only reference to the old wizard should be the one in the test.
+    EXPECT_EQ(oldMatcherWizard->getStrongCount(), 1);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateMetricActivations) {
+    StatsdConfig config;
+    // Add atom matchers
+    AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+    int64_t matcher2Id = matcher2.id();
+    *config.add_atom_matcher() = matcher2;
+
+    AtomMatcher matcher3 = CreateStartScheduledJobAtomMatcher();
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    AtomMatcher matcher4 = CreateFinishScheduledJobAtomMatcher();
+    int64_t matcher4Id = matcher4.id();
+    *config.add_atom_matcher() = matcher4;
+
+    // Add an event metric with multiple activations.
+    EventMetric event1 = createEventMetric("EVENT1", matcher1Id, nullopt);
+    int64_t event1Id = event1.id();
+    *config.add_event_metric() = event1;
+
+    int64_t matcher2TtlSec = 2, matcher3TtlSec = 3, matcher4TtlSec = 4;
+    MetricActivation metricActivation;
+    metricActivation.set_metric_id(event1Id);
+    EventActivation* activation = metricActivation.add_event_activation();
+    activation->set_atom_matcher_id(matcher2Id);
+    activation->set_ttl_seconds(matcher2TtlSec);
+    activation->set_activation_type(ACTIVATE_IMMEDIATELY);
+    activation->set_deactivation_atom_matcher_id(matcher1Id);
+    activation = metricActivation.add_event_activation();
+    activation->set_atom_matcher_id(matcher3Id);
+    activation->set_ttl_seconds(matcher3TtlSec);
+    activation->set_activation_type(ACTIVATE_ON_BOOT);
+    activation->set_deactivation_atom_matcher_id(matcher1Id);
+    activation = metricActivation.add_event_activation();
+    activation->set_atom_matcher_id(matcher4Id);
+    activation->set_ttl_seconds(matcher4TtlSec);
+    activation->set_activation_type(ACTIVATE_IMMEDIATELY);
+    activation->set_deactivation_atom_matcher_id(matcher2Id);
+    *config.add_metric_activation() = metricActivation;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Activate some of the event activations.
+    ASSERT_EQ(oldMetricProducers[0]->getMetricId(), event1Id);
+    int64_t matcher2StartNs = 12345;
+    oldMetricProducers[0]->activate(oldAtomMatchingTrackerMap[matcher2Id], matcher2StartNs);
+    int64_t matcher3StartNs = 23456;
+    oldMetricProducers[0]->activate(oldAtomMatchingTrackerMap[matcher3Id], matcher3StartNs);
+    EXPECT_TRUE(oldMetricProducers[0]->isActive());
+
+    // Map the matchers and predicates in reverse order to force the indices to change.
+    std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    const int matcher4Index = 0;
+    newAtomMatchingTrackerMap[matcher4Id] = 0;
+    const int matcher3Index = 1;
+    newAtomMatchingTrackerMap[matcher3Id] = 1;
+    const int matcher2Index = 2;
+    newAtomMatchingTrackerMap[matcher2Id] = 2;
+    const int matcher1Index = 3;
+    newAtomMatchingTrackerMap[matcher1Id] = 3;
+    // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(4);
+    std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+                      newAtomMatchingTrackers.begin());
+    set<int64_t> replacedMatchers;
+
+    unordered_map<int64_t, int> newConditionTrackerMap;
+    vector<sp<ConditionTracker>> newConditionTrackers;
+    set<int64_t> replacedConditions;
+    vector<ConditionState> conditionCache;
+    unordered_map<int64_t, int> newMetricProducerMap;
+    vector<sp<MetricProducer>> newMetricProducers;
+    unordered_map<int, vector<int>> conditionToMetricMap;
+    unordered_map<int, vector<int>> trackerToMetricMap;
+    set<int64_t> noReportMetricIds;
+    unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+    vector<int> metricsWithActivation;
+    set<int64_t> replacedMetrics;
+    EXPECT_TRUE(updateMetrics(
+            key, config, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+            oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
+            newAtomMatchingTrackers, newConditionTrackerMap, replacedConditions,
+            newConditionTrackers, conditionCache, /*stateAtomIdMap=*/{}, /*allStateGroupMaps=*/{},
+            /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
+            newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+            metricsWithActivation, replacedMetrics));
+
+    // Verify event activation/deactivation maps.
+    ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 3);
+    EXPECT_THAT(activationAtomTrackerToMetricMap[matcher2Index], UnorderedElementsAre(0));
+    EXPECT_THAT(activationAtomTrackerToMetricMap[matcher3Index], UnorderedElementsAre(0));
+    EXPECT_THAT(activationAtomTrackerToMetricMap[matcher4Index], UnorderedElementsAre(0));
+    ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 2);
+    EXPECT_THAT(deactivationAtomTrackerToMetricMap[matcher1Index], UnorderedElementsAre(0, 0));
+    EXPECT_THAT(deactivationAtomTrackerToMetricMap[matcher2Index], UnorderedElementsAre(0));
+    ASSERT_EQ(metricsWithActivation.size(), 1);
+    EXPECT_THAT(metricsWithActivation, UnorderedElementsAre(0));
+
+    // Verify mEventActivation and mEventDeactivation map of the producer.
+    sp<MetricProducer> producer = newMetricProducers[0];
+    EXPECT_TRUE(producer->isActive());
+    ASSERT_EQ(producer->mEventActivationMap.size(), 3);
+    shared_ptr<Activation> matcher2Activation = producer->mEventActivationMap[matcher2Index];
+    EXPECT_EQ(matcher2Activation->ttl_ns, matcher2TtlSec * NS_PER_SEC);
+    EXPECT_EQ(matcher2Activation->activationType, ACTIVATE_IMMEDIATELY);
+    EXPECT_EQ(matcher2Activation->state, kActive);
+    EXPECT_EQ(matcher2Activation->start_ns, matcher2StartNs);
+    shared_ptr<Activation> matcher3Activation = producer->mEventActivationMap[matcher3Index];
+    EXPECT_EQ(matcher3Activation->ttl_ns, matcher3TtlSec * NS_PER_SEC);
+    EXPECT_EQ(matcher3Activation->activationType, ACTIVATE_ON_BOOT);
+    EXPECT_EQ(matcher3Activation->state, kActiveOnBoot);
+    shared_ptr<Activation> matcher4Activation = producer->mEventActivationMap[matcher4Index];
+    EXPECT_EQ(matcher4Activation->ttl_ns, matcher4TtlSec * NS_PER_SEC);
+    EXPECT_EQ(matcher4Activation->activationType, ACTIVATE_IMMEDIATELY);
+    EXPECT_EQ(matcher4Activation->state, kNotActive);
+
+    ASSERT_EQ(producer->mEventDeactivationMap.size(), 2);
+    EXPECT_THAT(producer->mEventDeactivationMap[matcher1Index],
+                UnorderedElementsAre(matcher2Activation, matcher3Activation));
+    EXPECT_THAT(producer->mEventDeactivationMap[matcher2Index],
+                UnorderedElementsAre(matcher4Activation));
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateMetricsMultipleTypes) {
+    StatsdConfig config;
+    // Add atom matchers/predicates/states. These are mostly needed for initStatsdConfig
+    AtomMatcher matcher1 = CreateScreenTurnedOnAtomMatcher();
+    int64_t matcher1Id = matcher1.id();
+    *config.add_atom_matcher() = matcher1;
+
+    AtomMatcher matcher2 = CreateScreenTurnedOffAtomMatcher();
+    int64_t matcher2Id = matcher2.id();
+    *config.add_atom_matcher() = matcher2;
+
+    AtomMatcher matcher3 = CreateTemperatureAtomMatcher();
+    int64_t matcher3Id = matcher3.id();
+    *config.add_atom_matcher() = matcher3;
+
+    Predicate predicate1 = CreateScreenIsOnPredicate();
+    int64_t predicate1Id = predicate1.id();
+    *config.add_predicate() = predicate1;
+
+    // Add a few count metrics.
+    // Will be preserved.
+    CountMetric countMetric = createCountMetric("COUNT1", matcher1Id, predicate1Id, {});
+    int64_t countMetricId = countMetric.id();
+    *config.add_count_metric() = countMetric;
+
+    // Will be replaced since matcher2 is replaced.
+    EventMetric eventMetric = createEventMetric("EVENT1", matcher2Id, nullopt);
+    int64_t eventMetricId = eventMetric.id();
+    *config.add_event_metric() = eventMetric;
+
+    // Will be replaced because the definition changes - a predicate is added.
+    GaugeMetric gaugeMetric = createGaugeMetric("GAUGE1", matcher3Id,
+                                                GaugeMetric::RANDOM_ONE_SAMPLE, nullopt, nullopt);
+    int64_t gaugeMetricId = gaugeMetric.id();
+    *config.add_gauge_metric() = gaugeMetric;
+
+    // Preserved.
+    ValueMetric valueMetric = createValueMetric("VALUE1", matcher3, predicate1Id, {});
+    int64_t valueMetricId = valueMetric.id();
+    *config.add_value_metric() = valueMetric;
+
+    // Preserved.
+    DurationMetric durationMetric = createDurationMetric("DURATION1", predicate1Id, nullopt, {});
+    int64_t durationMetricId = durationMetric.id();
+    *config.add_duration_metric() = durationMetric;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Used later to ensure the condition wizard is replaced. Get it before doing the update.
+    sp<ConditionWizard> oldConditionWizard = oldMetricProducers[0]->mWizard;
+    EXPECT_EQ(oldConditionWizard->getStrongCount(), 6);
+
+    // Mark matcher 2 as replaced. Causes eventMetric to be replaced.
+    set<int64_t> replacedMatchers;
+    replacedMatchers.insert(matcher2Id);
+
+    // Add predicate1 as a predicate on gaugeMetric, causing it to be replaced.
+    gaugeMetric.set_condition(predicate1Id);
+
+    // Map the matchers and predicates in reverse order to force the indices to change.
+    std::unordered_map<int64_t, int> newAtomMatchingTrackerMap;
+    const int matcher3Index = 0;
+    newAtomMatchingTrackerMap[matcher3Id] = 0;
+    const int matcher2Index = 1;
+    newAtomMatchingTrackerMap[matcher2Id] = 1;
+    const int matcher1Index = 2;
+    newAtomMatchingTrackerMap[matcher1Id] = 2;
+    // Use the existing matchers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<AtomMatchingTracker>> newAtomMatchingTrackers(3);
+    std::reverse_copy(oldAtomMatchingTrackers.begin(), oldAtomMatchingTrackers.end(),
+                      newAtomMatchingTrackers.begin());
+
+    std::unordered_map<int64_t, int> newConditionTrackerMap;
+    const int predicate1Index = 0;
+    newConditionTrackerMap[predicate1Id] = 0;
+    // Use the existing conditionTrackers. A bit hacky, but saves code and we don't rely on them.
+    vector<sp<ConditionTracker>> newConditionTrackers(1);
+    std::reverse_copy(oldConditionTrackers.begin(), oldConditionTrackers.end(),
+                      newConditionTrackers.begin());
+    vector<ConditionState> conditionCache = {ConditionState::kUnknown};
+
+    // The order matters. we parse in the order of: count, duration, event, value, gauge.
+    StatsdConfig newConfig;
+    *newConfig.add_count_metric() = countMetric;
+    const int countMetricIndex = 0;
+    *newConfig.add_duration_metric() = durationMetric;
+    const int durationMetricIndex = 1;
+    *newConfig.add_event_metric() = eventMetric;
+    const int eventMetricIndex = 2;
+    *newConfig.add_value_metric() = valueMetric;
+    const int valueMetricIndex = 3;
+    *newConfig.add_gauge_metric() = gaugeMetric;
+    const int gaugeMetricIndex = 4;
+
+    // Add the predicate since duration metric needs it.
+    *newConfig.add_predicate() = predicate1;
+
+    // Output data structures to validate.
+    unordered_map<int64_t, int> newMetricProducerMap;
+    vector<sp<MetricProducer>> newMetricProducers;
+    unordered_map<int, vector<int>> conditionToMetricMap;
+    unordered_map<int, vector<int>> trackerToMetricMap;
+    set<int64_t> noReportMetricIds;
+    unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+    vector<int> metricsWithActivation;
+    set<int64_t> replacedMetrics;
+    EXPECT_TRUE(updateMetrics(
+            key, newConfig, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+            oldAtomMatchingTrackerMap, newAtomMatchingTrackerMap, replacedMatchers,
+            newAtomMatchingTrackers, newConditionTrackerMap, /*replacedConditions=*/{},
+            newConditionTrackers, conditionCache, /*stateAtomIdMap*/ {}, /*allStateGroupMaps=*/{},
+            /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
+            newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+            metricsWithActivation, replacedMetrics));
+
+    unordered_map<int64_t, int> expectedMetricProducerMap = {
+            {countMetricId, countMetricIndex}, {durationMetricId, durationMetricIndex},
+            {eventMetricId, eventMetricIndex}, {valueMetricId, valueMetricIndex},
+            {gaugeMetricId, gaugeMetricIndex},
+    };
+    EXPECT_THAT(newMetricProducerMap, ContainerEq(expectedMetricProducerMap));
+
+    EXPECT_EQ(replacedMetrics, set<int64_t>({eventMetricId, gaugeMetricId}));
+
+    // Make sure preserved metrics are the same.
+    ASSERT_EQ(newMetricProducers.size(), 5);
+    EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(countMetricId)],
+              newMetricProducers[newMetricProducerMap.at(countMetricId)]);
+    EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(durationMetricId)],
+              newMetricProducers[newMetricProducerMap.at(durationMetricId)]);
+    EXPECT_EQ(oldMetricProducers[oldMetricProducerMap.at(valueMetricId)],
+              newMetricProducers[newMetricProducerMap.at(valueMetricId)]);
+
+    // Make sure replaced metrics are different.
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(eventMetricId)],
+              newMetricProducers[newMetricProducerMap.at(eventMetricId)]);
+    EXPECT_NE(oldMetricProducers[oldMetricProducerMap.at(gaugeMetricId)],
+              newMetricProducers[newMetricProducerMap.at(gaugeMetricId)]);
+
+    // Verify the conditionToMetricMap.
+    ASSERT_EQ(conditionToMetricMap.size(), 1);
+    const vector<int>& condition1Metrics = conditionToMetricMap[predicate1Index];
+    EXPECT_THAT(condition1Metrics,
+                UnorderedElementsAre(countMetricIndex, gaugeMetricIndex, valueMetricIndex));
+
+    // Verify the trackerToMetricMap.
+    ASSERT_EQ(trackerToMetricMap.size(), 3);
+    const vector<int>& matcher1Metrics = trackerToMetricMap[matcher1Index];
+    EXPECT_THAT(matcher1Metrics, UnorderedElementsAre(countMetricIndex, durationMetricIndex));
+    const vector<int>& matcher2Metrics = trackerToMetricMap[matcher2Index];
+    EXPECT_THAT(matcher2Metrics, UnorderedElementsAre(eventMetricIndex, durationMetricIndex));
+    const vector<int>& matcher3Metrics = trackerToMetricMap[matcher3Index];
+    EXPECT_THAT(matcher3Metrics, UnorderedElementsAre(gaugeMetricIndex, valueMetricIndex));
+
+    // Verify event activation/deactivation maps.
+    ASSERT_EQ(activationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(deactivationAtomTrackerToMetricMap.size(), 0);
+    ASSERT_EQ(metricsWithActivation.size(), 0);
+
+    // Verify tracker indices/ids/conditions are correct.
+    EXPECT_EQ(newMetricProducers[countMetricIndex]->getMetricId(), countMetricId);
+    EXPECT_EQ(newMetricProducers[countMetricIndex]->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(newMetricProducers[countMetricIndex]->mCondition, ConditionState::kUnknown);
+    EXPECT_EQ(newMetricProducers[durationMetricIndex]->getMetricId(), durationMetricId);
+    EXPECT_EQ(newMetricProducers[durationMetricIndex]->mConditionTrackerIndex, -1);
+    EXPECT_EQ(newMetricProducers[durationMetricIndex]->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(newMetricProducers[eventMetricIndex]->getMetricId(), eventMetricId);
+    EXPECT_EQ(newMetricProducers[eventMetricIndex]->mConditionTrackerIndex, -1);
+    EXPECT_EQ(newMetricProducers[eventMetricIndex]->mCondition, ConditionState::kTrue);
+    EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->getMetricId(), gaugeMetricId);
+    EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->mConditionTrackerIndex, predicate1Index);
+    EXPECT_EQ(newMetricProducers[gaugeMetricIndex]->mCondition, ConditionState::kUnknown);
+
+    sp<ConditionWizard> newConditionWizard = newMetricProducers[0]->mWizard;
+    EXPECT_NE(newConditionWizard, oldConditionWizard);
+    EXPECT_EQ(newConditionWizard->getStrongCount(), 6);
+    oldMetricProducers.clear();
+    // Only reference to the old wizard should be the one in the test.
+    EXPECT_EQ(oldConditionWizard->getStrongCount(), 1);
+}
+
+TEST_F(ConfigUpdateTest, TestAlertPreserve) {
+    StatsdConfig config;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    *config.add_count_metric() = createCountMetric("VALUE1", whatMatcher.id(), nullopt, {});
+
+    Alert alert = createAlert("Alert1", config.count_metric(0).id(), 1, 1);
+    *config.add_alert() = alert;
+    EXPECT_TRUE(initConfig(config));
+
+    UpdateStatus updateStatus = UPDATE_UNKNOWN;
+    EXPECT_TRUE(determineAlertUpdateStatus(alert, oldAlertTrackerMap, oldAnomalyTrackers,
+                                           /*replacedMetrics*/ {}, updateStatus));
+    EXPECT_EQ(updateStatus, UPDATE_PRESERVE);
+}
+
+TEST_F(ConfigUpdateTest, TestAlertMetricChanged) {
+    StatsdConfig config;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    CountMetric metric = createCountMetric("VALUE1", whatMatcher.id(), nullopt, {});
+    *config.add_count_metric() = metric;
+
+    Alert alert = createAlert("Alert1", config.count_metric(0).id(), 1, 1);
+    *config.add_alert() = alert;
+    EXPECT_TRUE(initConfig(config));
+
+    UpdateStatus updateStatus = UPDATE_UNKNOWN;
+    EXPECT_TRUE(determineAlertUpdateStatus(alert, oldAlertTrackerMap, oldAnomalyTrackers,
+                                           /*replacedMetrics*/ {metric.id()}, updateStatus));
+    EXPECT_EQ(updateStatus, UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestAlertDefinitionChanged) {
+    StatsdConfig config;
+    AtomMatcher whatMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = whatMatcher;
+
+    *config.add_count_metric() = createCountMetric("VALUE1", whatMatcher.id(), nullopt, {});
+
+    Alert alert = createAlert("Alert1", config.count_metric(0).id(), 1, 1);
+    *config.add_alert() = alert;
+    EXPECT_TRUE(initConfig(config));
+
+    alert.set_num_buckets(2);
+
+    UpdateStatus updateStatus = UPDATE_UNKNOWN;
+    EXPECT_TRUE(determineAlertUpdateStatus(alert, oldAlertTrackerMap, oldAnomalyTrackers,
+                                           /*replacedMetrics*/ {}, updateStatus));
+    EXPECT_EQ(updateStatus, UPDATE_REPLACE);
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateAlerts) {
+    StatsdConfig config;
+    // Add atom matchers/predicates/metrics. These are mostly needed for initStatsdConfig
+    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+    *config.add_predicate() = CreateScreenIsOnPredicate();
+
+    CountMetric countMetric = createCountMetric("COUNT1", config.atom_matcher(0).id(), nullopt, {});
+    int64_t countMetricId = countMetric.id();
+    *config.add_count_metric() = countMetric;
+
+    DurationMetric durationMetric =
+            createDurationMetric("DURATION1", config.predicate(0).id(), nullopt, {});
+    int64_t durationMetricId = durationMetric.id();
+    *config.add_duration_metric() = durationMetric;
+
+    // Add alerts.
+    // Preserved.
+    Alert alert1 = createAlert("Alert1", durationMetricId, /*buckets*/ 1, /*triggerSum*/ 5000);
+    int64_t alert1Id = alert1.id();
+    *config.add_alert() = alert1;
+
+    // Replaced.
+    Alert alert2 = createAlert("Alert2", countMetricId, /*buckets*/ 1, /*triggerSum*/ 2);
+    int64_t alert2Id = alert2.id();
+    *config.add_alert() = alert2;
+
+    // Replaced.
+    Alert alert3 = createAlert("Alert3", durationMetricId, /*buckets*/ 3, /*triggerSum*/ 5000);
+    int64_t alert3Id = alert3.id();
+    *config.add_alert() = alert3;
+
+    // Add Subscriptions.
+    Subscription subscription1 = createSubscription("S1", Subscription::ALERT, alert1Id);
+    *config.add_subscription() = subscription1;
+    Subscription subscription2 = createSubscription("S2", Subscription::ALERT, alert1Id);
+    *config.add_subscription() = subscription2;
+    Subscription subscription3 = createSubscription("S3", Subscription::ALERT, alert2Id);
+    *config.add_subscription() = subscription3;
+
+    EXPECT_TRUE(initConfig(config));
+
+    // Add a duration tracker to the duration metric to ensure durationTrackers are updated
+    // with the proper anomalyTrackers.
+    unique_ptr<LogEvent> event = CreateScreenStateChangedEvent(
+            timeBaseNs + 1, android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+    oldMetricProducers[1]->onMatchedLogEvent(0, *event.get());
+
+    // Change the count metric. Causes alert2 to be replaced.
+    config.mutable_count_metric(0)->set_bucket(ONE_DAY);
+    // Change num buckets on alert3, causing replacement.
+    alert3.set_num_buckets(5);
+
+    // New alert.
+    Alert alert4 = createAlert("Alert4", durationMetricId, /*buckets*/ 3, /*triggerSum*/ 10000);
+    int64_t alert4Id = alert4.id();
+
+    // Move subscription2 to be on alert2 and make a new subscription.
+    subscription2.set_rule_id(alert2Id);
+    Subscription subscription4 = createSubscription("S4", Subscription::ALERT, alert2Id);
+
+    // Create the new config. Modify the old one to avoid adding the matchers/predicates.
+    // Add alerts in different order so the map is changed.
+    config.clear_alert();
+    *config.add_alert() = alert4;
+    const int alert4Index = 0;
+    *config.add_alert() = alert3;
+    const int alert3Index = 1;
+    *config.add_alert() = alert1;
+    const int alert1Index = 2;
+    *config.add_alert() = alert2;
+    const int alert2Index = 3;
+
+    // Subscription3 is removed.
+    config.clear_subscription();
+    *config.add_subscription() = subscription4;
+    *config.add_subscription() = subscription2;
+    *config.add_subscription() = subscription1;
+
+    // Output data structures from update metrics. Don't care about the outputs besides
+    // replacedMetrics, but need to do this so that the metrics clear their anomaly trackers.
+    unordered_map<int64_t, int> newMetricProducerMap;
+    vector<sp<MetricProducer>> newMetricProducers;
+    unordered_map<int, vector<int>> conditionToMetricMap;
+    unordered_map<int, vector<int>> trackerToMetricMap;
+    set<int64_t> noReportMetricIds;
+    unordered_map<int, vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, vector<int>> deactivationAtomTrackerToMetricMap;
+    vector<int> metricsWithActivation;
+    set<int64_t> replacedMetrics;
+    EXPECT_TRUE(updateMetrics(
+            key, config, /*timeBaseNs=*/123, /*currentTimeNs=*/12345, new StatsPullerManager(),
+            oldAtomMatchingTrackerMap, oldAtomMatchingTrackerMap, /*replacedMatchers*/ {},
+            oldAtomMatchingTrackers, oldConditionTrackerMap, /*replacedConditions=*/{},
+            oldConditionTrackers, {ConditionState::kUnknown}, /*stateAtomIdMap*/ {},
+            /*allStateGroupMaps=*/{},
+            /*replacedStates=*/{}, oldMetricProducerMap, oldMetricProducers, newMetricProducerMap,
+            newMetricProducers, conditionToMetricMap, trackerToMetricMap, noReportMetricIds,
+            activationAtomTrackerToMetricMap, deactivationAtomTrackerToMetricMap,
+            metricsWithActivation, replacedMetrics));
+
+    EXPECT_EQ(replacedMetrics, set<int64_t>({countMetricId}));
+
+    unordered_map<int64_t, int> newAlertTrackerMap;
+    vector<sp<AnomalyTracker>> newAnomalyTrackers;
+    EXPECT_TRUE(updateAlerts(config, newMetricProducerMap, replacedMetrics, oldAlertTrackerMap,
+                             oldAnomalyTrackers, anomalyAlarmMonitor, newMetricProducers,
+                             newAlertTrackerMap, newAnomalyTrackers));
+
+    unordered_map<int64_t, int> expectedAlertMap = {
+            {alert1Id, alert1Index},
+            {alert2Id, alert2Index},
+            {alert3Id, alert3Index},
+            {alert4Id, alert4Index},
+    };
+    EXPECT_THAT(newAlertTrackerMap, ContainerEq(expectedAlertMap));
+
+    // Make sure preserved alerts are the same.
+    ASSERT_EQ(newAnomalyTrackers.size(), 4);
+    EXPECT_EQ(oldAnomalyTrackers[oldAlertTrackerMap.at(alert1Id)],
+              newAnomalyTrackers[newAlertTrackerMap.at(alert1Id)]);
+
+    // Make sure replaced alerts are different.
+    EXPECT_NE(oldAnomalyTrackers[oldAlertTrackerMap.at(alert2Id)],
+              newAnomalyTrackers[newAlertTrackerMap.at(alert2Id)]);
+    EXPECT_NE(oldAnomalyTrackers[oldAlertTrackerMap.at(alert3Id)],
+              newAnomalyTrackers[newAlertTrackerMap.at(alert3Id)]);
+
+    // Verify the alerts have the correct anomaly trackers.
+    ASSERT_EQ(newMetricProducers.size(), 2);
+    EXPECT_THAT(newMetricProducers[0]->mAnomalyTrackers,
+                UnorderedElementsAre(newAnomalyTrackers[alert2Index]));
+    // For durationMetric, make sure the duration trackers get the updated anomalyTrackers.
+    DurationMetricProducer* durationProducer =
+            static_cast<DurationMetricProducer*>(newMetricProducers[1].get());
+    EXPECT_THAT(
+            durationProducer->mAnomalyTrackers,
+            UnorderedElementsAre(newAnomalyTrackers[alert1Index], newAnomalyTrackers[alert3Index],
+                                 newAnomalyTrackers[alert4Index]));
+    ASSERT_EQ(durationProducer->mCurrentSlicedDurationTrackerMap.size(), 1);
+    for (const auto& durationTrackerIt : durationProducer->mCurrentSlicedDurationTrackerMap) {
+        EXPECT_EQ(durationTrackerIt.second->mAnomalyTrackers, durationProducer->mAnomalyTrackers);
+    }
+
+    // Verify alerts have the correct subscriptions. Use subscription id as proxy for equivalency.
+    vector<int64_t> alert1Subscriptions;
+    for (const Subscription& subscription : newAnomalyTrackers[alert1Index]->mSubscriptions) {
+        alert1Subscriptions.push_back(subscription.id());
+    }
+    EXPECT_THAT(alert1Subscriptions, UnorderedElementsAre(subscription1.id()));
+    vector<int64_t> alert2Subscriptions;
+    for (const Subscription& subscription : newAnomalyTrackers[alert2Index]->mSubscriptions) {
+        alert2Subscriptions.push_back(subscription.id());
+    }
+    EXPECT_THAT(alert2Subscriptions, UnorderedElementsAre(subscription2.id(), subscription4.id()));
+    EXPECT_THAT(newAnomalyTrackers[alert3Index]->mSubscriptions, IsEmpty());
+    EXPECT_THAT(newAnomalyTrackers[alert4Index]->mSubscriptions, IsEmpty());
+}
+
+TEST_F(ConfigUpdateTest, TestUpdateAlarms) {
+    StatsdConfig config;
+    // Add alarms.
+    Alarm alarm1 = createAlarm("Alarm1", /*offset*/ 1 * MS_PER_SEC, /*period*/ 50 * MS_PER_SEC);
+    int64_t alarm1Id = alarm1.id();
+    *config.add_alarm() = alarm1;
+
+    Alarm alarm2 = createAlarm("Alarm2", /*offset*/ 1 * MS_PER_SEC, /*period*/ 2000 * MS_PER_SEC);
+    int64_t alarm2Id = alarm2.id();
+    *config.add_alarm() = alarm2;
+
+    Alarm alarm3 = createAlarm("Alarm3", /*offset*/ 10 * MS_PER_SEC, /*period*/ 5000 * MS_PER_SEC);
+    int64_t alarm3Id = alarm3.id();
+    *config.add_alarm() = alarm3;
+
+    // Add Subscriptions.
+    Subscription subscription1 = createSubscription("S1", Subscription::ALARM, alarm1Id);
+    *config.add_subscription() = subscription1;
+    Subscription subscription2 = createSubscription("S2", Subscription::ALARM, alarm1Id);
+    *config.add_subscription() = subscription2;
+    Subscription subscription3 = createSubscription("S3", Subscription::ALARM, alarm2Id);
+    *config.add_subscription() = subscription3;
+
+    EXPECT_TRUE(initConfig(config));
+
+    ASSERT_EQ(oldAlarmTrackers.size(), 3);
+    // Config is created at statsd start time, so just add the offsets.
+    EXPECT_EQ(oldAlarmTrackers[0]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 1);
+    EXPECT_EQ(oldAlarmTrackers[1]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 1);
+    EXPECT_EQ(oldAlarmTrackers[2]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 10);
+
+    // Change alarm2/alarm3.
+    config.mutable_alarm(1)->set_offset_millis(5 * MS_PER_SEC);
+    config.mutable_alarm(2)->set_period_millis(10000 * MS_PER_SEC);
+
+    // Move subscription2 to be on alarm2 and make a new subscription.
+    config.mutable_subscription(1)->set_rule_id(alarm2Id);
+    Subscription subscription4 = createSubscription("S4", Subscription::ALARM, alarm1Id);
+    *config.add_subscription() = subscription4;
+
+    // Update time is 2 seconds after the base time.
+    int64_t currentTimeNs = timeBaseNs + 2 * NS_PER_SEC;
+    vector<sp<AlarmTracker>> newAlarmTrackers;
+    EXPECT_TRUE(initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs,
+                           newAlarmTrackers));
+
+    ASSERT_EQ(newAlarmTrackers.size(), 3);
+    // Config is updated 2 seconds after statsd start
+    // The offset has passed for alarm1, but not for alarms 2/3.
+    EXPECT_EQ(newAlarmTrackers[0]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 1 + 50);
+    EXPECT_EQ(newAlarmTrackers[1]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 5);
+    EXPECT_EQ(newAlarmTrackers[2]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 10);
+
+    // Verify alarms have the correct subscriptions. Use subscription id as proxy for equivalency.
+    vector<int64_t> alarm1Subscriptions;
+    for (const Subscription& subscription : newAlarmTrackers[0]->mSubscriptions) {
+        alarm1Subscriptions.push_back(subscription.id());
+    }
+    EXPECT_THAT(alarm1Subscriptions, UnorderedElementsAre(subscription1.id(), subscription4.id()));
+    vector<int64_t> alarm2Subscriptions;
+    for (const Subscription& subscription : newAlarmTrackers[1]->mSubscriptions) {
+        alarm2Subscriptions.push_back(subscription.id());
+    }
+    EXPECT_THAT(alarm2Subscriptions, UnorderedElementsAre(subscription2.id(), subscription3.id()));
+    EXPECT_THAT(newAlarmTrackers[2]->mSubscriptions, IsEmpty());
+
+    // Verify the alarm monitor is updated accordingly once the old alarms are removed.
+    // Alarm2 fires the earliest.
+    oldAlarmTrackers.clear();
+    EXPECT_EQ(periodicAlarmMonitor->getRegisteredAlarmTimeSec(), timeBaseNs / NS_PER_SEC + 5);
+
+    // Do another update 60 seconds after config creation time, after the offsets of each alarm.
+    currentTimeNs = timeBaseNs + 60 * NS_PER_SEC;
+    newAlarmTrackers.clear();
+    EXPECT_TRUE(initAlarms(config, key, periodicAlarmMonitor, timeBaseNs, currentTimeNs,
+                           newAlarmTrackers));
+
+    ASSERT_EQ(newAlarmTrackers.size(), 3);
+    // Config is updated one minute after statsd start.
+    // Two periods have passed for alarm 1, one has passed for alarms2/3.
+    EXPECT_EQ(newAlarmTrackers[0]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 1 + 2 * 50);
+    EXPECT_EQ(newAlarmTrackers[1]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 5 + 2000);
+    EXPECT_EQ(newAlarmTrackers[2]->getAlarmTimestampSec(), timeBaseNs / NS_PER_SEC + 10 + 10000);
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/bin/tests/metrics/parsing_utils/metrics_manager_util_test.cpp b/bin/tests/metrics/parsing_utils/metrics_manager_util_test.cpp
new file mode 100644
index 0000000..00690b5
--- /dev/null
+++ b/bin/tests/metrics/parsing_utils/metrics_manager_util_test.cpp
@@ -0,0 +1,928 @@
+// Copyright (C) 2020 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/metrics/parsing_utils/metrics_manager_util.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <private/android_filesystem_config.h>
+#include <stdio.h>
+
+#include <set>
+#include <unordered_map>
+#include <vector>
+
+#include "packages/modules/StatsD/bin/src/statsd_config.pb.h"
+#include "src/condition/ConditionTracker.h"
+#include "src/matchers/AtomMatchingTracker.h"
+#include "src/metrics/CountMetricProducer.h"
+#include "src/metrics/DurationMetricProducer.h"
+#include "src/metrics/GaugeMetricProducer.h"
+#include "src/metrics/MetricProducer.h"
+#include "src/metrics/ValueMetricProducer.h"
+#include "src/state/StateManager.h"
+#include "tests/metrics/metrics_test_helper.h"
+#include "tests/statsd_test_util.h"
+
+using namespace testing;
+using android::sp;
+using android::os::statsd::Predicate;
+using std::map;
+using std::set;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+namespace {
+const ConfigKey kConfigKey(0, 12345);
+const long kAlertId = 3;
+
+const long timeBaseSec = 1000;
+
+StatsdConfig buildGoodConfig() {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
+
+    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_field_value_matcher()->set_field(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+            2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+    eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_IS_OFF"));
+
+    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_field_value_matcher()->set_field(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
+
+    eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
+
+    AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_matcher(StringToId("SCREEN_IS_ON"));
+    combination->add_matcher(StringToId("SCREEN_IS_OFF"));
+
+    CountMetric* metric = config.add_count_metric();
+    metric->set_id(3);
+    metric->set_what(StringToId("SCREEN_IS_ON"));
+    metric->set_bucket(ONE_MINUTE);
+    metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/);
+    metric->mutable_dimensions_in_what()->add_child()->set_field(1);
+
+    config.add_no_report_metric(3);
+
+    auto alert = config.add_alert();
+    alert->set_id(kAlertId);
+    alert->set_metric_id(3);
+    alert->set_num_buckets(10);
+    alert->set_refractory_period_secs(100);
+    alert->set_trigger_if_sum_gt(100);
+    return config;
+}
+
+StatsdConfig buildCircleMatchers() {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
+
+    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_field_value_matcher()->set_field(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+            2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+    eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
+
+    AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_matcher(StringToId("SCREEN_IS_ON"));
+    // Circle dependency
+    combination->add_matcher(StringToId("SCREEN_ON_OR_OFF"));
+
+    return config;
+}
+
+StatsdConfig buildAlertWithUnknownMetric() {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
+
+    CountMetric* metric = config.add_count_metric();
+    metric->set_id(3);
+    metric->set_what(StringToId("SCREEN_IS_ON"));
+    metric->set_bucket(ONE_MINUTE);
+    metric->mutable_dimensions_in_what()->set_field(2 /*SCREEN_STATE_CHANGE*/);
+    metric->mutable_dimensions_in_what()->add_child()->set_field(1);
+
+    auto alert = config.add_alert();
+    alert->set_id(3);
+    alert->set_metric_id(2);
+    alert->set_num_buckets(10);
+    alert->set_refractory_period_secs(100);
+    alert->set_trigger_if_sum_gt(100);
+    return config;
+}
+
+StatsdConfig buildMissingMatchers() {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
+
+    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_field_value_matcher()->set_field(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+            2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+    eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_ON_OR_OFF"));
+
+    AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_matcher(StringToId("SCREEN_IS_ON"));
+    // undefined matcher
+    combination->add_matcher(StringToId("ABC"));
+
+    return config;
+}
+
+StatsdConfig buildMissingPredicate() {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    CountMetric* metric = config.add_count_metric();
+    metric->set_id(3);
+    metric->set_what(StringToId("SCREEN_EVENT"));
+    metric->set_bucket(ONE_MINUTE);
+    metric->set_condition(StringToId("SOME_CONDITION"));
+
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_EVENT"));
+
+    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_atom_id(2);
+
+    return config;
+}
+
+StatsdConfig buildDimensionMetricsWithMultiTags() {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("BATTERY_VERY_LOW"));
+    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_atom_id(2);
+
+    eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("BATTERY_VERY_VERY_LOW"));
+    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_atom_id(3);
+
+    eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("BATTERY_LOW"));
+
+    AtomMatcher_Combination* combination = eventMatcher->mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_matcher(StringToId("BATTERY_VERY_LOW"));
+    combination->add_matcher(StringToId("BATTERY_VERY_VERY_LOW"));
+
+    // Count process state changes, slice by uid, while SCREEN_IS_OFF
+    CountMetric* metric = config.add_count_metric();
+    metric->set_id(3);
+    metric->set_what(StringToId("BATTERY_LOW"));
+    metric->set_bucket(ONE_MINUTE);
+    // This case is interesting. We want to dimension across two atoms.
+    metric->mutable_dimensions_in_what()->add_child()->set_field(1);
+
+    auto alert = config.add_alert();
+    alert->set_id(kAlertId);
+    alert->set_metric_id(3);
+    alert->set_num_buckets(10);
+    alert->set_refractory_period_secs(100);
+    alert->set_trigger_if_sum_gt(100);
+    return config;
+}
+
+StatsdConfig buildCirclePredicates() {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_IS_ON"));
+
+    SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_field_value_matcher()->set_field(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+            2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
+
+    eventMatcher = config.add_atom_matcher();
+    eventMatcher->set_id(StringToId("SCREEN_IS_OFF"));
+
+    simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_field_value_matcher()->set_field(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
+
+    auto condition = config.add_predicate();
+    condition->set_id(StringToId("SCREEN_IS_ON"));
+    SimplePredicate* simplePredicate = condition->mutable_simple_predicate();
+    simplePredicate->set_start(StringToId("SCREEN_IS_ON"));
+    simplePredicate->set_stop(StringToId("SCREEN_IS_OFF"));
+
+    condition = config.add_predicate();
+    condition->set_id(StringToId("SCREEN_IS_EITHER_ON_OFF"));
+
+    Predicate_Combination* combination = condition->mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_predicate(StringToId("SCREEN_IS_ON"));
+    combination->add_predicate(StringToId("SCREEN_IS_EITHER_ON_OFF"));
+
+    return config;
+}
+
+StatsdConfig buildConfigWithDifferentPredicates() {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    auto pulledAtomMatcher =
+            CreateSimpleAtomMatcher("SUBSYSTEM_SLEEP", util::SUBSYSTEM_SLEEP_STATE);
+    *config.add_atom_matcher() = pulledAtomMatcher;
+    auto screenOnAtomMatcher = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = screenOnAtomMatcher;
+    auto screenOffAtomMatcher = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = screenOffAtomMatcher;
+    auto batteryNoneAtomMatcher = CreateBatteryStateNoneMatcher();
+    *config.add_atom_matcher() = batteryNoneAtomMatcher;
+    auto batteryUsbAtomMatcher = CreateBatteryStateUsbMatcher();
+    *config.add_atom_matcher() = batteryUsbAtomMatcher;
+
+    // Simple condition with InitialValue set to default (unknown).
+    auto screenOnUnknownPredicate = CreateScreenIsOnPredicate();
+    *config.add_predicate() = screenOnUnknownPredicate;
+
+    // Simple condition with InitialValue set to false.
+    auto screenOnFalsePredicate = config.add_predicate();
+    screenOnFalsePredicate->set_id(StringToId("ScreenIsOnInitialFalse"));
+    SimplePredicate* simpleScreenOnFalsePredicate =
+            screenOnFalsePredicate->mutable_simple_predicate();
+    simpleScreenOnFalsePredicate->set_start(screenOnAtomMatcher.id());
+    simpleScreenOnFalsePredicate->set_stop(screenOffAtomMatcher.id());
+    simpleScreenOnFalsePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE);
+
+    // Simple condition with InitialValue set to false.
+    auto onBatteryFalsePredicate = config.add_predicate();
+    onBatteryFalsePredicate->set_id(StringToId("OnBatteryInitialFalse"));
+    SimplePredicate* simpleOnBatteryFalsePredicate =
+            onBatteryFalsePredicate->mutable_simple_predicate();
+    simpleOnBatteryFalsePredicate->set_start(batteryNoneAtomMatcher.id());
+    simpleOnBatteryFalsePredicate->set_stop(batteryUsbAtomMatcher.id());
+    simpleOnBatteryFalsePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE);
+
+    // Combination condition with both simple condition InitialValues set to false.
+    auto screenOnFalseOnBatteryFalsePredicate = config.add_predicate();
+    screenOnFalseOnBatteryFalsePredicate->set_id(StringToId("ScreenOnFalseOnBatteryFalse"));
+    screenOnFalseOnBatteryFalsePredicate->mutable_combination()->set_operation(
+            LogicalOperation::AND);
+    addPredicateToPredicateCombination(*screenOnFalsePredicate,
+                                       screenOnFalseOnBatteryFalsePredicate);
+    addPredicateToPredicateCombination(*onBatteryFalsePredicate,
+                                       screenOnFalseOnBatteryFalsePredicate);
+
+    // Combination condition with one simple condition InitialValue set to unknown and one set to
+    // false.
+    auto screenOnUnknownOnBatteryFalsePredicate = config.add_predicate();
+    screenOnUnknownOnBatteryFalsePredicate->set_id(StringToId("ScreenOnUnknowneOnBatteryFalse"));
+    screenOnUnknownOnBatteryFalsePredicate->mutable_combination()->set_operation(
+            LogicalOperation::AND);
+    addPredicateToPredicateCombination(screenOnUnknownPredicate,
+                                       screenOnUnknownOnBatteryFalsePredicate);
+    addPredicateToPredicateCombination(*onBatteryFalsePredicate,
+                                       screenOnUnknownOnBatteryFalsePredicate);
+
+    // Simple condition metric with initial value false.
+    ValueMetric* metric1 = config.add_value_metric();
+    metric1->set_id(StringToId("ValueSubsystemSleepWhileScreenOnInitialFalse"));
+    metric1->set_what(pulledAtomMatcher.id());
+    *metric1->mutable_value_field() =
+            CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
+    metric1->set_bucket(FIVE_MINUTES);
+    metric1->set_condition(screenOnFalsePredicate->id());
+
+    // Simple condition metric with initial value unknown.
+    ValueMetric* metric2 = config.add_value_metric();
+    metric2->set_id(StringToId("ValueSubsystemSleepWhileScreenOnInitialUnknown"));
+    metric2->set_what(pulledAtomMatcher.id());
+    *metric2->mutable_value_field() =
+            CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
+    metric2->set_bucket(FIVE_MINUTES);
+    metric2->set_condition(screenOnUnknownPredicate.id());
+
+    // Combination condition metric with initial values false and false.
+    ValueMetric* metric3 = config.add_value_metric();
+    metric3->set_id(StringToId("ValueSubsystemSleepWhileScreenOnFalseDeviceUnpluggedFalse"));
+    metric3->set_what(pulledAtomMatcher.id());
+    *metric3->mutable_value_field() =
+            CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
+    metric3->set_bucket(FIVE_MINUTES);
+    metric3->set_condition(screenOnFalseOnBatteryFalsePredicate->id());
+
+    // Combination condition metric with initial values unknown and false.
+    ValueMetric* metric4 = config.add_value_metric();
+    metric4->set_id(StringToId("ValueSubsystemSleepWhileScreenOnUnknownDeviceUnpluggedFalse"));
+    metric4->set_what(pulledAtomMatcher.id());
+    *metric4->mutable_value_field() =
+            CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {4 /* time sleeping field */});
+    metric4->set_bucket(FIVE_MINUTES);
+    metric4->set_condition(screenOnUnknownOnBatteryFalsePredicate->id());
+
+    return config;
+}
+}  // anonymous namespace
+
+TEST(MetricsManagerTest, TestInitialConditions) {
+    sp<UidMap> uidMap = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
+    StatsdConfig config = buildConfigWithDifferentPredicates();
+    set<int> allTagIds;
+    vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
+    vector<sp<MetricProducer>> allMetricProducers;
+    unordered_map<int64_t, int> metricProducerMap;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
+    vector<int> metricsWithActivation;
+    map<int64_t, uint64_t> stateProtoHashes;
+    std::set<int64_t> noReportMetricIds;
+
+    EXPECT_TRUE(initStatsdConfig(
+            kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+            allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+            trackerToConditionMap, activationAtomTrackerToMetricMap,
+            deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+            stateProtoHashes, noReportMetricIds));
+    ASSERT_EQ(4u, allMetricProducers.size());
+    ASSERT_EQ(5u, allConditionTrackers.size());
+
+    ConditionKey queryKey;
+    vector<ConditionState> conditionCache(5, ConditionState::kNotEvaluated);
+
+    allConditionTrackers[3]->isConditionMet(queryKey, allConditionTrackers, false, conditionCache);
+    allConditionTrackers[4]->isConditionMet(queryKey, allConditionTrackers, false, conditionCache);
+    EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]);
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[1]);
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[2]);
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[3]);
+    EXPECT_EQ(ConditionState::kUnknown, conditionCache[4]);
+
+    EXPECT_EQ(ConditionState::kFalse, allMetricProducers[0]->mCondition);
+    EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[1]->mCondition);
+    EXPECT_EQ(ConditionState::kFalse, allMetricProducers[2]->mCondition);
+    EXPECT_EQ(ConditionState::kUnknown, allMetricProducers[3]->mCondition);
+}
+
+TEST(MetricsManagerTest, TestGoodConfig) {
+    sp<UidMap> uidMap = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
+    StatsdConfig config = buildGoodConfig();
+    set<int> allTagIds;
+    vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
+    vector<sp<MetricProducer>> allMetricProducers;
+    unordered_map<int64_t, int> metricProducerMap;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
+    vector<int> metricsWithActivation;
+    map<int64_t, uint64_t> stateProtoHashes;
+    std::set<int64_t> noReportMetricIds;
+
+    EXPECT_TRUE(initStatsdConfig(
+            kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+            allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+            trackerToConditionMap, activationAtomTrackerToMetricMap,
+            deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+            stateProtoHashes, noReportMetricIds));
+    ASSERT_EQ(1u, allMetricProducers.size());
+    EXPECT_THAT(metricProducerMap, UnorderedElementsAre(Pair(config.count_metric(0).id(), 0)));
+    ASSERT_EQ(1u, allAnomalyTrackers.size());
+    ASSERT_EQ(1u, noReportMetricIds.size());
+    ASSERT_EQ(1u, alertTrackerMap.size());
+    EXPECT_NE(alertTrackerMap.find(kAlertId), alertTrackerMap.end());
+    EXPECT_EQ(alertTrackerMap.find(kAlertId)->second, 0);
+}
+
+TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
+    sp<UidMap> uidMap = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
+    StatsdConfig config = buildDimensionMetricsWithMultiTags();
+    set<int> allTagIds;
+    vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
+    vector<sp<MetricProducer>> allMetricProducers;
+    unordered_map<int64_t, int> metricProducerMap;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
+    vector<int> metricsWithActivation;
+    map<int64_t, uint64_t> stateProtoHashes;
+    std::set<int64_t> noReportMetricIds;
+
+    EXPECT_FALSE(initStatsdConfig(
+            kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+            allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+            trackerToConditionMap, activationAtomTrackerToMetricMap,
+            deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+            stateProtoHashes, noReportMetricIds));
+}
+
+TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
+    sp<UidMap> uidMap = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
+    StatsdConfig config = buildCircleMatchers();
+    set<int> allTagIds;
+    vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
+    vector<sp<MetricProducer>> allMetricProducers;
+    unordered_map<int64_t, int> metricProducerMap;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
+    vector<int> metricsWithActivation;
+    map<int64_t, uint64_t> stateProtoHashes;
+    std::set<int64_t> noReportMetricIds;
+
+    EXPECT_FALSE(initStatsdConfig(
+            kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+            allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+            trackerToConditionMap, activationAtomTrackerToMetricMap,
+            deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+            stateProtoHashes, noReportMetricIds));
+}
+
+TEST(MetricsManagerTest, TestMissingMatchers) {
+    sp<UidMap> uidMap = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
+    StatsdConfig config = buildMissingMatchers();
+    set<int> allTagIds;
+    vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
+    vector<sp<MetricProducer>> allMetricProducers;
+    unordered_map<int64_t, int> metricProducerMap;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
+    vector<int> metricsWithActivation;
+    map<int64_t, uint64_t> stateProtoHashes;
+    std::set<int64_t> noReportMetricIds;
+    EXPECT_FALSE(initStatsdConfig(
+            kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+            allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+            trackerToConditionMap, activationAtomTrackerToMetricMap,
+            deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+            stateProtoHashes, noReportMetricIds));
+}
+
+TEST(MetricsManagerTest, TestMissingPredicate) {
+    sp<UidMap> uidMap = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
+    StatsdConfig config = buildMissingPredicate();
+    set<int> allTagIds;
+    vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
+    vector<sp<MetricProducer>> allMetricProducers;
+    unordered_map<int64_t, int> metricProducerMap;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
+    vector<int> metricsWithActivation;
+    map<int64_t, uint64_t> stateProtoHashes;
+    std::set<int64_t> noReportMetricIds;
+    EXPECT_FALSE(initStatsdConfig(
+            kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+            allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+            trackerToConditionMap, activationAtomTrackerToMetricMap,
+            deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+            stateProtoHashes, noReportMetricIds));
+}
+
+TEST(MetricsManagerTest, TestCirclePredicateDependency) {
+    sp<UidMap> uidMap = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
+    StatsdConfig config = buildCirclePredicates();
+    set<int> allTagIds;
+    vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
+    vector<sp<MetricProducer>> allMetricProducers;
+    unordered_map<int64_t, int> metricProducerMap;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
+    vector<int> metricsWithActivation;
+    map<int64_t, uint64_t> stateProtoHashes;
+    std::set<int64_t> noReportMetricIds;
+
+    EXPECT_FALSE(initStatsdConfig(
+            kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+            allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+            trackerToConditionMap, activationAtomTrackerToMetricMap,
+            deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+            stateProtoHashes, noReportMetricIds));
+}
+
+TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
+    sp<UidMap> uidMap = new UidMap();
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
+    StatsdConfig config = buildAlertWithUnknownMetric();
+    set<int> allTagIds;
+    vector<sp<AtomMatchingTracker>> allAtomMatchingTrackers;
+    unordered_map<int64_t, int> atomMatchingTrackerMap;
+    vector<sp<ConditionTracker>> allConditionTrackers;
+    unordered_map<int64_t, int> conditionTrackerMap;
+    vector<sp<MetricProducer>> allMetricProducers;
+    unordered_map<int64_t, int> metricProducerMap;
+    std::vector<sp<AnomalyTracker>> allAnomalyTrackers;
+    std::vector<sp<AlarmTracker>> allAlarmTrackers;
+    unordered_map<int, std::vector<int>> conditionToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToMetricMap;
+    unordered_map<int, std::vector<int>> trackerToConditionMap;
+    unordered_map<int, std::vector<int>> activationAtomTrackerToMetricMap;
+    unordered_map<int, std::vector<int>> deactivationAtomTrackerToMetricMap;
+    unordered_map<int64_t, int> alertTrackerMap;
+    vector<int> metricsWithActivation;
+    map<int64_t, uint64_t> stateProtoHashes;
+    std::set<int64_t> noReportMetricIds;
+
+    EXPECT_FALSE(initStatsdConfig(
+            kConfigKey, config, uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
+            timeBaseSec, timeBaseSec, allTagIds, allAtomMatchingTrackers, atomMatchingTrackerMap,
+            allConditionTrackers, conditionTrackerMap, allMetricProducers, metricProducerMap,
+            allAnomalyTrackers, allAlarmTrackers, conditionToMetricMap, trackerToMetricMap,
+            trackerToConditionMap, activationAtomTrackerToMetricMap,
+            deactivationAtomTrackerToMetricMap, alertTrackerMap, metricsWithActivation,
+            stateProtoHashes, noReportMetricIds));
+}
+
+TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerInvalidMatcher) {
+    sp<UidMap> uidMap = new UidMap();
+    AtomMatcher matcher;
+    // Matcher has no contents_case (simple/combination), so it is invalid.
+    matcher.set_id(21);
+    EXPECT_EQ(createAtomMatchingTracker(matcher, 0, uidMap), nullptr);
+}
+
+TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerSimple) {
+    int index = 1;
+    int64_t id = 123;
+    sp<UidMap> uidMap = new UidMap();
+    AtomMatcher matcher;
+    matcher.set_id(id);
+    SimpleAtomMatcher* simpleAtomMatcher = matcher.mutable_simple_atom_matcher();
+    simpleAtomMatcher->set_atom_id(util::SCREEN_STATE_CHANGED);
+    simpleAtomMatcher->add_field_value_matcher()->set_field(
+            1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
+    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
+            android::view::DisplayStateEnum::DISPLAY_STATE_ON);
+
+    sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, index, uidMap);
+    EXPECT_NE(tracker, nullptr);
+
+    EXPECT_TRUE(tracker->mInitialized);
+    EXPECT_EQ(tracker->getId(), id);
+    EXPECT_EQ(tracker->mIndex, index);
+    const set<int>& atomIds = tracker->getAtomIds();
+    ASSERT_EQ(atomIds.size(), 1);
+    EXPECT_EQ(atomIds.count(util::SCREEN_STATE_CHANGED), 1);
+}
+
+TEST(MetricsManagerTest, TestCreateAtomMatchingTrackerCombination) {
+    int index = 1;
+    int64_t id = 123;
+    sp<UidMap> uidMap = new UidMap();
+    AtomMatcher matcher;
+    matcher.set_id(id);
+    AtomMatcher_Combination* combination = matcher.mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+    combination->add_matcher(123);
+    combination->add_matcher(223);
+
+    sp<AtomMatchingTracker> tracker = createAtomMatchingTracker(matcher, index, uidMap);
+    EXPECT_NE(tracker, nullptr);
+
+    // Combination matchers need to be initialized first.
+    EXPECT_FALSE(tracker->mInitialized);
+    EXPECT_EQ(tracker->getId(), id);
+    EXPECT_EQ(tracker->mIndex, index);
+    const set<int>& atomIds = tracker->getAtomIds();
+    ASSERT_EQ(atomIds.size(), 0);
+}
+
+TEST(MetricsManagerTest, TestCreateConditionTrackerInvalid) {
+    const ConfigKey key(123, 456);
+    // Predicate has no contents_case (simple/combination), so it is invalid.
+    Predicate predicate;
+    predicate.set_id(21);
+    unordered_map<int64_t, int> atomTrackerMap;
+    EXPECT_EQ(createConditionTracker(key, predicate, 0, atomTrackerMap), nullptr);
+}
+
+TEST(MetricsManagerTest, TestCreateConditionTrackerSimple) {
+    int index = 1;
+    int64_t id = 987;
+    const ConfigKey key(123, 456);
+
+    int startMatcherIndex = 2, stopMatcherIndex = 0, stopAllMatcherIndex = 1;
+    int64_t startMatcherId = 246, stopMatcherId = 153, stopAllMatcherId = 975;
+
+    Predicate predicate;
+    predicate.set_id(id);
+    SimplePredicate* simplePredicate = predicate.mutable_simple_predicate();
+    simplePredicate->set_start(startMatcherId);
+    simplePredicate->set_stop(stopMatcherId);
+    simplePredicate->set_stop_all(stopAllMatcherId);
+
+    unordered_map<int64_t, int> atomTrackerMap;
+    atomTrackerMap[startMatcherId] = startMatcherIndex;
+    atomTrackerMap[stopMatcherId] = stopMatcherIndex;
+    atomTrackerMap[stopAllMatcherId] = stopAllMatcherIndex;
+
+    sp<ConditionTracker> tracker = createConditionTracker(key, predicate, index, atomTrackerMap);
+    EXPECT_EQ(tracker->getConditionId(), id);
+    EXPECT_EQ(tracker->isSliced(), false);
+    EXPECT_TRUE(tracker->IsSimpleCondition());
+    const set<int>& interestedMatchers = tracker->getAtomMatchingTrackerIndex();
+    ASSERT_EQ(interestedMatchers.size(), 3);
+    ASSERT_EQ(interestedMatchers.count(startMatcherIndex), 1);
+    ASSERT_EQ(interestedMatchers.count(stopMatcherIndex), 1);
+    ASSERT_EQ(interestedMatchers.count(stopAllMatcherIndex), 1);
+}
+
+TEST(MetricsManagerTest, TestCreateConditionTrackerCombination) {
+    int index = 1;
+    int64_t id = 987;
+    const ConfigKey key(123, 456);
+
+    Predicate predicate;
+    predicate.set_id(id);
+    Predicate_Combination* combinationPredicate = predicate.mutable_combination();
+    combinationPredicate->set_operation(LogicalOperation::AND);
+    combinationPredicate->add_predicate(888);
+    combinationPredicate->add_predicate(777);
+
+    // Combination conditions must be initialized to set most state.
+    unordered_map<int64_t, int> atomTrackerMap;
+    sp<ConditionTracker> tracker = createConditionTracker(key, predicate, index, atomTrackerMap);
+    EXPECT_EQ(tracker->getConditionId(), id);
+    EXPECT_FALSE(tracker->IsSimpleCondition());
+}
+
+TEST(MetricsManagerTest, TestCreateAnomalyTrackerInvalidMetric) {
+    Alert alert;
+    alert.set_id(123);
+    alert.set_metric_id(1);
+    alert.set_trigger_if_sum_gt(1);
+    alert.set_num_buckets(1);
+
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    vector<sp<MetricProducer>> metricProducers;
+    // Pass in empty metric producers, causing an error.
+    EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, {}, metricProducers), nullopt);
+}
+
+TEST(MetricsManagerTest, TestCreateAnomalyTrackerNoThreshold) {
+    int64_t metricId = 1;
+    Alert alert;
+    alert.set_id(123);
+    alert.set_metric_id(metricId);
+    alert.set_num_buckets(1);
+
+    CountMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    vector<sp<MetricProducer>> metricProducers({new CountMetricProducer(
+            kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, 0x0123456789, 0, 0)});
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, {{1, 0}}, metricProducers), nullopt);
+}
+
+TEST(MetricsManagerTest, TestCreateAnomalyTrackerMissingBuckets) {
+    int64_t metricId = 1;
+    Alert alert;
+    alert.set_id(123);
+    alert.set_metric_id(metricId);
+    alert.set_trigger_if_sum_gt(1);
+
+    CountMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    vector<sp<MetricProducer>> metricProducers({new CountMetricProducer(
+            kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, 0x0123456789, 0, 0)});
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, {{1, 0}}, metricProducers), nullopt);
+}
+
+TEST(MetricsManagerTest, TestCreateAnomalyTrackerGood) {
+    int64_t metricId = 1;
+    Alert alert;
+    alert.set_id(123);
+    alert.set_metric_id(metricId);
+    alert.set_trigger_if_sum_gt(1);
+    alert.set_num_buckets(1);
+
+    CountMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    vector<sp<MetricProducer>> metricProducers({new CountMetricProducer(
+            kConfigKey, metric, 0, {ConditionState::kUnknown}, wizard, 0x0123456789, 0, 0)});
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    EXPECT_NE(createAnomalyTracker(alert, anomalyAlarmMonitor, {{1, 0}}, metricProducers), nullopt);
+}
+
+TEST(MetricsManagerTest, TestCreateAnomalyTrackerDurationTooLong) {
+    int64_t metricId = 1;
+    Alert alert;
+    alert.set_id(123);
+    alert.set_metric_id(metricId);
+    // Impossible for alert to fire since the time is bigger than bucketSize * numBuckets
+    alert.set_trigger_if_sum_gt(MillisToNano(TimeUnitToBucketSizeInMillis(ONE_MINUTE)) + 1);
+    alert.set_num_buckets(1);
+
+    DurationMetric metric;
+    metric.set_id(metricId);
+    metric.set_bucket(ONE_MINUTE);
+    metric.set_aggregation_type(DurationMetric_AggregationType_SUM);
+    FieldMatcher dimensions;
+    sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
+    vector<sp<MetricProducer>> metricProducers({new DurationMetricProducer(
+            kConfigKey, metric, -1 /*no condition*/, {}, -1 /* what index not needed*/,
+            1 /* start index */, 2 /* stop index */, 3 /* stop_all index */, false /*nesting*/,
+            wizard, 0x0123456789, dimensions, 0, 0)});
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    EXPECT_EQ(createAnomalyTracker(alert, anomalyAlarmMonitor, {{1, 0}}, metricProducers), nullopt);
+}
+
+TEST(MetricsManagerTest, TestCreateDurationProducerDimensionsInWhatInvalid) {
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");
+    *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+    *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+    *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
+    *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
+
+    Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+    // The predicate is dimensioning by first attribution node by uid.
+    FieldMatcher dimensions =
+            CreateAttributionUidDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() = dimensions;
+    *config.add_predicate() = holdingWakelockPredicate;
+
+    DurationMetric* durationMetric = config.add_duration_metric();
+    durationMetric->set_id(StringToId("WakelockDuration"));
+    durationMetric->set_what(holdingWakelockPredicate.id());
+    durationMetric->set_aggregation_type(DurationMetric::SUM);
+    // The metric is dimensioning by first attribution node by uid AND tag.
+    // Invalid since the predicate only dimensions by uid.
+    *durationMetric->mutable_dimensions_in_what() = CreateAttributionUidAndOtherDimensions(
+            util::WAKELOCK_STATE_CHANGED, {Position::FIRST}, {3 /* tag */});
+    durationMetric->set_bucket(FIVE_MINUTES);
+
+    ConfigKey key(123, 987);
+    uint64_t timeNs = 456;
+    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
+    sp<AlarmMonitor> anomalyAlarmMonitor;
+    sp<AlarmMonitor> periodicAlarmMonitor;
+    sp<UidMap> uidMap;
+    sp<MetricsManager> metricsManager =
+            new MetricsManager(key, config, timeNs, timeNs, uidMap, pullerManager,
+                               anomalyAlarmMonitor, periodicAlarmMonitor);
+    EXPECT_FALSE(metricsManager->isConfigValid());
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/bin/tests/shell/ShellSubscriber_test.cpp b/bin/tests/shell/ShellSubscriber_test.cpp
index 5d10bf6..7b131fc 100644
--- a/bin/tests/shell/ShellSubscriber_test.cpp
+++ b/bin/tests/shell/ShellSubscriber_test.cpp
@@ -190,9 +190,9 @@
 
     sp<MockStatsPullerManager> pullerManager = new StrictMock<MockStatsPullerManager>();
     const vector<int32_t> uids = {AID_SYSTEM};
-    EXPECT_CALL(*pullerManager, Pull(10016, uids, _, _, _))
+    EXPECT_CALL(*pullerManager, Pull(10016, uids, _, _))
             .WillRepeatedly(Invoke([](int tagId, const vector<int32_t>&, const int64_t,
-                                      vector<std::shared_ptr<LogEvent>>* data, bool) {
+                                      vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid1, /*timeMillis=*/kCpuTime1));
                 data->push_back(makeCpuActiveTimeAtom(/*uid=*/kUid2, /*timeMillis=*/kCpuTime2));
diff --git a/bin/tests/statsd_test_util.cpp b/bin/tests/statsd_test_util.cpp
index cee8372..153b696 100644
--- a/bin/tests/statsd_test_util.cpp
+++ b/bin/tests/statsd_test_util.cpp
@@ -15,6 +15,8 @@
 #include "statsd_test_util.h"
 
 #include <aidl/android/util/StatsEventParcel.h>
+
+#include "matchers/SimpleAtomMatchingTracker.h"
 #include "stats_event.h"
 
 using aidl::android::util::StatsEventParcel;
@@ -996,6 +998,20 @@
     return static_cast<int64_t>(std::hash<std::string>()(str));
 }
 
+sp<EventMatcherWizard> createEventMatcherWizard(
+        int tagId, int matcherIndex, const vector<FieldValueMatcher>& fieldValueMatchers) {
+    sp<UidMap> uidMap = new UidMap();
+    SimpleAtomMatcher atomMatcher;
+    atomMatcher.set_atom_id(tagId);
+    for (const FieldValueMatcher& fvm : fieldValueMatchers) {
+        *atomMatcher.add_field_value_matcher() = fvm;
+    }
+    uint64_t matcherHash = 0x12345678;
+    int64_t matcherId = 678;
+    return new EventMatcherWizard({new SimpleAtomMatchingTracker(
+            matcherId, matcherIndex, matcherHash, atomMatcher, uidMap)});
+}
+
 void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, const int atomId,
                                                    const int uid, const string& tag) {
     EXPECT_EQ(value.field(), atomId);
@@ -1075,6 +1091,21 @@
         .value_tuple().dimensions_value(1).value_str(), tag);
 }
 
+void ValidateStateValue(const google::protobuf::RepeatedPtrField<StateValue>& stateValues,
+                        int atomId, int64_t value) {
+    ASSERT_EQ(stateValues.size(), 1);
+    ASSERT_EQ(stateValues[0].atom_id(), atomId);
+    switch (stateValues[0].contents_case()) {
+        case StateValue::ContentsCase::kValue:
+            EXPECT_EQ(stateValues[0].value(), (int32_t)value);
+            break;
+        case StateValue::ContentsCase::kGroupId:
+            EXPECT_EQ(stateValues[0].group_id(), value);
+            break;
+        default:
+            FAIL() << "State value should have either a value or a group id";
+    }
+}
 bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) {
     if (s1.field() != s2.field()) {
         return false;
diff --git a/bin/tests/statsd_test_util.h b/bin/tests/statsd_test_util.h
index eb2f294..dc9b285 100644
--- a/bin/tests/statsd_test_util.h
+++ b/bin/tests/statsd_test_util.h
@@ -25,6 +25,7 @@
 #include "src/StatsLogProcessor.h"
 #include "src/hash.h"
 #include "src/logd/LogEvent.h"
+#include "src/matchers/EventMatcherWizard.h"
 #include "src/packages/UidMap.h"
 #include "src/stats_log_util.h"
 #include "stats_event.h"
@@ -335,6 +336,9 @@
 
 int64_t StringToId(const string& str);
 
+sp<EventMatcherWizard> createEventMatcherWizard(
+        int tagId, int matcherIndex, const std::vector<FieldValueMatcher>& fieldValueMatchers = {});
+
 void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, const int atomId,
                                                    const int uid, const string& tag);
 void ValidateUidDimension(const DimensionsValue& value, int node_idx, int atomId, int uid);
@@ -343,6 +347,8 @@
     const DimensionsValue& value, int atomId, int uid, const std::string& tag);
 void ValidateAttributionUidAndTagDimension(
     const DimensionsValue& value, int node_idx, int atomId, int uid, const std::string& tag);
+void ValidateStateValue(const google::protobuf::RepeatedPtrField<StateValue>& stateValues,
+                        int atomId, int64_t value);
 
 struct DimensionsPair {
     DimensionsPair(DimensionsValue m1, google::protobuf::RepeatedPtrField<StateValue> m2)
diff --git a/lib/libstatspull/Android.bp b/lib/libstatspull/Android.bp
index 6d6b466..54ef7a8 100644
--- a/lib/libstatspull/Android.bp
+++ b/lib/libstatspull/Android.bp
@@ -30,18 +30,39 @@
     shared_libs: [
         "libbinder_ndk",
         "liblog",
-        "libstatssocket",
     ],
     static_libs: [
         "libutils",
         "statsd-aidl-ndk_platform",
     ],
+    target: {
+        android: {
+            shared_libs: ["libstatssocket"],
+        },
+        host: {
+            static_libs: ["libstatssocket"],
+        },
+    },
 }
-cc_library_shared {
+cc_library {
     name: "libstatspull",
     defaults: [
-        "libstatspull_defaults"
+        "libstatspull_defaults",
+        "libbinder_ndk_host_user",
     ],
+    host_supported: true,
+    target: {
+        android: {
+            static: {
+                enabled: false,
+            },
+        },
+        host: {
+            shared: {
+                enabled: false,
+            },
+        },
+    },
     // enumerate stable entry points for APEX use
     stubs: {
         symbol_file: "libstatspull.map.txt",
@@ -68,6 +89,9 @@
     defaults: [
         "libstatspull_defaults",
     ],
+    cflags: [
+        "-DLIB_STATS_PULL_TESTS_FLAG",
+    ],
     visibility: [
         "//frameworks/base/apex/statsd/tests/libstatspull",
         "//packages/modules/StatsD/apex/tests/libstatspull",
diff --git a/lib/libstatspull/stats_pull_atom_callback.cpp b/lib/libstatspull/stats_pull_atom_callback.cpp
index 478cae7..2d924a5 100644
--- a/lib/libstatspull/stats_pull_atom_callback.cpp
+++ b/lib/libstatspull/stats_pull_atom_callback.cpp
@@ -120,6 +120,9 @@
 
         // Convert stats_events into StatsEventParcels.
         std::vector<StatsEventParcel> parcels;
+
+        // Resolves fuzz build failure in b/161575591.
+#if defined(__ANDROID_APEX__) || defined(LIB_STATS_PULL_TESTS_FLAG)
         for (int i = 0; i < statsEventList.data.size(); i++) {
             size_t size;
             uint8_t* buffer = AStatsEvent_getBuffer(statsEventList.data[i], &size);
@@ -130,6 +133,7 @@
             p.buffer.assign(buffer, buffer + size);
             parcels.push_back(std::move(p));
         }
+#endif
 
         Status status = resultReceiver->pullFinished(atomTag, success, parcels);
         if (!status.isOk()) {
diff --git a/lib/libstatssocket/stats_event.c b/lib/libstatssocket/stats_event.c
index f3e8087..dcd34aa 100644
--- a/lib/libstatssocket/stats_event.c
+++ b/lib/libstatssocket/stats_event.c
@@ -232,14 +232,19 @@
 
 void AStatsEvent_writeByteArray(AStatsEvent* event, const uint8_t* buf, size_t numBytes) {
     start_field(event, BYTE_ARRAY_TYPE);
+    if (buf == NULL) {
+        numBytes = 0;
+    }
     append_int32(event, numBytes);
-    append_byte_array(event, buf, numBytes);
+    if (numBytes > 0) {
+        append_byte_array(event, buf, numBytes);
+    }
 }
 
 // Value is assumed to be encoded using UTF8
 void AStatsEvent_writeString(AStatsEvent* event, const char* value) {
     start_field(event, STRING_TYPE);
-    append_string(event, value);
+    append_string(event, value == NULL ? "" : value);
 }
 
 // Tags are assumed to be encoded using UTF8
@@ -255,7 +260,7 @@
 
     for (uint8_t i = 0; i < numNodes; i++) {
         append_int32(event, uids[i]);
-        append_string(event, tags[i]);
+        append_string(event, tags[i] == NULL ? "" : tags[i]);
     }
 }
 
diff --git a/lib/libstatssocket/tests/stats_event_test.cpp b/lib/libstatssocket/tests/stats_event_test.cpp
index 9a1fac8..2f9ccdc 100644
--- a/lib/libstatssocket/tests/stats_event_test.cpp
+++ b/lib/libstatssocket/tests/stats_event_test.cpp
@@ -183,6 +183,31 @@
     AStatsEvent_release(event);
 }
 
+TEST(StatsEventTest, TestNullString) {
+    uint32_t atomId = 100;
+    char* str = nullptr;
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    AStatsEvent* event = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(event, atomId);
+    AStatsEvent_writeString(event, str);
+    AStatsEvent_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+    checkTypeHeader(&buffer, STRING_TYPE);
+    checkString(&buffer, "");
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(AStatsEvent_getErrors(event), 0);
+    AStatsEvent_release(event);
+}
+
 TEST(StatsEventTest, TestByteArrays) {
     uint32_t atomId = 100;
     vector<uint8_t> message = {'b', 'y', 't', '\0', 'e', 's'};
@@ -208,6 +233,32 @@
     AStatsEvent_release(event);
 }
 
+TEST(StatsEventTest, TestNullByteArrays) {
+    uint32_t atomId = 100;
+    uint8_t* buf = nullptr;
+    vector<uint8_t> message;
+
+    int64_t startTime = android::elapsedRealtimeNano();
+    AStatsEvent* event = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(event, atomId);
+    AStatsEvent_writeByteArray(event, buf, 2);
+    AStatsEvent_build(event);
+    int64_t endTime = android::elapsedRealtimeNano();
+
+    size_t bufferSize;
+    uint8_t* buffer = AStatsEvent_getBuffer(event, &bufferSize);
+    uint8_t* bufferEnd = buffer + bufferSize;
+
+    checkMetadata(&buffer, /*numElements=*/1, startTime, endTime, atomId);
+
+    checkTypeHeader(&buffer, BYTE_ARRAY_TYPE);
+    checkByteArray(&buffer, message);
+
+    EXPECT_EQ(buffer, bufferEnd);  // ensure that we have read the entire buffer
+    EXPECT_EQ(AStatsEvent_getErrors(event), 0);
+    AStatsEvent_release(event);
+}
+
 TEST(StatsEventTest, TestAttributionChains) {
     uint32_t atomId = 100;
 
@@ -217,8 +268,13 @@
     const char* cTags[numNodes];
     for (int i = 0; i < (int)numNodes; i++) {
         uids[i] = i;
-        tags.push_back("test" + std::to_string(i));
-        cTags[i] = tags[i].c_str();
+        if (0 == i) {
+            tags.push_back("");
+            cTags[i] = nullptr;
+        } else {
+            tags.push_back("test" + std::to_string(i));
+            cTags[i] = tags[i].c_str();
+        }
     }
 
     int64_t startTime = android::elapsedRealtimeNano();
diff --git a/tests/Android.bp b/tests/Android.bp
index dccd5be..386606b 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -39,7 +39,5 @@
     data: [
         "**/*.pbtxt",
         ":CtsStatsdApp",
-        ":CtsStatsdEmptyApp",
-        ":CtsStatsdEmptySplitApp",
     ],
 }
diff --git a/tests/TEST_MAPPING b/tests/TEST_MAPPING
new file mode 100644
index 0000000..f48ff4b
--- /dev/null
+++ b/tests/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "postsubmit" : [
+    {
+      "name" : "CtsStatsdHostTestCases"
+    }
+  ]
+}
diff --git a/tests/apps/emptyapp/Android.bp b/tests/apps/emptyapp/Android.bp
deleted file mode 100644
index d01d14d..0000000
--- a/tests/apps/emptyapp/Android.bp
+++ /dev/null
@@ -1,28 +0,0 @@
-// Copyright (C) 2020 The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-android_test_helper_app {
-    name: "CtsStatsdEmptyApp",
-    defaults: ["cts_defaults"],
-    sdk_version: "current",
-    v4_signature: true,
-}
-
-android_test_helper_app {
-    name: "CtsStatsdEmptySplitApp",
-    defaults: ["cts_defaults"],
-    sdk_version: "current",
-    v4_signature: true,
-    package_splits: ["pl"],
-}
\ No newline at end of file
diff --git a/tests/apps/emptyapp/AndroidManifest.xml b/tests/apps/emptyapp/AndroidManifest.xml
deleted file mode 100644
index f40d070..0000000
--- a/tests/apps/emptyapp/AndroidManifest.xml
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  ~ Copyright (C) 2020 The Android Open Source Project
-  ~
-  ~ Licensed under the Apache License, Version 2.0 (the "License");
-  ~ you may not use this file except in compliance with the License.
-  ~ You may obtain a copy of the License at
-  ~
-  ~      http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing, software
-  ~ distributed under the License is distributed on an "AS IS" BASIS,
-  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  ~ See the License for the specific language governing permissions and
-  ~ limitations under the License.
-  -->
-
-<manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.cts.device.statsd.emptyapp">
-    <application android:hasCode="false" android:label="Empty Test App" />
-</manifest>
-
diff --git a/tests/apps/statsdapp/AndroidManifest.xml b/tests/apps/statsdapp/AndroidManifest.xml
index 2b479bd..875488b 100644
--- a/tests/apps/statsdapp/AndroidManifest.xml
+++ b/tests/apps/statsdapp/AndroidManifest.xml
@@ -15,9 +15,9 @@
 -->
 
 <manifest xmlns:android="http://schemas.android.com/apk/res/android"
-          package="com.android.server.cts.device.statsd"
-          android:versionCode="10" >
-    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
+     package="com.android.server.cts.device.statsd"
+     android:versionCode="10">
+    <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
@@ -27,86 +27,91 @@
     <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.CHANGE_WIFI_MULTICAST_STATE"/>
     <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
-    <uses-permission android:name="android.permission.CONFIGURE_DISPLAY_BRIGHTNESS" />
-    <uses-permission android:name="android.permission.DUMP" /> <!-- must be granted via pm grant -->
-    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
-    <uses-permission android:name="android.permission.INTERNET" />
-    <uses-permission android:name="android.permission.READ_SYNC_STATS" />
-    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
-    <uses-permission android:name="android.permission.VIBRATE" />
-    <uses-permission android:name="android.permission.WAKE_LOCK" />
-    <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
+    <uses-permission android:name="android.permission.CONFIGURE_DISPLAY_BRIGHTNESS"/>
+    <uses-permission android:name="android.permission.DUMP"/> <!-- must be granted via pm grant -->
+    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
+    <uses-permission android:name="android.permission.INTERNET"/>
+    <uses-permission android:name="android.permission.READ_SYNC_STATS"/>
+    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
+    <uses-permission android:name="android.permission.VIBRATE"/>
+    <uses-permission android:name="android.permission.WAKE_LOCK"/>
+    <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS"/>
+    <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS"/>
 
     <application android:label="@string/app_name">
-        <uses-library android:name="android.test.runner" />
-        <uses-library android:name="org.apache.http.legacy" android:required="false" />
+        <uses-library android:name="android.test.runner"/>
+        <uses-library android:name="org.apache.http.legacy"
+             android:required="false"/>
 
-        <service android:name=".StatsdCtsBackgroundService" android:exported="true" />
-        <activity android:name=".StatsdCtsForegroundActivity" android:exported="true" />
+        <service android:name=".StatsdCtsBackgroundService"
+             android:exported="true"/>
+        <activity android:name=".StatsdCtsForegroundActivity"
+             android:exported="true"/>
         <service android:name=".StatsdCtsForegroundService"
-                 android:foregroundServiceType="camera" android:exported="true" />
+             android:foregroundServiceType="camera"
+             android:exported="true"/>
 
-        <activity
-            android:name=".VideoPlayerActivity"
-            android:label="@string/app_name"
-            android:resizeableActivity="true"
-            android:supportsPictureInPicture="true"
-            android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
-            android:launchMode="singleTop" >
+        <activity android:name=".VideoPlayerActivity"
+             android:label="@string/app_name"
+             android:resizeableActivity="true"
+             android:supportsPictureInPicture="true"
+             android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation"
+             android:launchMode="singleTop"
+             android:exported="true">
             <intent-filter>
-                <action android:name="android.intent.action.MAIN" />
-                <category android:name="android.intent.category.LAUNCHER" />
+                <action android:name="android.intent.action.MAIN"/>
+                <category android:name="android.intent.category.LAUNCHER"/>
             </intent-filter>
         </activity>
 
-        <activity android:name=".DaveyActivity" android:exported="true" />
-        <activity android:name=".HiddenApiUsedActivity" android:exported="true" />
-        <activity
-            android:name=".ANRActivity"
-            android:label="ANR Test Activity"
-            android:launchMode="singleInstance"
-            android:process=":ANRProcess"
-            android:exported="true"
-          />
+        <activity android:name=".DaveyActivity"
+             android:exported="true"/>
+        <activity android:name=".ANRActivity"
+             android:label="ANR Test Activity"
+             android:launchMode="singleInstance"
+             android:process=":ANRProcess"
+             android:exported="true"/>
 
         <service android:name=".StatsdAuthenticator"
-            android:exported="false">
+             android:exported="false">
             <intent-filter>
-                <action android:name="android.accounts.AccountAuthenticator" />
+                <action android:name="android.accounts.AccountAuthenticator"/>
             </intent-filter>
 
             <meta-data android:name="android.accounts.AccountAuthenticator"
-                android:resource="@xml/authenticator" />
+                 android:resource="@xml/authenticator"/>
         </service>
         <service android:name="StatsdSyncService"
-            android:exported="false" >
+             android:exported="false">
             <intent-filter>
-                <action android:name="android.content.SyncAdapter" />
+                <action android:name="android.content.SyncAdapter"/>
             </intent-filter>
             <meta-data android:name="android.content.SyncAdapter"
-                android:resource="@xml/syncadapter" />
+                 android:resource="@xml/syncadapter"/>
         </service>
 
         <provider android:name=".StatsdProvider"
-            android:authorities="com.android.server.cts.device.statsd.provider" />
+             android:authorities="com.android.server.cts.device.statsd.provider"/>
 
         <service android:name=".StatsdJobService"
-            android:permission="android.permission.BIND_JOB_SERVICE" />
+             android:permission="android.permission.BIND_JOB_SERVICE"/>
 
         <service android:name=".DummyCallscreeningService"
-                 android:permission="android.permission.BIND_SCREENING_SERVICE">
+             android:permission="android.permission.BIND_SCREENING_SERVICE"
+             android:exported="true">
             <intent-filter>
-                <action android:name="android.telecom.CallScreeningService" />
+                <action android:name="android.telecom.CallScreeningService"/>
             </intent-filter>
         </service>
 
-        <service android:name=".IsolatedProcessService" android:isolatedProcess="true" />
+        <service android:name=".IsolatedProcessService"
+             android:isolatedProcess="true"/>
     </application>
 
     <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
-                     android:targetPackage="com.android.server.cts.device.statsd"
-                     android:label="CTS tests of android.os.statsd stats collection">
+         android:targetPackage="com.android.server.cts.device.statsd"
+         android:label="CTS tests of android.os.statsd stats collection">
         <meta-data android:name="listener"
-                   android:value="com.android.cts.runner.CtsTestRunListener" />
-    </instrumentation>/>
+             android:value="com.android.cts.runner.CtsTestRunListener"/>
+    </instrumentation>
 </manifest>
diff --git a/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java b/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
index 4eb659c..a53064b 100644
--- a/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
+++ b/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/AtomTests.java
@@ -17,7 +17,6 @@
 package com.android.server.cts.device.statsd;
 
 import static com.android.compatibility.common.util.SystemUtil.runShellCommand;
-
 import static com.google.common.truth.Truth.assertWithMessage;
 
 import android.accounts.Account;
@@ -68,22 +67,17 @@
 import android.os.SystemClock;
 import android.os.VibrationEffect;
 import android.os.Vibrator;
+import android.provider.Settings;
 import android.text.TextUtils;
 import android.util.ArrayMap;
 import android.util.Log;
 import android.util.StatsEvent;
 import android.util.StatsLog;
-
 import androidx.annotation.NonNull;
 import androidx.test.InstrumentationRegistry;
-
 import com.android.compatibility.common.util.ShellIdentityUtils;
-import com.android.utils.blob.DummyBlobData;
-
+import com.android.utils.blob.FakeBlobData;
 import com.google.common.io.BaseEncoding;
-
-import org.junit.Test;
-
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.util.Arrays;
@@ -93,6 +87,7 @@
 import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.TimeUnit;
 import java.util.function.BiConsumer;
+import org.junit.Test;
 
 public class AtomTests {
     private static final String TAG = AtomTests.class.getSimpleName();
@@ -205,6 +200,7 @@
         APP_OPS_ENUM_MAP.put(AppOpsManager.OPSTR_PHONE_CALL_CAMERA, 101);
         APP_OPS_ENUM_MAP.put(AppOpsManager.OPSTR_RECORD_AUDIO_HOTWORD, 102);
         APP_OPS_ENUM_MAP.put(AppOpsManager.OPSTR_MANAGE_ONGOING_CALLS, 103);
+        APP_OPS_ENUM_MAP.put(AppOpsManager.OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER, 105);
     }
 
     @Test
@@ -262,23 +258,38 @@
             int uid = Process.myUid();
             int whatAtomId = 9_999;
 
+            // Get the current setting for bluetooth background scanning.
+            // Set to 0 if the setting is not found or an error occurs.
+            int initialBleScanGlobalSetting = Settings.Global.getInt(
+                    InstrumentationRegistry.getTargetContext().getContentResolver(),
+                    Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0);
+
+            // Turn off bluetooth background scanning.
+            Settings.Global.putInt(InstrumentationRegistry.getTargetContext().getContentResolver(),
+                    Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, 0);
+
             // Change state to State.ON.
             bleScanner.startScan(null, scanSettings, scanCallback);
-            sleep(500);
+            sleep(6_000);
             writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
             writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
+
             bluetoothAdapter.disable();
-            sleep(1500);
+            sleep(6_000);
 
             // Trigger State.RESET so that new state is State.OFF.
             if (!bluetoothAdapter.enable()) {
                 Log.e(TAG, "Could not enable bluetooth to trigger state reset");
                 return;
             }
-            sleep(3_000); // Wait for Bluetooth to fully turn on.
+            sleep(6_000); // Wait for Bluetooth to fully turn on.
             writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
             writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
             writeSliceByBleScanStateChangedAtom(whatAtomId, uid, false, false, false);
+
+            // Set bluetooth background scanning to original setting.
+            Settings.Global.putInt(InstrumentationRegistry.getTargetContext().getContentResolver(),
+                    Settings.Global.BLE_SCAN_ALWAYS_AVAILABLE, initialBleScanGlobalSetting);
         });
     }
 
@@ -938,20 +949,6 @@
         }
     }
 
-    @Test
-    public void testIsolatedProcessService() throws Exception {
-        Context context = InstrumentationRegistry.getContext();
-        int uid = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0).uid;
-
-        // Start the isolated service, which logs an AppBreadcrumbReported atom, and then exit
-        // shortly afterwards.
-        Intent intent = new Intent(context, IsolatedProcessService.class);
-        context.startService(intent);
-        sleep(500);
-        context.stopService(intent);
-    }
-
-
     // Constants for testBlobStore
     private static final long BLOB_COMMIT_CALLBACK_TIMEOUT_SEC = 5;
     private static final long BLOB_EXPIRY_DURATION_MS = 24 * 60 * 60 * 1000;
@@ -968,7 +965,7 @@
         BlobStoreManager bsm = context.getSystemService(BlobStoreManager.class);
         final long leaseExpiryMs = System.currentTimeMillis() + BLOB_LEASE_EXPIRY_DURATION_MS;
 
-        final DummyBlobData blobData = new DummyBlobData.Builder(context).setExpiryDurationMs(
+        final FakeBlobData blobData = new FakeBlobData.Builder(context).setExpiryDurationMs(
                 BLOB_EXPIRY_DURATION_MS).setFileSize(BLOB_FILE_SIZE_BYTES).build();
 
         blobData.prepare();
@@ -1045,7 +1042,7 @@
     }
 
 
-    private void commitBlob(Context context, BlobStoreManager bsm, DummyBlobData blobData)
+    private void commitBlob(Context context, BlobStoreManager bsm, FakeBlobData blobData)
             throws Exception {;
         final long sessionId = bsm.createSession(blobData.getBlobHandle());
         try (BlobStoreManager.Session session = bsm.openSession(sessionId)) {
diff --git a/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/HiddenApiUsedActivity.java b/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/HiddenApiUsedActivity.java
deleted file mode 100644
index 2132f3c..0000000
--- a/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/HiddenApiUsedActivity.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2019 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.cts.device.statsd;
-
-import android.app.Activity;
-import android.os.Bundle;
-
-import java.lang.reflect.Field;
-
-
-public class HiddenApiUsedActivity extends Activity {
-    /** Called when the activity is first created. */
-    @Override
-    public void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        try {
-            Field field = Activity.class.getDeclaredField("mWindow");
-            field.setAccessible(true);
-            Object object = field.get(this);
-        } catch(NoSuchFieldException e) {
-        } catch(IllegalAccessException e) {
-        }
-        finish();
-    }
-
-}
\ No newline at end of file
diff --git a/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/IsolatedProcessService.java b/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/IsolatedProcessService.java
deleted file mode 100644
index 086a3be..0000000
--- a/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/IsolatedProcessService.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.server.cts.device.statsd;
-
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
-import android.util.StatsLog;
-
-public class IsolatedProcessService extends Service {
-    private static final String TAG = "IsolatedProcessService";
-
-    @Override
-    public int onStartCommand(Intent intent, int flags, int startId) {
-        StatsLog.logStart(/*label=*/0);
-        return START_NOT_STICKY;
-    }
-
-    @Override
-    public IBinder onBind(Intent intent) {
-        return null;
-    }
-}
diff --git a/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java b/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
index c8c02fc..1f14c3a 100644
--- a/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
+++ b/tests/apps/statsdapp/src/com/android/server/cts/device/statsd/StatsdCtsForegroundActivity.java
@@ -46,7 +46,6 @@
     public static final String ACTION_LONG_SLEEP_WHILE_TOP = "action.long_sleep_top";
     public static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
     public static final String ACTION_SHOW_NOTIFICATION = "action.show_notification";
-    public static final String ACTION_CRASH = "action.crash";
     public static final String ACTION_CREATE_CHANNEL_GROUP = "action.create_channel_group";
     public static final String ACTION_POLL_NETWORK_STATS = "action.poll_network_stats";
 
@@ -83,9 +82,6 @@
             case ACTION_SHOW_NOTIFICATION:
                 doShowNotification();
                 break;
-            case ACTION_CRASH:
-                doCrash();
-                break;
             case ACTION_CREATE_CHANNEL_GROUP:
                 doCreateChannelGroup();
                 break;
@@ -193,9 +189,4 @@
             finish();
         }
     }
-
-    @SuppressWarnings("ConstantOverflow")
-    private void doCrash() {
-        Log.e(TAG, "About to crash the app with 1/0 " + (long) 1 / 0);
-    }
 }
diff --git a/tests/src/android/cts/statsd/atom/DeviceAtomTestCase.java b/tests/src/android/cts/statsd/atom/DeviceAtomTestCase.java
index 035160f..03facd0 100644
--- a/tests/src/android/cts/statsd/atom/DeviceAtomTestCase.java
+++ b/tests/src/android/cts/statsd/atom/DeviceAtomTestCase.java
@@ -194,7 +194,7 @@
     }
 
     /**
-     * Required to successfully start a background service from adb in O.
+     * Required to successfully start a background service from adb in Android O.
      */
     protected void allowBackgroundServices() throws Exception {
         getDevice().executeShellCommand(String.format(
@@ -320,4 +320,9 @@
                 "settings get global netstats_combine_subtype_enabled").trim();
         return output.equals("1");
     }
+
+    void setNetworkStatsCombinedSubTypeEnabled(boolean enable) throws Exception {
+        getDevice().executeShellCommand("settings put global netstats_combine_subtype_enabled "
+                + (enable ? "1" : "0"));
+    }
 }
diff --git a/tests/src/android/cts/statsd/atom/HostAtomTests.java b/tests/src/android/cts/statsd/atom/HostAtomTests.java
deleted file mode 100644
index ee6f324..0000000
--- a/tests/src/android/cts/statsd/atom/HostAtomTests.java
+++ /dev/null
@@ -1,699 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.cts.statsd.atom;
-
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import android.os.BatteryPluggedStateEnum;
-import android.os.BatteryStatusEnum;
-import android.platform.test.annotations.RestrictedBuildTest;
-import android.server.DeviceIdleModeEnum;
-import android.view.DisplayStateEnum;
-import android.telephony.NetworkTypeEnum;
-
-import com.android.internal.os.StatsdConfigProto.StatsdConfig;
-import com.android.os.AtomsProto.AppBreadcrumbReported;
-import com.android.os.AtomsProto.Atom;
-import com.android.os.AtomsProto.BatterySaverModeStateChanged;
-import com.android.os.AtomsProto.BuildInformation;
-import com.android.os.AtomsProto.ConnectivityStateChanged;
-import com.android.os.AtomsProto.SimSlotState;
-import com.android.os.AtomsProto.SupportedRadioAccessFamily;
-import com.android.os.StatsLog.ConfigMetricsReportList;
-import com.android.os.StatsLog.EventMetricData;
-
-import com.google.common.collect.Range;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-/**
- * Statsd atom tests that are done via adb (hostside).
- */
-public class HostAtomTests extends AtomTestCase {
-
-    private static final String TAG = "Statsd.HostAtomTests";
-
-    // Either file must exist to read kernel wake lock stats.
-    private static final String WAKE_LOCK_FILE = "/proc/wakelocks";
-    private static final String WAKE_SOURCES_FILE = "/d/wakeup_sources";
-
-    // Bitmask of radio access technologies that all GSM phones should at least partially support
-    protected static final long NETWORK_TYPE_BITMASK_GSM_ALL =
-            (1 << (NetworkTypeEnum.NETWORK_TYPE_GSM_VALUE - 1))
-            | (1 << (NetworkTypeEnum.NETWORK_TYPE_GPRS_VALUE - 1))
-            | (1 << (NetworkTypeEnum.NETWORK_TYPE_EDGE_VALUE - 1))
-            | (1 << (NetworkTypeEnum.NETWORK_TYPE_UMTS_VALUE - 1))
-            | (1 << (NetworkTypeEnum.NETWORK_TYPE_HSDPA_VALUE - 1))
-            | (1 << (NetworkTypeEnum.NETWORK_TYPE_HSUPA_VALUE - 1))
-            | (1 << (NetworkTypeEnum.NETWORK_TYPE_HSPA_VALUE - 1))
-            | (1 << (NetworkTypeEnum.NETWORK_TYPE_HSPAP_VALUE - 1))
-            | (1 << (NetworkTypeEnum.NETWORK_TYPE_TD_SCDMA_VALUE - 1))
-            | (1 << (NetworkTypeEnum.NETWORK_TYPE_LTE_VALUE - 1))
-            | (1 << (NetworkTypeEnum.NETWORK_TYPE_LTE_CA_VALUE - 1))
-            | (1 << (NetworkTypeEnum.NETWORK_TYPE_NR_VALUE - 1));
-    // Bitmask of radio access technologies that all CDMA phones should at least partially support
-    protected static final long NETWORK_TYPE_BITMASK_CDMA_ALL =
-            (1 << (NetworkTypeEnum.NETWORK_TYPE_CDMA_VALUE - 1))
-            | (1 << (NetworkTypeEnum.NETWORK_TYPE_1XRTT_VALUE - 1))
-            | (1 << (NetworkTypeEnum.NETWORK_TYPE_EVDO_0_VALUE - 1))
-            | (1 << (NetworkTypeEnum.NETWORK_TYPE_EVDO_A_VALUE - 1))
-            | (1 << (NetworkTypeEnum.NETWORK_TYPE_EHRPD_VALUE - 1));
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-    }
-
-    public void testScreenStateChangedAtom() throws Exception {
-        // Setup, make sure the screen is off and turn off AoD if it is on.
-        // AoD needs to be turned off because the screen should go into an off state. But, if AoD is
-        // on and the device doesn't support STATE_DOZE, the screen sadly goes back to STATE_ON.
-        String aodState = getAodState();
-        setAodState("0");
-        turnScreenOn();
-        Thread.sleep(WAIT_TIME_SHORT);
-        turnScreenOff();
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        final int atomTag = Atom.SCREEN_STATE_CHANGED_FIELD_NUMBER;
-
-        Set<Integer> screenOnStates = new HashSet<>(
-                Arrays.asList(DisplayStateEnum.DISPLAY_STATE_ON_VALUE,
-                        DisplayStateEnum.DISPLAY_STATE_ON_SUSPEND_VALUE,
-                        DisplayStateEnum.DISPLAY_STATE_VR_VALUE));
-        Set<Integer> screenOffStates = new HashSet<>(
-                Arrays.asList(DisplayStateEnum.DISPLAY_STATE_OFF_VALUE,
-                        DisplayStateEnum.DISPLAY_STATE_DOZE_VALUE,
-                        DisplayStateEnum.DISPLAY_STATE_DOZE_SUSPEND_VALUE,
-                        DisplayStateEnum.DISPLAY_STATE_UNKNOWN_VALUE));
-
-        // Add state sets to the list in order.
-        List<Set<Integer>> stateSet = Arrays.asList(screenOnStates, screenOffStates);
-
-        createAndUploadConfig(atomTag);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Trigger events in same order.
-        turnScreenOn();
-        Thread.sleep(WAIT_TIME_LONG);
-        turnScreenOff();
-        Thread.sleep(WAIT_TIME_LONG);
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-        // reset screen to on
-        turnScreenOn();
-        // Restores AoD to initial state.
-        setAodState(aodState);
-        // Assert that the events happened in the expected order.
-        assertStatesOccurred(stateSet, data, WAIT_TIME_LONG,
-                atom -> atom.getScreenStateChanged().getState().getNumber());
-    }
-
-    public void testChargingStateChangedAtom() throws Exception {
-        if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
-        // Setup, set charging state to full.
-        setChargingState(5);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        final int atomTag = Atom.CHARGING_STATE_CHANGED_FIELD_NUMBER;
-
-        Set<Integer> batteryUnknownStates = new HashSet<>(
-                Arrays.asList(BatteryStatusEnum.BATTERY_STATUS_UNKNOWN_VALUE));
-        Set<Integer> batteryChargingStates = new HashSet<>(
-                Arrays.asList(BatteryStatusEnum.BATTERY_STATUS_CHARGING_VALUE));
-        Set<Integer> batteryDischargingStates = new HashSet<>(
-                Arrays.asList(BatteryStatusEnum.BATTERY_STATUS_DISCHARGING_VALUE));
-        Set<Integer> batteryNotChargingStates = new HashSet<>(
-                Arrays.asList(BatteryStatusEnum.BATTERY_STATUS_NOT_CHARGING_VALUE));
-        Set<Integer> batteryFullStates = new HashSet<>(
-                Arrays.asList(BatteryStatusEnum.BATTERY_STATUS_FULL_VALUE));
-
-        // Add state sets to the list in order.
-        List<Set<Integer>> stateSet = Arrays.asList(batteryUnknownStates, batteryChargingStates,
-                batteryDischargingStates, batteryNotChargingStates, batteryFullStates);
-
-        createAndUploadConfig(atomTag);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Trigger events in same order.
-        setChargingState(1);
-        Thread.sleep(WAIT_TIME_SHORT);
-        setChargingState(2);
-        Thread.sleep(WAIT_TIME_SHORT);
-        setChargingState(3);
-        Thread.sleep(WAIT_TIME_SHORT);
-        setChargingState(4);
-        Thread.sleep(WAIT_TIME_SHORT);
-        setChargingState(5);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        // Unfreeze battery state after test
-        resetBatteryStatus();
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Assert that the events happened in the expected order.
-        assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
-                atom -> atom.getChargingStateChanged().getState().getNumber());
-    }
-
-    public void testPluggedStateChangedAtom() throws Exception {
-        if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
-        // Setup, unplug device.
-        unplugDevice();
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        final int atomTag = Atom.PLUGGED_STATE_CHANGED_FIELD_NUMBER;
-
-        Set<Integer> unpluggedStates = new HashSet<>(
-                Arrays.asList(BatteryPluggedStateEnum.BATTERY_PLUGGED_NONE_VALUE));
-        Set<Integer> acStates = new HashSet<>(
-                Arrays.asList(BatteryPluggedStateEnum.BATTERY_PLUGGED_AC_VALUE));
-        Set<Integer> usbStates = new HashSet<>(
-                Arrays.asList(BatteryPluggedStateEnum.BATTERY_PLUGGED_USB_VALUE));
-        Set<Integer> wirelessStates = new HashSet<>(
-                Arrays.asList(BatteryPluggedStateEnum.BATTERY_PLUGGED_WIRELESS_VALUE));
-
-        // Add state sets to the list in order.
-        List<Set<Integer>> stateSet = Arrays.asList(acStates, unpluggedStates, usbStates,
-                unpluggedStates, wirelessStates, unpluggedStates);
-
-        createAndUploadConfig(atomTag);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Trigger events in same order.
-        plugInAc();
-        Thread.sleep(WAIT_TIME_SHORT);
-        unplugDevice();
-        Thread.sleep(WAIT_TIME_SHORT);
-        plugInUsb();
-        Thread.sleep(WAIT_TIME_SHORT);
-        unplugDevice();
-        Thread.sleep(WAIT_TIME_SHORT);
-        plugInWireless();
-        Thread.sleep(WAIT_TIME_SHORT);
-        unplugDevice();
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        // Unfreeze battery state after test
-        resetBatteryStatus();
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Assert that the events happened in the expected order.
-        assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
-                atom -> atom.getPluggedStateChanged().getState().getNumber());
-    }
-
-    public void testBatteryLevelChangedAtom() throws Exception {
-        if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
-        // Setup, set battery level to full.
-        setBatteryLevel(100);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        final int atomTag = Atom.BATTERY_LEVEL_CHANGED_FIELD_NUMBER;
-
-        Set<Integer> batteryLow = new HashSet<>(Arrays.asList(2));
-        Set<Integer> battery25p = new HashSet<>(Arrays.asList(25));
-        Set<Integer> battery50p = new HashSet<>(Arrays.asList(50));
-        Set<Integer> battery75p = new HashSet<>(Arrays.asList(75));
-        Set<Integer> batteryFull = new HashSet<>(Arrays.asList(100));
-
-        // Add state sets to the list in order.
-        List<Set<Integer>> stateSet = Arrays.asList(batteryLow, battery25p, battery50p,
-                battery75p, batteryFull);
-
-        createAndUploadConfig(atomTag);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Trigger events in same order.
-        setBatteryLevel(2);
-        Thread.sleep(WAIT_TIME_SHORT);
-        setBatteryLevel(25);
-        Thread.sleep(WAIT_TIME_SHORT);
-        setBatteryLevel(50);
-        Thread.sleep(WAIT_TIME_SHORT);
-        setBatteryLevel(75);
-        Thread.sleep(WAIT_TIME_SHORT);
-        setBatteryLevel(100);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        // Unfreeze battery state after test
-        resetBatteryStatus();
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Assert that the events happened in the expected order.
-        assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
-                atom -> atom.getBatteryLevelChanged().getBatteryLevel());
-    }
-
-    public void testDeviceIdleModeStateChangedAtom() throws Exception {
-        // Setup, leave doze mode.
-        leaveDozeMode();
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        final int atomTag = Atom.DEVICE_IDLE_MODE_STATE_CHANGED_FIELD_NUMBER;
-
-        Set<Integer> dozeOff = new HashSet<>(
-                Arrays.asList(DeviceIdleModeEnum.DEVICE_IDLE_MODE_OFF_VALUE));
-        Set<Integer> dozeLight = new HashSet<>(
-                Arrays.asList(DeviceIdleModeEnum.DEVICE_IDLE_MODE_LIGHT_VALUE));
-        Set<Integer> dozeDeep = new HashSet<>(
-                Arrays.asList(DeviceIdleModeEnum.DEVICE_IDLE_MODE_DEEP_VALUE));
-
-        // Add state sets to the list in order.
-        List<Set<Integer>> stateSet = Arrays.asList(dozeLight, dozeDeep, dozeOff);
-
-        createAndUploadConfig(atomTag);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Trigger events in same order.
-        enterDozeModeLight();
-        Thread.sleep(WAIT_TIME_SHORT);
-        enterDozeModeDeep();
-        Thread.sleep(WAIT_TIME_SHORT);
-        leaveDozeMode();
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();;
-
-        // Assert that the events happened in the expected order.
-        assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
-                atom -> atom.getDeviceIdleModeStateChanged().getState().getNumber());
-    }
-
-    public void testBatterySaverModeStateChangedAtom() throws Exception {
-        if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
-        // Setup, turn off battery saver.
-        turnBatterySaverOff();
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        final int atomTag = Atom.BATTERY_SAVER_MODE_STATE_CHANGED_FIELD_NUMBER;
-
-        Set<Integer> batterySaverOn = new HashSet<>(
-                Arrays.asList(BatterySaverModeStateChanged.State.ON_VALUE));
-        Set<Integer> batterySaverOff = new HashSet<>(
-                Arrays.asList(BatterySaverModeStateChanged.State.OFF_VALUE));
-
-        // Add state sets to the list in order.
-        List<Set<Integer>> stateSet = Arrays.asList(batterySaverOn, batterySaverOff);
-
-        createAndUploadConfig(atomTag);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Trigger events in same order.
-        turnBatterySaverOn();
-        Thread.sleep(WAIT_TIME_LONG);
-        turnBatterySaverOff();
-        Thread.sleep(WAIT_TIME_LONG);
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        // Assert that the events happened in the expected order.
-        assertStatesOccurred(stateSet, data, WAIT_TIME_LONG,
-                atom -> atom.getBatterySaverModeStateChanged().getState().getNumber());
-    }
-
-    @RestrictedBuildTest
-    public void testRemainingBatteryCapacity() throws Exception {
-        if (!hasFeature(FEATURE_WATCH, false)) return;
-        if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.REMAINING_BATTERY_CAPACITY_FIELD_NUMBER, null);
-
-        uploadConfig(config);
-
-        Thread.sleep(WAIT_TIME_LONG);
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_LONG);
-
-        List<Atom> data = getGaugeMetricDataList();
-
-        assertThat(data).isNotEmpty();
-        Atom atom = data.get(0);
-        assertThat(atom.getRemainingBatteryCapacity().hasChargeMicroAmpereHour()).isTrue();
-        if (hasBattery()) {
-            assertThat(atom.getRemainingBatteryCapacity().getChargeMicroAmpereHour())
-                .isGreaterThan(0);
-        }
-    }
-
-    @RestrictedBuildTest
-    public void testFullBatteryCapacity() throws Exception {
-        if (!hasFeature(FEATURE_WATCH, false)) return;
-        if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.FULL_BATTERY_CAPACITY_FIELD_NUMBER, null);
-
-        uploadConfig(config);
-
-        Thread.sleep(WAIT_TIME_LONG);
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_LONG);
-
-        List<Atom> data = getGaugeMetricDataList();
-
-        assertThat(data).isNotEmpty();
-        Atom atom = data.get(0);
-        assertThat(atom.getFullBatteryCapacity().hasCapacityMicroAmpereHour()).isTrue();
-        if (hasBattery()) {
-            assertThat(atom.getFullBatteryCapacity().getCapacityMicroAmpereHour()).isGreaterThan(0);
-        }
-    }
-
-    public void testBatteryVoltage() throws Exception {
-        if (!hasFeature(FEATURE_WATCH, false)) return;
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.BATTERY_VOLTAGE_FIELD_NUMBER, null);
-
-        uploadConfig(config);
-
-        Thread.sleep(WAIT_TIME_LONG);
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_LONG);
-
-        List<Atom> data = getGaugeMetricDataList();
-
-        assertThat(data).isNotEmpty();
-        Atom atom = data.get(0);
-        assertThat(atom.getBatteryVoltage().hasVoltageMillivolt()).isTrue();
-        if (hasBattery()) {
-            assertThat(atom.getBatteryVoltage().getVoltageMillivolt()).isGreaterThan(0);
-        }
-    }
-
-    // This test is for the pulled battery level atom.
-    public void testBatteryLevel() throws Exception {
-        if (!hasFeature(FEATURE_WATCH, false)) return;
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.BATTERY_LEVEL_FIELD_NUMBER, null);
-
-        uploadConfig(config);
-
-        Thread.sleep(WAIT_TIME_LONG);
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_LONG);
-
-        List<Atom> data = getGaugeMetricDataList();
-
-        assertThat(data).isNotEmpty();
-        Atom atom = data.get(0);
-        assertThat(atom.getBatteryLevel().hasBatteryLevel()).isTrue();
-        if (hasBattery()) {
-            assertThat(atom.getBatteryLevel().getBatteryLevel()).isIn(Range.openClosed(0, 100));
-        }
-    }
-
-    // This test is for the pulled battery charge count atom.
-    public void testBatteryCycleCount() throws Exception {
-        if (!hasFeature(FEATURE_WATCH, false)) return;
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.BATTERY_CYCLE_COUNT_FIELD_NUMBER, null);
-
-        uploadConfig(config);
-
-        Thread.sleep(WAIT_TIME_LONG);
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_LONG);
-
-        List<Atom> data = getGaugeMetricDataList();
-
-        assertThat(data).isNotEmpty();
-        Atom atom = data.get(0);
-        assertThat(atom.getBatteryCycleCount().hasCycleCount()).isTrue();
-        if (hasBattery()) {
-            assertThat(atom.getBatteryCycleCount().getCycleCount()).isAtLeast(0);
-        }
-    }
-
-    public void testKernelWakelock() throws Exception {
-        if (!kernelWakelockStatsExist()) {
-            return;
-        }
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.KERNEL_WAKELOCK_FIELD_NUMBER, null);
-
-        uploadConfig(config);
-
-        Thread.sleep(WAIT_TIME_LONG);
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_LONG);
-
-        List<Atom> data = getGaugeMetricDataList();
-
-        assertThat(data).isNotEmpty();
-        for (Atom atom : data) {
-            assertThat(atom.getKernelWakelock().hasName()).isTrue();
-            assertThat(atom.getKernelWakelock().hasCount()).isTrue();
-            assertThat(atom.getKernelWakelock().hasVersion()).isTrue();
-            assertThat(atom.getKernelWakelock().getVersion()).isGreaterThan(0);
-            assertThat(atom.getKernelWakelock().hasTimeMicros()).isTrue();
-        }
-    }
-
-    // Returns true iff either |WAKE_LOCK_FILE| or |WAKE_SOURCES_FILE| exists.
-    private boolean kernelWakelockStatsExist() {
-      try {
-        return doesFileExist(WAKE_LOCK_FILE) || doesFileExist(WAKE_SOURCES_FILE);
-      } catch(Exception e) {
-        return false;
-      }
-    }
-
-    public void testWifiActivityInfo() throws Exception {
-        if (!hasFeature(FEATURE_WIFI, true)) return;
-        if (!hasFeature(FEATURE_WATCH, false)) return;
-        if (!checkDeviceFor("checkWifiEnhancedPowerReportingSupported")) return;
-
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.WIFI_ACTIVITY_INFO_FIELD_NUMBER, null);
-
-        uploadConfig(config);
-
-        Thread.sleep(WAIT_TIME_LONG);
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_LONG);
-
-        List<Atom> dataList = getGaugeMetricDataList();
-
-        for (Atom atom: dataList) {
-            assertThat(atom.getWifiActivityInfo().getTimestampMillis()).isGreaterThan(0L);
-            assertThat(atom.getWifiActivityInfo().getStackState()).isAtLeast(0);
-            assertThat(atom.getWifiActivityInfo().getControllerIdleTimeMillis()).isGreaterThan(0L);
-            assertThat(atom.getWifiActivityInfo().getControllerTxTimeMillis()).isAtLeast(0L);
-            assertThat(atom.getWifiActivityInfo().getControllerRxTimeMillis()).isAtLeast(0L);
-            assertThat(atom.getWifiActivityInfo().getControllerEnergyUsed()).isAtLeast(0L);
-        }
-    }
-
-    public void testBuildInformation() throws Exception {
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.BUILD_INFORMATION_FIELD_NUMBER, null);
-        uploadConfig(config);
-
-        Thread.sleep(WAIT_TIME_LONG);
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_LONG);
-
-        List<Atom> data = getGaugeMetricDataList();
-        assertThat(data).isNotEmpty();
-        BuildInformation atom = data.get(0).getBuildInformation();
-        assertThat(getProperty("ro.product.brand")).isEqualTo(atom.getBrand());
-        assertThat(getProperty("ro.product.name")).isEqualTo(atom.getProduct());
-        assertThat(getProperty("ro.product.device")).isEqualTo(atom.getDevice());
-        assertThat(getProperty("ro.build.version.release_or_codename")).isEqualTo(atom.getVersionRelease());
-        assertThat(getProperty("ro.build.id")).isEqualTo(atom.getId());
-        assertThat(getProperty("ro.build.version.incremental"))
-            .isEqualTo(atom.getVersionIncremental());
-        assertThat(getProperty("ro.build.type")).isEqualTo(atom.getType());
-        assertThat(getProperty("ro.build.tags")).isEqualTo(atom.getTags());
-    }
-
-    public void testOnDevicePowerMeasurement() throws Exception {
-        if (!OPTIONAL_TESTS_ENABLED) return;
-
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.ON_DEVICE_POWER_MEASUREMENT_FIELD_NUMBER, null);
-
-        uploadConfig(config);
-
-        Thread.sleep(WAIT_TIME_LONG);
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_LONG);
-
-        List<Atom> dataList = getGaugeMetricDataList();
-
-        for (Atom atom: dataList) {
-            assertThat(atom.getOnDevicePowerMeasurement().getMeasurementTimestampMillis())
-                .isAtLeast(0L);
-            assertThat(atom.getOnDevicePowerMeasurement().getEnergyMicrowattSecs()).isAtLeast(0L);
-        }
-    }
-
-    // Explicitly tests if the adb command to log a breadcrumb is working.
-    public void testBreadcrumbAdb() throws Exception {
-        final int atomTag = Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER;
-        createAndUploadConfig(atomTag);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        doAppBreadcrumbReportedStart(1);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        List<EventMetricData> data = getEventMetricDataList();
-        AppBreadcrumbReported atom = data.get(0).getAtom().getAppBreadcrumbReported();
-        assertThat(atom.getLabel()).isEqualTo(1);
-        assertThat(atom.getState().getNumber()).isEqualTo(AppBreadcrumbReported.State.START_VALUE);
-    }
-
-    // Test dumpsys stats --proto.
-    public void testDumpsysStats() throws Exception {
-        final int atomTag = Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER;
-        createAndUploadConfig(atomTag);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        doAppBreadcrumbReportedStart(1);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Get the stats incident section.
-        List<ConfigMetricsReportList> listList = getReportsFromStatsDataDumpProto();
-        assertThat(listList).isNotEmpty();
-
-        // Extract the relevent report from the incident section.
-        ConfigMetricsReportList ourList = null;
-        int hostUid = getHostUid();
-        for (ConfigMetricsReportList list : listList) {
-            ConfigMetricsReportList.ConfigKey configKey = list.getConfigKey();
-            if (configKey.getUid() == hostUid && configKey.getId() == CONFIG_ID) {
-                ourList = list;
-                break;
-            }
-        }
-        assertWithMessage(String.format("Could not find list for uid=%d id=%d", hostUid, CONFIG_ID))
-            .that(ourList).isNotNull();
-
-        // Make sure that the report is correct.
-        List<EventMetricData> data = getEventMetricDataList(ourList);
-        AppBreadcrumbReported atom = data.get(0).getAtom().getAppBreadcrumbReported();
-        assertThat(atom.getLabel()).isEqualTo(1);
-        assertThat(atom.getState().getNumber()).isEqualTo(AppBreadcrumbReported.State.START_VALUE);
-    }
-
-    public void testConnectivityStateChange() throws Exception {
-        if (!hasFeature(FEATURE_WIFI, true)) return;
-        if (!hasFeature(FEATURE_WATCH, false)) return;
-        if (!hasFeature(FEATURE_LEANBACK_ONLY, false)) return;
-
-        final int atomTag = Atom.CONNECTIVITY_STATE_CHANGED_FIELD_NUMBER;
-        createAndUploadConfig(atomTag);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        turnOnAirplaneMode();
-        // wait long enough for airplane mode events to propagate.
-        Thread.sleep(1_200);
-        turnOffAirplaneMode();
-        // wait long enough for the device to restore connection
-        Thread.sleep(13_000);
-
-        List<EventMetricData> data = getEventMetricDataList();
-        // at least 1 disconnect and 1 connect
-        assertThat(data.size()).isAtLeast(2);
-        boolean foundDisconnectEvent = false;
-        boolean foundConnectEvent = false;
-        for (EventMetricData d : data) {
-            ConnectivityStateChanged atom = d.getAtom().getConnectivityStateChanged();
-            if(atom.getState().getNumber()
-                    == ConnectivityStateChanged.State.DISCONNECTED_VALUE) {
-                foundDisconnectEvent = true;
-            }
-            if(atom.getState().getNumber()
-                    == ConnectivityStateChanged.State.CONNECTED_VALUE) {
-                foundConnectEvent = true;
-            }
-        }
-        assertThat(foundConnectEvent).isTrue();
-        assertThat(foundDisconnectEvent).isTrue();
-    }
-
-    public void testSimSlotState() throws Exception {
-        if (!hasFeature(FEATURE_TELEPHONY, true)) {
-            return;
-        }
-
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.SIM_SLOT_STATE_FIELD_NUMBER, null);
-        uploadConfig(config);
-
-        Thread.sleep(WAIT_TIME_LONG);
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_LONG);
-
-        List<Atom> data = getGaugeMetricDataList();
-        assertThat(data).isNotEmpty();
-        SimSlotState atom = data.get(0).getSimSlotState();
-        // NOTE: it is possible for devices with telephony support to have no SIM at all
-        assertThat(atom.getActiveSlotCount()).isEqualTo(getActiveSimSlotCount());
-        assertThat(atom.getSimCount()).isAtMost(getActiveSimCountUpperBound());
-        assertThat(atom.getEsimCount()).isAtMost(getActiveEsimCountUpperBound());
-        // Above assertions do no necessarily enforce the following, since some are upper bounds
-        assertThat(atom.getActiveSlotCount()).isAtLeast(atom.getSimCount());
-        assertThat(atom.getSimCount()).isAtLeast(atom.getEsimCount());
-        assertThat(atom.getEsimCount()).isAtLeast(0);
-        // For GSM phones, at least one slot should be active even if there is no card
-        if (hasGsmPhone()) {
-            assertThat(atom.getActiveSlotCount()).isAtLeast(1);
-        }
-    }
-
-    public void testSupportedRadioAccessFamily() throws Exception {
-        if (!hasFeature(FEATURE_TELEPHONY, true)) {
-            return;
-        }
-
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.SUPPORTED_RADIO_ACCESS_FAMILY_FIELD_NUMBER, null);
-        uploadConfig(config);
-
-        Thread.sleep(WAIT_TIME_LONG);
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_LONG);
-
-        List<Atom> data = getGaugeMetricDataList();
-        assertThat(data).isNotEmpty();
-        SupportedRadioAccessFamily atom = data.get(0).getSupportedRadioAccessFamily();
-        if (hasGsmPhone()) {
-            assertThat(atom.getNetworkTypeBitmask() & NETWORK_TYPE_BITMASK_GSM_ALL)
-                    .isNotEqualTo(0L);
-        }
-        if (hasCdmaPhone()) {
-            assertThat(atom.getNetworkTypeBitmask() & NETWORK_TYPE_BITMASK_CDMA_ALL)
-                    .isNotEqualTo(0L);
-        }
-    }
-}
diff --git a/tests/src/android/cts/statsd/atom/ProcStateAtomTests.java b/tests/src/android/cts/statsd/atom/ProcStateAtomTests.java
deleted file mode 100644
index 230a516..0000000
--- a/tests/src/android/cts/statsd/atom/ProcStateAtomTests.java
+++ /dev/null
@@ -1,247 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.cts.statsd.atom;
-
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import android.app.ProcessStateEnum; // From enums.proto for atoms.proto's UidProcessStateChanged.
-
-import com.android.os.AtomsProto.Atom;
-import com.android.os.StatsLog.EventMetricData;
-
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-import java.util.stream.Stream;
-
-/**
- * Statsd atom tests that are done via app, for atoms that report a uid.
- */
-public class ProcStateAtomTests extends ProcStateTestCase {
-
-    private static final String TAG = "Statsd.ProcStateAtomTests";
-
-    private static final int WAIT_TIME_FOR_CONFIG_UPDATE_MS = 200;
-    // ActivityManager can take a while to register screen state changes, mandating an extra delay.
-    private static final int WAIT_TIME_FOR_CONFIG_AND_SCREEN_MS = 1_000;
-    private static final int EXTRA_WAIT_TIME_MS = 5_000; // as buffer when proc state changing.
-    private static final int STATSD_REPORT_WAIT_TIME_MS = 500; // make sure statsd finishes log.
-
-    private static final String FEATURE_WATCH = "android.hardware.type.watch";
-
-    // The tests here are using the BatteryStats definition of 'background'.
-    private static final Set<Integer> BG_STATES = new HashSet<>(
-            Arrays.asList(
-                    ProcessStateEnum.PROCESS_STATE_IMPORTANT_BACKGROUND_VALUE,
-                    ProcessStateEnum.PROCESS_STATE_TRANSIENT_BACKGROUND_VALUE,
-                    ProcessStateEnum.PROCESS_STATE_BACKUP_VALUE,
-                    ProcessStateEnum.PROCESS_STATE_SERVICE_VALUE,
-                    ProcessStateEnum.PROCESS_STATE_RECEIVER_VALUE,
-                    ProcessStateEnum.PROCESS_STATE_HEAVY_WEIGHT_VALUE
-            ));
-
-    // Using the BatteryStats definition of 'cached', which is why HOME (etc) are considered cached.
-    private static final Set<Integer> CACHED_STATES = new HashSet<>(
-            Arrays.asList(
-                    ProcessStateEnum.PROCESS_STATE_HOME_VALUE,
-                    ProcessStateEnum.PROCESS_STATE_LAST_ACTIVITY_VALUE,
-                    ProcessStateEnum.PROCESS_STATE_CACHED_ACTIVITY_VALUE,
-                    ProcessStateEnum.PROCESS_STATE_CACHED_ACTIVITY_CLIENT_VALUE,
-                    ProcessStateEnum.PROCESS_STATE_CACHED_RECENT_VALUE,
-                    ProcessStateEnum.PROCESS_STATE_CACHED_EMPTY_VALUE
-            ));
-
-    private static final Set<Integer> MISC_STATES = new HashSet<>(
-            Arrays.asList(
-                    ProcessStateEnum.PROCESS_STATE_PERSISTENT_VALUE, // TODO: untested
-                    ProcessStateEnum.PROCESS_STATE_PERSISTENT_UI_VALUE, // TODO: untested
-                    ProcessStateEnum.PROCESS_STATE_TOP_VALUE,
-                    ProcessStateEnum.PROCESS_STATE_BOUND_TOP_VALUE, // TODO: untested
-                    ProcessStateEnum.PROCESS_STATE_BOUND_FOREGROUND_SERVICE_VALUE, // TODO: untested
-                    ProcessStateEnum.PROCESS_STATE_FOREGROUND_SERVICE_VALUE,
-                    ProcessStateEnum.PROCESS_STATE_IMPORTANT_FOREGROUND_VALUE,
-                    ProcessStateEnum.PROCESS_STATE_TOP_SLEEPING_VALUE,
-
-                    ProcessStateEnum.PROCESS_STATE_UNKNOWN_VALUE,
-                    ProcessStateEnum.PROCESS_STATE_NONEXISTENT_VALUE
-            ));
-
-    private static final Set<Integer> ALL_STATES = Stream.of(MISC_STATES, CACHED_STATES, BG_STATES)
-            .flatMap(s -> s.stream()).collect(Collectors.toSet());
-
-    private static final Function<Atom, Integer> PROC_STATE_FUNCTION =
-            atom -> atom.getUidProcessStateChanged().getState().getNumber();
-
-    private static final int PROC_STATE_ATOM_TAG = Atom.UID_PROCESS_STATE_CHANGED_FIELD_NUMBER;
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-    }
-
-    public void testForegroundService() throws Exception {
-        Set<Integer> onStates = new HashSet<>(Arrays.asList(
-                ProcessStateEnum.PROCESS_STATE_FOREGROUND_SERVICE_VALUE));
-        Set<Integer> offStates = complement(onStates);
-
-        List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
-        createAndUploadConfig(PROC_STATE_ATOM_TAG, false);  // False: does not use attribution.
-        Thread.sleep(WAIT_TIME_FOR_CONFIG_UPDATE_MS);
-
-        executeForegroundService();
-        final int waitTime = SLEEP_OF_FOREGROUND_SERVICE;
-        Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS + EXTRA_WAIT_TIME_MS);
-
-        List<EventMetricData> data = getEventMetricDataList();
-        popUntilFind(data, onStates, PROC_STATE_FUNCTION); // clear out initial proc states.
-        assertStatesOccurred(stateSet, data, waitTime, PROC_STATE_FUNCTION);
-    }
-
-    public void testForeground() throws Exception {
-        Set<Integer> onStates = new HashSet<>(Arrays.asList(
-                ProcessStateEnum.PROCESS_STATE_IMPORTANT_FOREGROUND_VALUE));
-        // There are no offStates, since the app remains in foreground until killed.
-
-        List<Set<Integer>> stateSet = Arrays.asList(onStates); // state sets, in order
-        createAndUploadConfig(PROC_STATE_ATOM_TAG, false);  // False: does not use attribution.
-
-        Thread.sleep(WAIT_TIME_FOR_CONFIG_AND_SCREEN_MS);
-
-        executeForegroundActivity(ACTION_SHOW_APPLICATION_OVERLAY);
-        final int waitTime = EXTRA_WAIT_TIME_MS + 5_000; // Overlay may need to sit there a while.
-        Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS);
-
-        List<EventMetricData> data = getEventMetricDataList();
-        popUntilFind(data, onStates, PROC_STATE_FUNCTION); // clear out initial proc states.
-        assertStatesOccurred(stateSet, data, 0, PROC_STATE_FUNCTION);
-    }
-
-    public void testBackground() throws Exception {
-        Set<Integer> onStates = BG_STATES;
-        Set<Integer> offStates = complement(onStates);
-
-        List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
-        createAndUploadConfig(PROC_STATE_ATOM_TAG, false);  // False: does not use attribution.
-        Thread.sleep(WAIT_TIME_FOR_CONFIG_UPDATE_MS);
-
-        executeBackgroundService(ACTION_BACKGROUND_SLEEP);
-        final int waitTime = SLEEP_OF_ACTION_BACKGROUND_SLEEP;
-        Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS + EXTRA_WAIT_TIME_MS);
-
-        List<EventMetricData> data = getEventMetricDataList();
-        popUntilFind(data, onStates, PROC_STATE_FUNCTION); // clear out initial proc states.
-        assertStatesOccurred(stateSet, data, waitTime, PROC_STATE_FUNCTION);
-    }
-
-    public void testTop() throws Exception {
-        Set<Integer> onStates = new HashSet<>(Arrays.asList(
-                ProcessStateEnum.PROCESS_STATE_TOP_VALUE));
-        Set<Integer> offStates = complement(onStates);
-
-        List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
-        createAndUploadConfig(PROC_STATE_ATOM_TAG, false);  // False: does not use attribution.
-
-        Thread.sleep(WAIT_TIME_FOR_CONFIG_AND_SCREEN_MS);
-
-        executeForegroundActivity(ACTION_SLEEP_WHILE_TOP);
-        final int waitTime = SLEEP_OF_ACTION_SLEEP_WHILE_TOP;
-        Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS + EXTRA_WAIT_TIME_MS);
-
-        List<EventMetricData> data = getEventMetricDataList();
-        popUntilFind(data, onStates, PROC_STATE_FUNCTION); // clear out initial proc states.
-        assertStatesOccurred(stateSet, data, waitTime, PROC_STATE_FUNCTION);
-    }
-
-    public void testTopSleeping() throws Exception {
-        if (!hasFeature(FEATURE_WATCH, false)) return;
-        Set<Integer> onStates = new HashSet<>(Arrays.asList(
-                ProcessStateEnum.PROCESS_STATE_TOP_SLEEPING_VALUE));
-        Set<Integer> offStates = complement(onStates);
-
-        List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
-        createAndUploadConfig(PROC_STATE_ATOM_TAG, false);  //False: does not use attribution.
-
-        turnScreenOn();
-        Thread.sleep(WAIT_TIME_FOR_CONFIG_AND_SCREEN_MS);
-
-        executeForegroundActivity(ACTION_SLEEP_WHILE_TOP);
-        // ASAP, turn off the screen to make proc state -> top_sleeping.
-        turnScreenOff();
-        final int waitTime = SLEEP_OF_ACTION_SLEEP_WHILE_TOP + EXTRA_WAIT_TIME_MS;
-        Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS);
-
-        List<EventMetricData> data = getEventMetricDataList();
-        popUntilFind(data, new HashSet<>(Arrays.asList(ProcessStateEnum.PROCESS_STATE_TOP_VALUE)),
-                PROC_STATE_FUNCTION); // clear out anything prior to it entering TOP.
-        popUntilFind(data, onStates, PROC_STATE_FUNCTION); // clear out TOP itself.
-        // reset screen back on
-        turnScreenOn();
-        // Don't check the wait time, since it's up to the system how long top sleeping persists.
-        assertStatesOccurred(stateSet, data, 0, PROC_STATE_FUNCTION);
-    }
-
-    public void testCached() throws Exception {
-        Set<Integer> onStates = CACHED_STATES;
-        Set<Integer> offStates = complement(onStates);
-
-        List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
-        createAndUploadConfig(PROC_STATE_ATOM_TAG, false);  // False: des not use attribution.
-        Thread.sleep(WAIT_TIME_FOR_CONFIG_UPDATE_MS);
-
-        // The schedule is as follows
-        // #1. The system may do anything it wants, such as moving the app into a cache state.
-        // #2. We move the app into the background.
-        // #3. The background process ends, so the app definitely moves to a cache state
-        //          (this is the ultimate goal of the test).
-        // #4. We start a foreground activity, moving the app out of cache.
-
-        // Start extremely short-lived activity, so app goes into cache state (#1 - #3 above).
-        executeBackgroundService(ACTION_END_IMMEDIATELY);
-        final int cacheTime = 2_000; // process should be in cached state for up to this long
-        Thread.sleep(cacheTime);
-        // Now forcibly bring the app out of cache (#4 above).
-        executeForegroundActivity(ACTION_SHOW_APPLICATION_OVERLAY);
-        // Now check the data *before* the app enters cache again (to avoid another cache event).
-
-        List<EventMetricData> data = getEventMetricDataList();
-        // First, clear out any incidental cached states of step #1, prior to step #2.
-        popUntilFind(data, BG_STATES, PROC_STATE_FUNCTION);
-        // Now clear out the bg state from step #2 (since we are interested in the cache after it).
-        popUntilFind(data, onStates, PROC_STATE_FUNCTION);
-        // The result is that data should start at step #3, definitively in a cached state.
-        assertStatesOccurred(stateSet, data, 1_000, PROC_STATE_FUNCTION);
-    }
-
-    public void testValidityOfStates() throws Exception {
-        assertWithMessage("UNKNOWN_TO_PROTO should not be a valid state")
-            .that(ALL_STATES).doesNotContain(ProcessStateEnum.PROCESS_STATE_UNKNOWN_TO_PROTO_VALUE);
-    }
-
-    /** Returns the a set containing elements of a that are not elements of b. */
-    private Set<Integer> difference(Set<Integer> a, Set<Integer> b) {
-        Set<Integer> result = new HashSet<Integer>(a);
-        result.removeAll(b);
-        return result;
-    }
-
-    /** Returns the set of all states that are not in set. */
-    private Set<Integer> complement(Set<Integer> set) {
-        return difference(ALL_STATES, set);
-    }
-}
diff --git a/tests/src/android/cts/statsd/atom/UidAtomTests.java b/tests/src/android/cts/statsd/atom/UidAtomTests.java
deleted file mode 100644
index f41bd86..0000000
--- a/tests/src/android/cts/statsd/atom/UidAtomTests.java
+++ /dev/null
@@ -1,2254 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package android.cts.statsd.atom;
-
-import static com.android.os.AtomsProto.IntegrityCheckResultReported.Response.ALLOWED;
-import static com.google.common.truth.Truth.assertThat;
-import static com.google.common.truth.Truth.assertWithMessage;
-
-import android.app.AppOpEnum;
-import android.net.wifi.WifiModeEnum;
-import android.os.WakeLockLevelEnum;
-import android.server.ErrorSource;
-import android.telephony.NetworkTypeEnum;
-import com.android.compatibility.common.tradefed.build.CompatibilityBuildHelper;
-import com.android.compatibility.common.util.PropertyUtil;
-import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
-import com.android.internal.os.StatsdConfigProto.StatsdConfig;
-import com.android.os.AtomsProto;
-import com.android.os.AtomsProto.ANROccurred;
-import com.android.os.AtomsProto.AppBreadcrumbReported;
-import com.android.os.AtomsProto.AppCrashOccurred;
-import com.android.os.AtomsProto.AppOps;
-import com.android.os.AtomsProto.AppStartOccurred;
-import com.android.os.AtomsProto.AppUsageEventOccurred;
-import com.android.os.AtomsProto.Atom;
-import com.android.os.AtomsProto.AttributedAppOps;
-import com.android.os.AtomsProto.AttributionNode;
-import com.android.os.AtomsProto.AudioStateChanged;
-import com.android.os.AtomsProto.BinderCalls;
-import com.android.os.AtomsProto.BleScanResultReceived;
-import com.android.os.AtomsProto.BleScanStateChanged;
-import com.android.os.AtomsProto.BlobCommitted;
-import com.android.os.AtomsProto.BlobLeased;
-import com.android.os.AtomsProto.BlobOpened;
-import com.android.os.AtomsProto.CameraStateChanged;
-import com.android.os.AtomsProto.DangerousPermissionState;
-import com.android.os.AtomsProto.DangerousPermissionStateSampled;
-import com.android.os.AtomsProto.DeviceCalculatedPowerBlameUid;
-import com.android.os.AtomsProto.FlashlightStateChanged;
-import com.android.os.AtomsProto.ForegroundServiceAppOpSessionEnded;
-import com.android.os.AtomsProto.ForegroundServiceStateChanged;
-import com.android.os.AtomsProto.GpsScanStateChanged;
-import com.android.os.AtomsProto.HiddenApiUsed;
-import com.android.os.AtomsProto.IntegrityCheckResultReported;
-import com.android.os.AtomsProto.IonHeapSize;
-import com.android.os.AtomsProto.LmkKillOccurred;
-import com.android.os.AtomsProto.LooperStats;
-import com.android.os.AtomsProto.MediaCodecStateChanged;
-import com.android.os.AtomsProto.NotificationReported;
-import com.android.os.AtomsProto.OverlayStateChanged;
-import com.android.os.AtomsProto.PackageNotificationChannelGroupPreferences;
-import com.android.os.AtomsProto.PackageNotificationChannelPreferences;
-import com.android.os.AtomsProto.PackageNotificationPreferences;
-import com.android.os.AtomsProto.PictureInPictureStateChanged;
-import com.android.os.AtomsProto.ProcessMemoryHighWaterMark;
-import com.android.os.AtomsProto.ProcessMemorySnapshot;
-import com.android.os.AtomsProto.ProcessMemoryState;
-import com.android.os.AtomsProto.ScheduledJobStateChanged;
-import com.android.os.AtomsProto.SettingSnapshot;
-import com.android.os.AtomsProto.SyncStateChanged;
-import com.android.os.AtomsProto.TestAtomReported;
-import com.android.os.AtomsProto.UiEventReported;
-import com.android.os.AtomsProto.VibratorStateChanged;
-import com.android.os.AtomsProto.WakelockStateChanged;
-import com.android.os.AtomsProto.WakeupAlarmOccurred;
-import com.android.os.AtomsProto.WifiLockStateChanged;
-import com.android.os.AtomsProto.WifiMulticastLockStateChanged;
-import com.android.os.AtomsProto.WifiScanStateChanged;
-import com.android.os.StatsLog.EventMetricData;
-import com.android.server.notification.SmallHash;
-import com.android.tradefed.log.LogUtil;
-import com.google.common.collect.Range;
-import com.google.protobuf.Descriptors;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-/**
- * Statsd atom tests that are done via app, for atoms that report a uid.
- */
-public class UidAtomTests extends DeviceAtomTestCase {
-
-    private static final String TAG = "Statsd.UidAtomTests";
-
-    private static final String TEST_PACKAGE_NAME = "com.android.server.cts.device.statsd";
-
-    private static final boolean DAVEY_ENABLED = false;
-
-    private static final int NUM_APP_OPS = AttributedAppOps.getDefaultInstance().getOp().
-            getDescriptorForType().getValues().size() - 1;
-
-    private static final String TEST_INSTALL_APK = "CtsStatsdEmptyApp.apk";
-    private static final String TEST_INSTALL_APK_BASE = "CtsStatsdEmptySplitApp.apk";
-    private static final String TEST_INSTALL_APK_SPLIT = "CtsStatsdEmptySplitApp_pl.apk";
-    private static final String TEST_INSTALL_PACKAGE =
-            "com.android.cts.device.statsd.emptyapp";
-    private static final String TEST_REMOTE_DIR = "/data/local/tmp/statsd";
-    private static final String ACTION_SHOW_APPLICATION_OVERLAY = "action.show_application_overlay";
-    private static final String ACTION_LONG_SLEEP_WHILE_TOP = "action.long_sleep_top";
-
-    private static final int WAIT_TIME_FOR_CONFIG_UPDATE_MS = 200;
-    private static final int EXTRA_WAIT_TIME_MS = 5_000; // as buffer when app starting/stopping.
-    private static final int STATSD_REPORT_WAIT_TIME_MS = 500; // make sure statsd finishes log.
-
-    @Override
-    protected void setUp() throws Exception {
-        super.setUp();
-    }
-
-    @Override
-    protected void tearDown() throws Exception {
-        resetBatteryStatus();
-        super.tearDown();
-    }
-
-    public void testLmkKillOccurred() throws Exception {
-        if (!"true".equals(getProperty("ro.lmk.log_stats"))) {
-            return;
-        }
-
-        final int atomTag = Atom.LMK_KILL_OCCURRED_FIELD_NUMBER;
-        createAndUploadConfig(atomTag, false);
-
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        executeBackgroundService(ACTION_LMK);
-        Thread.sleep(15_000);
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        assertThat(data).hasSize(1);
-        assertThat(data.get(0).getAtom().hasLmkKillOccurred()).isTrue();
-        LmkKillOccurred atom = data.get(0).getAtom().getLmkKillOccurred();
-        assertThat(atom.getUid()).isEqualTo(getUid());
-        assertThat(atom.getProcessName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
-        assertThat(atom.getOomAdjScore()).isAtLeast(500);
-    }
-
-    public void testAppCrashOccurred() throws Exception {
-        final int atomTag = Atom.APP_CRASH_OCCURRED_FIELD_NUMBER;
-        createAndUploadConfig(atomTag, false);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        runActivity("StatsdCtsForegroundActivity", "action", "action.crash");
-
-        Thread.sleep(WAIT_TIME_SHORT);
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        AppCrashOccurred atom = data.get(0).getAtom().getAppCrashOccurred();
-        assertThat(atom.getEventType()).isEqualTo("crash");
-        assertThat(atom.getIsInstantApp().getNumber())
-            .isEqualTo(AppCrashOccurred.InstantApp.FALSE_VALUE);
-        assertThat(atom.getForegroundState().getNumber())
-            .isEqualTo(AppCrashOccurred.ForegroundState.FOREGROUND_VALUE);
-        assertThat(atom.getPackageName()).isEqualTo(TEST_PACKAGE_NAME);
-    }
-
-    public void testAppStartOccurred() throws Exception {
-        final int atomTag = Atom.APP_START_OCCURRED_FIELD_NUMBER;
-
-        createAndUploadConfig(atomTag, false);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        runActivity("StatsdCtsForegroundActivity", "action", "action.sleep_top", 3_500);
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        assertThat(data).hasSize(1);
-        AppStartOccurred atom = data.get(0).getAtom().getAppStartOccurred();
-        assertThat(atom.getPkgName()).isEqualTo(TEST_PACKAGE_NAME);
-        assertThat(atom.getActivityName())
-            .isEqualTo("com.android.server.cts.device.statsd.StatsdCtsForegroundActivity");
-        assertThat(atom.getIsInstantApp()).isFalse();
-        assertThat(atom.getActivityStartMillis()).isGreaterThan(0L);
-        assertThat(atom.getTransitionDelayMillis()).isGreaterThan(0);
-    }
-
-    public void testAudioState() throws Exception {
-        if (!hasFeature(FEATURE_AUDIO_OUTPUT, true)) return;
-
-        final int atomTag = Atom.AUDIO_STATE_CHANGED_FIELD_NUMBER;
-        final String name = "testAudioState";
-
-        Set<Integer> onState = new HashSet<>(
-                Arrays.asList(AudioStateChanged.State.ON_VALUE));
-        Set<Integer> offState = new HashSet<>(
-                Arrays.asList(AudioStateChanged.State.OFF_VALUE));
-
-        // Add state sets to the list in order.
-        List<Set<Integer>> stateSet = Arrays.asList(onState, offState);
-
-        createAndUploadConfig(atomTag, true);  // True: uses attribution.
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", name);
-
-        Thread.sleep(WAIT_TIME_SHORT);
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        // Because the timestamp is truncated, we skip checking time differences between state
-        // changes.
-        assertStatesOccurred(stateSet, data, 0,
-                atom -> atom.getAudioStateChanged().getState().getNumber());
-
-        // Check that timestamp is truncated
-        for (EventMetricData metric : data) {
-            long elapsedTimestampNs = metric.getElapsedTimestampNanos();
-            assertTimestampIsTruncated(elapsedTimestampNs);
-        }
-    }
-
-    public void testBleScan() throws Exception {
-        if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return;
-
-        final int atom = Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER;
-        final int field = BleScanStateChanged.STATE_FIELD_NUMBER;
-        final int stateOn = BleScanStateChanged.State.ON_VALUE;
-        final int stateOff = BleScanStateChanged.State.OFF_VALUE;
-        final int minTimeDiffMillis = 1_500;
-        final int maxTimeDiffMillis = 3_000;
-
-        List<EventMetricData> data = doDeviceMethodOnOff("testBleScanUnoptimized", atom, field,
-                stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, true);
-
-        BleScanStateChanged a0 = data.get(0).getAtom().getBleScanStateChanged();
-        BleScanStateChanged a1 = data.get(1).getAtom().getBleScanStateChanged();
-        assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
-        assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
-    }
-
-    public void testBleUnoptimizedScan() throws Exception {
-        if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return;
-
-        final int atom = Atom.BLE_SCAN_STATE_CHANGED_FIELD_NUMBER;
-        final int field = BleScanStateChanged.STATE_FIELD_NUMBER;
-        final int stateOn = BleScanStateChanged.State.ON_VALUE;
-        final int stateOff = BleScanStateChanged.State.OFF_VALUE;
-        final int minTimeDiffMillis = 1_500;
-        final int maxTimeDiffMillis = 3_000;
-
-        List<EventMetricData> data = doDeviceMethodOnOff("testBleScanUnoptimized", atom, field,
-                stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, true);
-
-        BleScanStateChanged a0 = data.get(0).getAtom().getBleScanStateChanged();
-        assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
-        assertThat(a0.getIsFiltered()).isFalse();
-        assertThat(a0.getIsFirstMatch()).isFalse();
-        assertThat(a0.getIsOpportunistic()).isFalse();
-        BleScanStateChanged a1 = data.get(1).getAtom().getBleScanStateChanged();
-        assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
-        assertThat(a1.getIsFiltered()).isFalse();
-        assertThat(a1.getIsFirstMatch()).isFalse();
-        assertThat(a1.getIsOpportunistic()).isFalse();
-
-
-        // Now repeat the test for opportunistic scanning and make sure it is reported correctly.
-        data = doDeviceMethodOnOff("testBleScanOpportunistic", atom, field,
-                stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, true);
-
-        a0 = data.get(0).getAtom().getBleScanStateChanged();
-        assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
-        assertThat(a0.getIsFiltered()).isFalse();
-        assertThat(a0.getIsFirstMatch()).isFalse();
-        assertThat(a0.getIsOpportunistic()).isTrue();  // This scan is opportunistic.
-        a1 = data.get(1).getAtom().getBleScanStateChanged();
-        assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
-        assertThat(a1.getIsFiltered()).isFalse();
-        assertThat(a1.getIsFirstMatch()).isFalse();
-        assertThat(a1.getIsOpportunistic()).isTrue();
-    }
-
-    public void testBleScanResult() throws Exception {
-        if (!hasFeature(FEATURE_BLUETOOTH_LE, true)) return;
-
-        final int atom = Atom.BLE_SCAN_RESULT_RECEIVED_FIELD_NUMBER;
-        final int field = BleScanResultReceived.NUM_RESULTS_FIELD_NUMBER;
-
-        StatsdConfig.Builder conf = createConfigBuilder();
-        addAtomEvent(conf, atom, createFvm(field).setGteInt(0));
-        List<EventMetricData> data = doDeviceMethod("testBleScanResult", conf);
-
-        assertThat(data.size()).isAtLeast(1);
-        BleScanResultReceived a0 = data.get(0).getAtom().getBleScanResultReceived();
-        assertThat(a0.getNumResults()).isAtLeast(1);
-    }
-
-    public void testHiddenApiUsed() throws Exception {
-        String oldRate = getDevice().executeShellCommand(
-                "device_config get app_compat hidden_api_access_statslog_sampling_rate").trim();
-
-        getDevice().executeShellCommand(
-                "device_config put app_compat hidden_api_access_statslog_sampling_rate 65536");
-
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        try {
-            final int atomTag = Atom.HIDDEN_API_USED_FIELD_NUMBER;
-
-            createAndUploadConfig(atomTag, false);
-
-            runActivity("HiddenApiUsedActivity", null, null, 2_500);
-
-            List<EventMetricData> data = getEventMetricDataList();
-            assertThat(data).hasSize(1);
-
-            HiddenApiUsed atom = data.get(0).getAtom().getHiddenApiUsed();
-
-            int uid = getUid();
-            assertThat(atom.getUid()).isEqualTo(uid);
-            assertThat(atom.getAccessDenied()).isFalse();
-            assertThat(atom.getSignature())
-                .isEqualTo("Landroid/app/Activity;->mWindow:Landroid/view/Window;");
-        } finally {
-            if (!oldRate.equals("null")) {
-                getDevice().executeShellCommand(
-                        "device_config put app_compat hidden_api_access_statslog_sampling_rate "
-                        + oldRate);
-            } else {
-                getDevice().executeShellCommand(
-                        "device_config delete hidden_api_access_statslog_sampling_rate");
-            }
-        }
-    }
-
-    public void testCameraState() throws Exception {
-        if (!hasFeature(FEATURE_CAMERA, true) && !hasFeature(FEATURE_CAMERA_FRONT, true)) return;
-
-        final int atomTag = Atom.CAMERA_STATE_CHANGED_FIELD_NUMBER;
-        Set<Integer> cameraOn = new HashSet<>(Arrays.asList(CameraStateChanged.State.ON_VALUE));
-        Set<Integer> cameraOff = new HashSet<>(Arrays.asList(CameraStateChanged.State.OFF_VALUE));
-
-        // Add state sets to the list in order.
-        List<Set<Integer>> stateSet = Arrays.asList(cameraOn, cameraOff);
-
-        createAndUploadConfig(atomTag, true);  // True: uses attribution.
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testCameraState");
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        // Assert that the events happened in the expected order.
-        assertStatesOccurred(stateSet, data, WAIT_TIME_LONG,
-                atom -> atom.getCameraStateChanged().getState().getNumber());
-    }
-
-    public void testCpuTimePerUid() throws Exception {
-        if (!hasFeature(FEATURE_WATCH, false)) return;
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.CPU_TIME_PER_UID_FIELD_NUMBER, null);
-
-        uploadConfig(config);
-
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testSimpleCpu");
-
-        Thread.sleep(WAIT_TIME_SHORT);
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_LONG);
-
-        List<Atom> atomList = getGaugeMetricDataList();
-
-        // TODO: We don't have atom matching on gauge yet. Let's refactor this after that feature is
-        // implemented.
-        boolean found = false;
-        int uid = getUid();
-        for (Atom atom : atomList) {
-            if (atom.getCpuTimePerUid().getUid() == uid) {
-                found = true;
-                assertThat(atom.getCpuTimePerUid().getUserTimeMicros()).isGreaterThan(0L);
-                assertThat(atom.getCpuTimePerUid().getSysTimeMicros()).isGreaterThan(0L);
-            }
-        }
-        assertWithMessage(String.format("did not find uid %d", uid)).that(found).isTrue();
-    }
-
-    public void testDeviceCalculatedPowerUse() throws Exception {
-        if (!hasFeature(FEATURE_LEANBACK_ONLY, false)) return;
-
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.DEVICE_CALCULATED_POWER_USE_FIELD_NUMBER, null);
-        uploadConfig(config);
-        unplugDevice();
-
-        Thread.sleep(WAIT_TIME_LONG);
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testSimpleCpu");
-        Thread.sleep(WAIT_TIME_SHORT);
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_LONG);
-
-        Atom atom = getGaugeMetricDataList().get(0);
-        assertThat(atom.getDeviceCalculatedPowerUse().getComputedPowerNanoAmpSecs())
-            .isGreaterThan(0L);
-    }
-
-
-    public void testDeviceCalculatedPowerBlameUid() throws Exception {
-        if (!hasFeature(FEATURE_LEANBACK_ONLY, false)) return;
-        if (!hasBattery()) {
-            return;
-        }
-
-        String kernelVersion = getDevice().executeShellCommand("uname -r");
-        if (kernelVersion.contains("3.18")) {
-            LogUtil.CLog.d("Skipping calculated power blame uid test.");
-            return;
-        }
-
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config,
-                Atom.DEVICE_CALCULATED_POWER_BLAME_UID_FIELD_NUMBER, null);
-        uploadConfig(config);
-        unplugDevice();
-
-        Thread.sleep(WAIT_TIME_LONG);
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testSimpleCpu");
-        Thread.sleep(WAIT_TIME_SHORT);
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_LONG);
-
-        List<Atom> atomList = getGaugeMetricDataList();
-        boolean uidFound = false;
-        int uid = getUid();
-        long uidPower = 0;
-        for (Atom atom : atomList) {
-            DeviceCalculatedPowerBlameUid item = atom.getDeviceCalculatedPowerBlameUid();
-                if (item.getUid() == uid) {
-                assertWithMessage(String.format("Found multiple power values for uid %d", uid))
-                    .that(uidFound).isFalse();
-                uidFound = true;
-                uidPower = item.getPowerNanoAmpSecs();
-            }
-        }
-        assertWithMessage(String.format("No power value for uid %d", uid)).that(uidFound).isTrue();
-        assertWithMessage(String.format("Non-positive power value for uid %d", uid))
-            .that(uidPower).isGreaterThan(0L);
-    }
-
-    public void testDavey() throws Exception {
-        if (!DAVEY_ENABLED ) return;
-        long MAX_DURATION = 2000;
-        long MIN_DURATION = 750;
-        final int atomTag = Atom.DAVEY_OCCURRED_FIELD_NUMBER;
-        createAndUploadConfig(atomTag, false); // UID is logged without attribution node
-
-        runActivity("DaveyActivity", null, null);
-
-        List<EventMetricData> data = getEventMetricDataList();
-        assertThat(data).hasSize(1);
-        long duration = data.get(0).getAtom().getDaveyOccurred().getJankDurationMillis();
-        assertWithMessage("Incorrect jank duration")
-            .that(duration).isIn(Range.closed(MIN_DURATION, MAX_DURATION));
-    }
-
-    public void testFlashlightState() throws Exception {
-        if (!hasFeature(FEATURE_CAMERA_FLASH, true)) return;
-
-        final int atomTag = Atom.FLASHLIGHT_STATE_CHANGED_FIELD_NUMBER;
-        final String name = "testFlashlight";
-
-        Set<Integer> flashlightOn = new HashSet<>(
-            Arrays.asList(FlashlightStateChanged.State.ON_VALUE));
-        Set<Integer> flashlightOff = new HashSet<>(
-            Arrays.asList(FlashlightStateChanged.State.OFF_VALUE));
-
-        // Add state sets to the list in order.
-        List<Set<Integer>> stateSet = Arrays.asList(flashlightOn, flashlightOff);
-
-        createAndUploadConfig(atomTag, true);  // True: uses attribution.
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", name);
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        // Assert that the events happened in the expected order.
-        assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
-                atom -> atom.getFlashlightStateChanged().getState().getNumber());
-    }
-
-    public void testForegroundServiceState() throws Exception {
-        final int atomTag = Atom.FOREGROUND_SERVICE_STATE_CHANGED_FIELD_NUMBER;
-        final String name = "testForegroundService";
-
-        Set<Integer> enterForeground = new HashSet<>(
-                Arrays.asList(ForegroundServiceStateChanged.State.ENTER_VALUE));
-        Set<Integer> exitForeground = new HashSet<>(
-                Arrays.asList(ForegroundServiceStateChanged.State.EXIT_VALUE));
-
-        // Add state sets to the list in order.
-        List<Set<Integer>> stateSet = Arrays.asList(enterForeground, exitForeground);
-
-        createAndUploadConfig(atomTag, false);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", name);
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        // Assert that the events happened in the expected order.
-        assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
-                atom -> atom.getForegroundServiceStateChanged().getState().getNumber());
-    }
-
-
-    public void testForegroundServiceAccessAppOp() throws Exception {
-        final int atomTag = Atom.FOREGROUND_SERVICE_APP_OP_SESSION_ENDED_FIELD_NUMBER;
-        final String name = "testForegroundServiceAccessAppOp";
-
-        createAndUploadConfig(atomTag, false);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", name);
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        assertWithMessage("Wrong atom size").that(data.size()).isEqualTo(3);
-        for (int i = 0; i < data.size(); i++) {
-            ForegroundServiceAppOpSessionEnded atom
-                    = data.get(i).getAtom().getForegroundServiceAppOpSessionEnded();
-            final int opName = atom.getAppOpName().getNumber();
-            final int acceptances = atom.getCountOpsAccepted();
-            final int rejections = atom.getCountOpsRejected();
-            final int count = acceptances + rejections;
-            int expectedCount = 0;
-            switch (opName) {
-                case AppOpEnum.APP_OP_CAMERA_VALUE:
-                    expectedCount = 3;
-                    break;
-                case AppOpEnum.APP_OP_FINE_LOCATION_VALUE:
-                    expectedCount = 1;
-                    break;
-                case AppOpEnum.APP_OP_RECORD_AUDIO_VALUE:
-                    expectedCount = 2;
-                    break;
-                case AppOpEnum.APP_OP_COARSE_LOCATION_VALUE:
-                    // fall-through
-                default:
-                    fail("Unexpected opName " + opName);
-            }
-            assertWithMessage("Wrong count for " + opName).that(count).isEqualTo(expectedCount);
-        }
-    }
-
-    public void testGpsScan() throws Exception {
-        if (!hasFeature(FEATURE_LOCATION_GPS, true)) return;
-        // Whitelist this app against background location request throttling
-        String origWhitelist = getDevice().executeShellCommand(
-                "settings get global location_background_throttle_package_whitelist").trim();
-        getDevice().executeShellCommand(String.format(
-                "settings put global location_background_throttle_package_whitelist %s",
-                DEVICE_SIDE_TEST_PACKAGE));
-
-        try {
-            final int atom = Atom.GPS_SCAN_STATE_CHANGED_FIELD_NUMBER;
-            final int key = GpsScanStateChanged.STATE_FIELD_NUMBER;
-            final int stateOn = GpsScanStateChanged.State.ON_VALUE;
-            final int stateOff = GpsScanStateChanged.State.OFF_VALUE;
-            final int minTimeDiffMillis = 500;
-            final int maxTimeDiffMillis = 60_000;
-
-            List<EventMetricData> data = doDeviceMethodOnOff("testGpsScan", atom, key,
-                    stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, true);
-
-            GpsScanStateChanged a0 = data.get(0).getAtom().getGpsScanStateChanged();
-            GpsScanStateChanged a1 = data.get(1).getAtom().getGpsScanStateChanged();
-            assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
-            assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
-        } finally {
-            if ("null".equals(origWhitelist) || "".equals(origWhitelist)) {
-                getDevice().executeShellCommand(
-                        "settings delete global location_background_throttle_package_whitelist");
-            } else {
-                getDevice().executeShellCommand(String.format(
-                        "settings put global location_background_throttle_package_whitelist %s",
-                        origWhitelist));
-            }
-        }
-    }
-
-    public void testGnssStats() throws Exception {
-        // Get GnssMetrics as a simple gauge metric.
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.GNSS_STATS_FIELD_NUMBER, null);
-        uploadConfig(config);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        if (!hasFeature(FEATURE_LOCATION_GPS, true)) return;
-        // Whitelist this app against background location request throttling
-        String origWhitelist = getDevice().executeShellCommand(
-                "settings get global location_background_throttle_package_whitelist").trim();
-        getDevice().executeShellCommand(String.format(
-                "settings put global location_background_throttle_package_whitelist %s",
-                DEVICE_SIDE_TEST_PACKAGE));
-
-        try {
-            runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testGpsStatus");
-
-            Thread.sleep(WAIT_TIME_LONG);
-            // Trigger a pull and wait for new pull before killing the process.
-            setAppBreadcrumbPredicate();
-            Thread.sleep(WAIT_TIME_LONG);
-
-            // Assert about GnssMetrics for the test app.
-            List<Atom> atoms = getGaugeMetricDataList();
-
-            boolean found = false;
-            for (Atom atom : atoms) {
-                AtomsProto.GnssStats state = atom.getGnssStats();
-                found = true;
-                if ((state.getSvStatusReports() > 0 || state.getL5SvStatusReports() > 0)
-                        && state.getLocationReports() == 0) {
-                    // Device is detected to be indoors and not able to acquire location.
-                    // flaky test device
-                    break;
-                }
-                assertThat(state.getLocationReports()).isGreaterThan((long) 0);
-                assertThat(state.getLocationFailureReports()).isAtLeast((long) 0);
-                assertThat(state.getTimeToFirstFixReports()).isGreaterThan((long) 0);
-                assertThat(state.getTimeToFirstFixMillis()).isGreaterThan((long) 0);
-                assertThat(state.getPositionAccuracyReports()).isGreaterThan((long) 0);
-                assertThat(state.getPositionAccuracyMeters()).isGreaterThan((long) 0);
-                assertThat(state.getTopFourAverageCn0Reports()).isGreaterThan((long) 0);
-                assertThat(state.getTopFourAverageCn0DbMhz()).isGreaterThan((long) 0);
-                assertThat(state.getL5TopFourAverageCn0Reports()).isAtLeast((long) 0);
-                assertThat(state.getL5TopFourAverageCn0DbMhz()).isAtLeast((long) 0);
-                assertThat(state.getSvStatusReports()).isAtLeast((long) 0);
-                assertThat(state.getSvStatusReportsUsedInFix()).isAtLeast((long) 0);
-                assertThat(state.getL5SvStatusReports()).isAtLeast((long) 0);
-                assertThat(state.getL5SvStatusReportsUsedInFix()).isAtLeast((long) 0);
-            }
-            assertWithMessage(String.format("Did not find a matching atom"))
-                    .that(found).isTrue();
-        } finally {
-            if ("null".equals(origWhitelist) || "".equals(origWhitelist)) {
-                getDevice().executeShellCommand(
-                        "settings delete global location_background_throttle_package_whitelist");
-            } else {
-                getDevice().executeShellCommand(String.format(
-                        "settings put global location_background_throttle_package_whitelist %s",
-                        origWhitelist));
-            }
-        }
-    }
-
-    public void testMediaCodecActivity() throws Exception {
-        if (!hasFeature(FEATURE_WATCH, false)) return;
-        final int atomTag = Atom.MEDIA_CODEC_STATE_CHANGED_FIELD_NUMBER;
-
-        // 5 seconds. Starting video tends to be much slower than most other
-        // tests on slow devices. This is unfortunate, because it leaves a
-        // really big slop in assertStatesOccurred.  It would be better if
-        // assertStatesOccurred had a tighter range on large timeouts.
-        final int waitTime = 5000;
-
-        // From {@link VideoPlayerActivity#DELAY_MILLIS}
-        final int videoDuration = 2000;
-
-        Set<Integer> onState = new HashSet<>(
-                Arrays.asList(MediaCodecStateChanged.State.ON_VALUE));
-        Set<Integer> offState = new HashSet<>(
-                Arrays.asList(MediaCodecStateChanged.State.OFF_VALUE));
-
-        // Add state sets to the list in order.
-        List<Set<Integer>> stateSet = Arrays.asList(onState, offState);
-
-        createAndUploadConfig(atomTag, true);  // True: uses attribution.
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        runActivity("VideoPlayerActivity", "action", "action.play_video",
-            waitTime);
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        // Assert that the events happened in the expected order.
-        assertStatesOccurred(stateSet, data, videoDuration,
-                atom -> atom.getMediaCodecStateChanged().getState().getNumber());
-    }
-
-    public void testOverlayState() throws Exception {
-        if (!hasFeature(FEATURE_WATCH, false)) return;
-        final int atomTag = Atom.OVERLAY_STATE_CHANGED_FIELD_NUMBER;
-
-        Set<Integer> entered = new HashSet<>(
-                Arrays.asList(OverlayStateChanged.State.ENTERED_VALUE));
-        Set<Integer> exited = new HashSet<>(
-                Arrays.asList(OverlayStateChanged.State.EXITED_VALUE));
-
-        // Add state sets to the list in order.
-        List<Set<Integer>> stateSet = Arrays.asList(entered, exited);
-
-        createAndUploadConfig(atomTag, false);
-
-        runActivity("StatsdCtsForegroundActivity", "action", "action.show_application_overlay",
-                5_000);
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        // Assert that the events happened in the expected order.
-        // The overlay box should appear about 2sec after the app start
-        assertStatesOccurred(stateSet, data, 0,
-                atom -> atom.getOverlayStateChanged().getState().getNumber());
-    }
-
-    public void testPictureInPictureState() throws Exception {
-        String supported = getDevice().executeShellCommand("am supports-multiwindow");
-        if (!hasFeature(FEATURE_WATCH, false) ||
-                !hasFeature(FEATURE_PICTURE_IN_PICTURE, true) ||
-                !supported.contains("true")) {
-            LogUtil.CLog.d("Skipping picture in picture atom test.");
-            return;
-        }
-
-        StatsdConfig.Builder conf = createConfigBuilder();
-        // PictureInPictureStateChanged atom is used prior to rvc-qpr
-        addAtomEvent(conf, Atom.PICTURE_IN_PICTURE_STATE_CHANGED_FIELD_NUMBER,
-                /*useAttribution=*/false);
-        // Picture-in-picture logs' been migrated to UiEvent since rvc-qpr
-        FieldValueMatcher.Builder pkgMatcher = createFvm(UiEventReported.PACKAGE_NAME_FIELD_NUMBER)
-                .setEqString(DEVICE_SIDE_TEST_PACKAGE);
-        addAtomEvent(conf, Atom.UI_EVENT_REPORTED_FIELD_NUMBER, Arrays.asList(pkgMatcher));
-        uploadConfig(conf);
-
-        LogUtil.CLog.d("Playing video in Picture-in-Picture mode");
-        runActivity("VideoPlayerActivity", "action", "action.play_video_picture_in_picture_mode");
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        // Filter out the PictureInPictureStateChanged and UiEventReported atom
-        List<EventMetricData> pictureInPictureStateChangedData = data.stream()
-                .filter(e -> e.getAtom().hasPictureInPictureStateChanged())
-                .collect(Collectors.toList());
-        List<EventMetricData> uiEventReportedData = data.stream()
-                .filter(e -> e.getAtom().hasUiEventReported())
-                .collect(Collectors.toList());
-
-        if (!pictureInPictureStateChangedData.isEmpty()) {
-            LogUtil.CLog.d("Assert using PictureInPictureStateChanged");
-            Set<Integer> entered = new HashSet<>(
-                    Arrays.asList(PictureInPictureStateChanged.State.ENTERED_VALUE));
-            List<Set<Integer>> stateSet = Arrays.asList(entered);
-            assertStatesOccurred(stateSet, data, WAIT_TIME_LONG,
-                    atom -> atom.getPictureInPictureStateChanged().getState().getNumber());
-        } else if (!uiEventReportedData.isEmpty()) {
-            LogUtil.CLog.d("Assert using UiEventReported");
-            // See PipUiEventEnum for definitions
-            final int enterPipEventId = 603;
-            // Assert that log for entering PiP happens exactly once, we do not use
-            // assertStateOccurred here since PiP may log something else when activity finishes.
-            List<EventMetricData> entered = uiEventReportedData.stream()
-                    .filter(e -> e.getAtom().getUiEventReported().getEventId() == enterPipEventId)
-                    .collect(Collectors.toList());
-            assertThat(entered).hasSize(1);
-        } else {
-            fail("No logging event from PictureInPictureStateChanged nor UiEventReported");
-        }
-    }
-
-    public void testScheduledJobState() throws Exception {
-        String expectedName = "com.android.server.cts.device.statsd/.StatsdJobService";
-        final int atomTag = Atom.SCHEDULED_JOB_STATE_CHANGED_FIELD_NUMBER;
-        Set<Integer> jobSchedule = new HashSet<>(
-                Arrays.asList(ScheduledJobStateChanged.State.SCHEDULED_VALUE));
-        Set<Integer> jobOn = new HashSet<>(
-                Arrays.asList(ScheduledJobStateChanged.State.STARTED_VALUE));
-        Set<Integer> jobOff = new HashSet<>(
-                Arrays.asList(ScheduledJobStateChanged.State.FINISHED_VALUE));
-
-        // Add state sets to the list in order.
-        List<Set<Integer>> stateSet = Arrays.asList(jobSchedule, jobOn, jobOff);
-
-        createAndUploadConfig(atomTag, true);  // True: uses attribution.
-        allowImmediateSyncs();
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testScheduledJob");
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        assertStatesOccurred(stateSet, data, 0,
-                atom -> atom.getScheduledJobStateChanged().getState().getNumber());
-
-        for (EventMetricData e : data) {
-            assertThat(e.getAtom().getScheduledJobStateChanged().getJobName())
-                .isEqualTo(expectedName);
-        }
-    }
-
-    //Note: this test does not have uid, but must run on the device
-    public void testScreenBrightness() throws Exception {
-        int initialBrightness = getScreenBrightness();
-        boolean isInitialManual = isScreenBrightnessModeManual();
-        setScreenBrightnessMode(true);
-        setScreenBrightness(200);
-        Thread.sleep(WAIT_TIME_LONG);
-
-        final int atomTag = Atom.SCREEN_BRIGHTNESS_CHANGED_FIELD_NUMBER;
-
-        Set<Integer> screenMin = new HashSet<>(Arrays.asList(47));
-        Set<Integer> screen100 = new HashSet<>(Arrays.asList(100));
-        Set<Integer> screen200 = new HashSet<>(Arrays.asList(198));
-        // Set<Integer> screenMax = new HashSet<>(Arrays.asList(255));
-
-        // Add state sets to the list in order.
-        List<Set<Integer>> stateSet = Arrays.asList(screenMin, screen100, screen200);
-
-        createAndUploadConfig(atomTag);
-        Thread.sleep(WAIT_TIME_SHORT);
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testScreenBrightness");
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        // Restore initial screen brightness
-        setScreenBrightness(initialBrightness);
-        setScreenBrightnessMode(isInitialManual);
-
-        popUntilFind(data, screenMin, atom->atom.getScreenBrightnessChanged().getLevel());
-        popUntilFindFromEnd(data, screen200, atom->atom.getScreenBrightnessChanged().getLevel());
-        // Assert that the events happened in the expected order.
-        assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
-            atom -> atom.getScreenBrightnessChanged().getLevel());
-    }
-    public void testSyncState() throws Exception {
-        final int atomTag = Atom.SYNC_STATE_CHANGED_FIELD_NUMBER;
-        Set<Integer> syncOn = new HashSet<>(Arrays.asList(SyncStateChanged.State.ON_VALUE));
-        Set<Integer> syncOff = new HashSet<>(Arrays.asList(SyncStateChanged.State.OFF_VALUE));
-
-        // Add state sets to the list in order.
-        List<Set<Integer>> stateSet = Arrays.asList(syncOn, syncOff, syncOn, syncOff);
-
-        createAndUploadConfig(atomTag, true);
-        allowImmediateSyncs();
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testSyncState");
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        // Assert that the events happened in the expected order.
-        assertStatesOccurred(stateSet, data,
-            /* wait = */ 0 /* don't verify time differences between state changes */,
-            atom -> atom.getSyncStateChanged().getState().getNumber());
-    }
-
-    public void testVibratorState() throws Exception {
-        if (!checkDeviceFor("checkVibratorSupported")) return;
-
-        final int atomTag = Atom.VIBRATOR_STATE_CHANGED_FIELD_NUMBER;
-        final String name = "testVibratorState";
-
-        Set<Integer> onState = new HashSet<>(
-                Arrays.asList(VibratorStateChanged.State.ON_VALUE));
-        Set<Integer> offState = new HashSet<>(
-                Arrays.asList(VibratorStateChanged.State.OFF_VALUE));
-
-        // Add state sets to the list in order.
-        List<Set<Integer>> stateSet = Arrays.asList(onState, offState);
-
-        createAndUploadConfig(atomTag, true);  // True: uses attribution.
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", name);
-
-        Thread.sleep(WAIT_TIME_LONG);
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        assertStatesOccurred(stateSet, data, 300,
-                atom -> atom.getVibratorStateChanged().getState().getNumber());
-    }
-
-    public void testWakelockState() throws Exception {
-        final int atomTag = Atom.WAKELOCK_STATE_CHANGED_FIELD_NUMBER;
-        Set<Integer> wakelockOn = new HashSet<>(Arrays.asList(
-                WakelockStateChanged.State.ACQUIRE_VALUE,
-                WakelockStateChanged.State.CHANGE_ACQUIRE_VALUE));
-        Set<Integer> wakelockOff = new HashSet<>(Arrays.asList(
-                WakelockStateChanged.State.RELEASE_VALUE,
-                WakelockStateChanged.State.CHANGE_RELEASE_VALUE));
-
-        final String EXPECTED_TAG = "StatsdPartialWakelock";
-        final WakeLockLevelEnum EXPECTED_LEVEL = WakeLockLevelEnum.PARTIAL_WAKE_LOCK;
-
-        // Add state sets to the list in order.
-        List<Set<Integer>> stateSet = Arrays.asList(wakelockOn, wakelockOff);
-
-        createAndUploadConfig(atomTag, true);  // True: uses attribution.
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testWakelockState");
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        // Assert that the events happened in the expected order.
-        assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
-            atom -> atom.getWakelockStateChanged().getState().getNumber());
-
-        for (EventMetricData event: data) {
-            String tag = event.getAtom().getWakelockStateChanged().getTag();
-            WakeLockLevelEnum type = event.getAtom().getWakelockStateChanged().getType();
-            assertThat(tag).isEqualTo(EXPECTED_TAG);
-            assertThat(type).isEqualTo(EXPECTED_LEVEL);
-        }
-    }
-
-    public void testWakeupAlarm() throws Exception {
-        // For automotive, all wakeup alarm becomes normal alarm. So this
-        // test does not work.
-        if (!hasFeature(FEATURE_AUTOMOTIVE, false)) return;
-        final int atomTag = Atom.WAKEUP_ALARM_OCCURRED_FIELD_NUMBER;
-
-        StatsdConfig.Builder config = createConfigBuilder();
-        addAtomEvent(config, atomTag, true);  // True: uses attribution.
-
-        List<EventMetricData> data = doDeviceMethod("testWakeupAlarm", config);
-        assertThat(data.size()).isAtLeast(1);
-        for (int i = 0; i < data.size(); i++) {
-            WakeupAlarmOccurred wao = data.get(i).getAtom().getWakeupAlarmOccurred();
-            assertThat(wao.getTag()).isEqualTo("*walarm*:android.cts.statsd.testWakeupAlarm");
-            assertThat(wao.getPackageName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
-        }
-    }
-
-    public void testWifiLockHighPerf() throws Exception {
-        if (!hasFeature(FEATURE_WIFI, true)) return;
-        if (!hasFeature(FEATURE_PC, false)) return;
-
-        final int atomTag = Atom.WIFI_LOCK_STATE_CHANGED_FIELD_NUMBER;
-        Set<Integer> lockOn = new HashSet<>(Arrays.asList(WifiLockStateChanged.State.ON_VALUE));
-        Set<Integer> lockOff = new HashSet<>(Arrays.asList(WifiLockStateChanged.State.OFF_VALUE));
-
-        // Add state sets to the list in order.
-        List<Set<Integer>> stateSet = Arrays.asList(lockOn, lockOff);
-
-        createAndUploadConfig(atomTag, true);  // True: uses attribution.
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testWifiLockHighPerf");
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        // Assert that the events happened in the expected order.
-        assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
-                atom -> atom.getWifiLockStateChanged().getState().getNumber());
-
-        for (EventMetricData event : data) {
-            assertThat(event.getAtom().getWifiLockStateChanged().getMode())
-                .isEqualTo(WifiModeEnum.WIFI_MODE_FULL_HIGH_PERF);
-        }
-    }
-
-    public void testWifiLockLowLatency() throws Exception {
-        if (!hasFeature(FEATURE_WIFI, true)) return;
-        if (!hasFeature(FEATURE_PC, false)) return;
-
-        final int atomTag = Atom.WIFI_LOCK_STATE_CHANGED_FIELD_NUMBER;
-        Set<Integer> lockOn = new HashSet<>(Arrays.asList(WifiLockStateChanged.State.ON_VALUE));
-        Set<Integer> lockOff = new HashSet<>(Arrays.asList(WifiLockStateChanged.State.OFF_VALUE));
-
-        // Add state sets to the list in order.
-        List<Set<Integer>> stateSet = Arrays.asList(lockOn, lockOff);
-
-        createAndUploadConfig(atomTag, true);  // True: uses attribution.
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testWifiLockLowLatency");
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        // Assert that the events happened in the expected order.
-        assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
-                atom -> atom.getWifiLockStateChanged().getState().getNumber());
-
-        for (EventMetricData event : data) {
-            assertThat(event.getAtom().getWifiLockStateChanged().getMode())
-                .isEqualTo(WifiModeEnum.WIFI_MODE_FULL_LOW_LATENCY);
-        }
-    }
-
-    public void testWifiMulticastLock() throws Exception {
-        if (!hasFeature(FEATURE_WIFI, true)) return;
-        if (!hasFeature(FEATURE_PC, false)) return;
-
-        final int atomTag = Atom.WIFI_MULTICAST_LOCK_STATE_CHANGED_FIELD_NUMBER;
-        Set<Integer> lockOn = new HashSet<>(
-                Arrays.asList(WifiMulticastLockStateChanged.State.ON_VALUE));
-        Set<Integer> lockOff = new HashSet<>(
-                Arrays.asList(WifiMulticastLockStateChanged.State.OFF_VALUE));
-
-        final String EXPECTED_TAG = "StatsdCTSMulticastLock";
-
-        // Add state sets to the list in order.
-        List<Set<Integer>> stateSet = Arrays.asList(lockOn, lockOff);
-
-        createAndUploadConfig(atomTag, true);
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testWifiMulticastLock");
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        // Assert that the events happened in the expected order.
-        assertStatesOccurred(stateSet, data, WAIT_TIME_SHORT,
-                atom -> atom.getWifiMulticastLockStateChanged().getState().getNumber());
-
-        for (EventMetricData event: data) {
-            String tag = event.getAtom().getWifiMulticastLockStateChanged().getTag();
-            assertThat(tag).isEqualTo(EXPECTED_TAG);
-        }
-    }
-
-    public void testWifiScan() throws Exception {
-        if (!hasFeature(FEATURE_WIFI, true)) return;
-
-        final int atom = Atom.WIFI_SCAN_STATE_CHANGED_FIELD_NUMBER;
-        final int key = WifiScanStateChanged.STATE_FIELD_NUMBER;
-        final int stateOn = WifiScanStateChanged.State.ON_VALUE;
-        final int stateOff = WifiScanStateChanged.State.OFF_VALUE;
-        final int minTimeDiffMillis = 250;
-        final int maxTimeDiffMillis = 60_000;
-        final boolean demandExactlyTwo = false; // Two scans are performed, so up to 4 atoms logged.
-
-        List<EventMetricData> data = doDeviceMethodOnOff("testWifiScan", atom, key,
-                stateOn, stateOff, minTimeDiffMillis, maxTimeDiffMillis, demandExactlyTwo);
-
-        assertThat(data.size()).isIn(Range.closed(2, 4));
-        WifiScanStateChanged a0 = data.get(0).getAtom().getWifiScanStateChanged();
-        WifiScanStateChanged a1 = data.get(1).getAtom().getWifiScanStateChanged();
-        assertThat(a0.getState().getNumber()).isEqualTo(stateOn);
-        assertThat(a1.getState().getNumber()).isEqualTo(stateOff);
-    }
-
-    public void testBinderStats() throws Exception {
-        try {
-            unplugDevice();
-            Thread.sleep(WAIT_TIME_SHORT);
-            enableBinderStats();
-            binderStatsNoSampling();
-            resetBinderStats();
-            StatsdConfig.Builder config = createConfigBuilder();
-            addGaugeAtomWithDimensions(config, Atom.BINDER_CALLS_FIELD_NUMBER, null);
-
-            uploadConfig(config);
-            Thread.sleep(WAIT_TIME_SHORT);
-
-            runActivity("StatsdCtsForegroundActivity", "action", "action.show_notification",3_000);
-
-            setAppBreadcrumbPredicate();
-            Thread.sleep(WAIT_TIME_SHORT);
-
-            boolean found = false;
-            int uid = getUid();
-            List<Atom> atomList = getGaugeMetricDataList();
-            for (Atom atom : atomList) {
-                BinderCalls calls = atom.getBinderCalls();
-                boolean classMatches = calls.getServiceClassName().contains(
-                        "com.android.server.notification.NotificationManagerService");
-                boolean methodMatches = calls.getServiceMethodName()
-                        .equals("createNotificationChannels");
-
-                if (calls.getUid() == uid && classMatches && methodMatches) {
-                    found = true;
-                    assertThat(calls.getRecordedCallCount()).isGreaterThan(0L);
-                    assertThat(calls.getCallCount()).isGreaterThan(0L);
-                    assertThat(calls.getRecordedTotalLatencyMicros())
-                        .isIn(Range.open(0L, 1000000L));
-                    assertThat(calls.getRecordedTotalCpuMicros()).isIn(Range.open(0L, 1000000L));
-                }
-            }
-
-            assertWithMessage(String.format("Did not find a matching atom for uid %d", uid))
-                .that(found).isTrue();
-
-        } finally {
-            disableBinderStats();
-            plugInAc();
-        }
-    }
-
-    public void testLooperStats() throws Exception {
-        try {
-            unplugDevice();
-            setUpLooperStats();
-            StatsdConfig.Builder config = createConfigBuilder();
-            addGaugeAtomWithDimensions(config, Atom.LOOPER_STATS_FIELD_NUMBER, null);
-            uploadConfig(config);
-            Thread.sleep(WAIT_TIME_SHORT);
-
-            runActivity("StatsdCtsForegroundActivity", "action", "action.show_notification", 3_000);
-
-            setAppBreadcrumbPredicate();
-            Thread.sleep(WAIT_TIME_SHORT);
-
-            List<Atom> atomList = getGaugeMetricDataList();
-
-            boolean found = false;
-            int uid = getUid();
-            for (Atom atom : atomList) {
-                LooperStats stats = atom.getLooperStats();
-                String notificationServiceFullName =
-                        "com.android.server.notification.NotificationManagerService";
-                boolean handlerMatches =
-                        stats.getHandlerClassName().equals(
-                                notificationServiceFullName + "$WorkerHandler");
-                boolean messageMatches =
-                        stats.getMessageName().equals(
-                                notificationServiceFullName + "$EnqueueNotificationRunnable");
-                if (atom.getLooperStats().getUid() == uid && handlerMatches && messageMatches) {
-                    found = true;
-                    assertThat(stats.getMessageCount()).isGreaterThan(0L);
-                    assertThat(stats.getRecordedMessageCount()).isGreaterThan(0L);
-                    assertThat(stats.getRecordedTotalLatencyMicros())
-                        .isIn(Range.open(0L, 1000000L));
-                    assertThat(stats.getRecordedTotalCpuMicros()).isIn(Range.open(0L, 1000000L));
-                    assertThat(stats.getRecordedMaxLatencyMicros()).isIn(Range.open(0L, 1000000L));
-                    assertThat(stats.getRecordedMaxCpuMicros()).isIn(Range.open(0L, 1000000L));
-                    assertThat(stats.getRecordedDelayMessageCount()).isGreaterThan(0L);
-                    assertThat(stats.getRecordedTotalDelayMillis())
-                        .isIn(Range.closedOpen(0L, 5000L));
-                    assertThat(stats.getRecordedMaxDelayMillis()).isIn(Range.closedOpen(0L, 5000L));
-                }
-            }
-            assertWithMessage(String.format("Did not find a matching atom for uid %d", uid))
-                .that(found).isTrue();
-        } finally {
-            cleanUpLooperStats();
-            plugInAc();
-        }
-    }
-
-    public void testProcessMemoryState() throws Exception {
-        // Get ProcessMemoryState as a simple gauge metric.
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.PROCESS_MEMORY_STATE_FIELD_NUMBER, null);
-        uploadConfig(config);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Start test app.
-        try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
-                "action.show_notification")) {
-            Thread.sleep(WAIT_TIME_LONG);
-            // Trigger a pull and wait for new pull before killing the process.
-            setAppBreadcrumbPredicate();
-            Thread.sleep(WAIT_TIME_LONG);
-        }
-
-        // Assert about ProcessMemoryState for the test app.
-        List<Atom> atoms = getGaugeMetricDataList();
-        int uid = getUid();
-        boolean found = false;
-        for (Atom atom : atoms) {
-            ProcessMemoryState state = atom.getProcessMemoryState();
-            if (state.getUid() != uid) {
-                continue;
-            }
-            found = true;
-            assertThat(state.getProcessName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
-            assertThat(state.getOomAdjScore()).isAtLeast(0);
-            assertThat(state.getPageFault()).isAtLeast(0L);
-            assertThat(state.getPageMajorFault()).isAtLeast(0L);
-            assertThat(state.getRssInBytes()).isGreaterThan(0L);
-            assertThat(state.getCacheInBytes()).isAtLeast(0L);
-            assertThat(state.getSwapInBytes()).isAtLeast(0L);
-        }
-        assertWithMessage(String.format("Did not find a matching atom for uid %d", uid))
-            .that(found).isTrue();
-    }
-
-    public void testProcessMemoryHighWaterMark() throws Exception {
-        // Get ProcessMemoryHighWaterMark as a simple gauge metric.
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.PROCESS_MEMORY_HIGH_WATER_MARK_FIELD_NUMBER, null);
-        uploadConfig(config);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Start test app and trigger a pull while it is running.
-        try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
-                "action.show_notification")) {
-            Thread.sleep(WAIT_TIME_SHORT);
-            // Trigger a pull and wait for new pull before killing the process.
-            setAppBreadcrumbPredicate();
-            Thread.sleep(WAIT_TIME_LONG);
-        }
-
-        // Assert about ProcessMemoryHighWaterMark for the test app, statsd and system server.
-        List<Atom> atoms = getGaugeMetricDataList();
-        int uid = getUid();
-        boolean foundTestApp = false;
-        boolean foundStatsd = false;
-        boolean foundSystemServer = false;
-        for (Atom atom : atoms) {
-            ProcessMemoryHighWaterMark state = atom.getProcessMemoryHighWaterMark();
-            if (state.getUid() == uid) {
-                foundTestApp = true;
-                assertThat(state.getProcessName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
-                assertThat(state.getRssHighWaterMarkInBytes()).isGreaterThan(0L);
-            } else if (state.getProcessName().contains("/statsd")) {
-                foundStatsd = true;
-                assertThat(state.getRssHighWaterMarkInBytes()).isGreaterThan(0L);
-            } else if (state.getProcessName().equals("system")) {
-                foundSystemServer = true;
-                assertThat(state.getRssHighWaterMarkInBytes()).isGreaterThan(0L);
-            }
-        }
-        assertWithMessage(String.format("Did not find a matching atom for test app uid=%d",uid))
-            .that(foundTestApp).isTrue();
-        assertWithMessage("Did not find a matching atom for statsd").that(foundStatsd).isTrue();
-        assertWithMessage("Did not find a matching atom for system server")
-            .that(foundSystemServer).isTrue();
-    }
-
-    public void testProcessMemorySnapshot() throws Exception {
-        // Get ProcessMemorySnapshot as a simple gauge metric.
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.PROCESS_MEMORY_SNAPSHOT_FIELD_NUMBER, null);
-        uploadConfig(config);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Start test app and trigger a pull while it is running.
-        try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
-                "action.show_notification")) {
-            Thread.sleep(WAIT_TIME_LONG);
-            setAppBreadcrumbPredicate();
-        }
-
-        // Assert about ProcessMemorySnapshot for the test app, statsd and system server.
-        List<Atom> atoms = getGaugeMetricDataList();
-        int uid = getUid();
-        boolean foundTestApp = false;
-        boolean foundStatsd = false;
-        boolean foundSystemServer = false;
-        for (Atom atom : atoms) {
-          ProcessMemorySnapshot snapshot = atom.getProcessMemorySnapshot();
-          if (snapshot.getUid() == uid) {
-              foundTestApp = true;
-              assertThat(snapshot.getProcessName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
-          } else if (snapshot.getProcessName().contains("/statsd")) {
-              foundStatsd = true;
-          } else if (snapshot.getProcessName().equals("system")) {
-              foundSystemServer = true;
-          }
-
-          assertThat(snapshot.getPid()).isGreaterThan(0);
-          assertThat(snapshot.getAnonRssAndSwapInKilobytes()).isAtLeast(0);
-          assertThat(snapshot.getAnonRssAndSwapInKilobytes()).isEqualTo(
-                  snapshot.getAnonRssInKilobytes() + snapshot.getSwapInKilobytes());
-          assertThat(snapshot.getRssInKilobytes()).isAtLeast(0);
-          assertThat(snapshot.getAnonRssInKilobytes()).isAtLeast(0);
-          assertThat(snapshot.getSwapInKilobytes()).isAtLeast(0);
-        }
-        assertWithMessage(String.format("Did not find a matching atom for test app uid=%d",uid))
-            .that(foundTestApp).isTrue();
-        assertWithMessage("Did not find a matching atom for statsd").that(foundStatsd).isTrue();
-        assertWithMessage("Did not find a matching atom for system server")
-            .that(foundSystemServer).isTrue();
-    }
-
-    public void testIonHeapSize_optional() throws Exception {
-        if (isIonHeapSizeMandatory()) {
-            return;
-        }
-
-        List<Atom> atoms = pullIonHeapSizeAsGaugeMetric();
-        if (atoms.isEmpty()) {
-            // No support.
-            return;
-        }
-        assertIonHeapSize(atoms);
-    }
-
-    public void testIonHeapSize_mandatory() throws Exception {
-        if (!isIonHeapSizeMandatory()) {
-            return;
-        }
-
-        List<Atom> atoms = pullIonHeapSizeAsGaugeMetric();
-        assertIonHeapSize(atoms);
-    }
-
-    /** Returns whether IonHeapSize atom is supported. */
-    private boolean isIonHeapSizeMandatory() throws Exception {
-        // Support is guaranteed by libmeminfo VTS.
-        return PropertyUtil.getFirstApiLevel(getDevice()) >= 30;
-    }
-
-    /** Returns IonHeapSize atoms pulled as a simple gauge metric while test app is running. */
-    private List<Atom> pullIonHeapSizeAsGaugeMetric() throws Exception {
-        // Get IonHeapSize as a simple gauge metric.
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.ION_HEAP_SIZE_FIELD_NUMBER, null);
-        uploadConfig(config);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Start test app and trigger a pull while it is running.
-        try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
-                "action.show_notification")) {
-            setAppBreadcrumbPredicate();
-            Thread.sleep(WAIT_TIME_LONG);
-        }
-
-        return getGaugeMetricDataList();
-    }
-
-    private static void assertIonHeapSize(List<Atom> atoms) {
-        assertThat(atoms).hasSize(1);
-        IonHeapSize ionHeapSize = atoms.get(0).getIonHeapSize();
-        assertThat(ionHeapSize.getTotalSizeKb()).isAtLeast(0);
-    }
-
-    /**
-     * The the app id from a uid.
-     *
-     * @param uid The uid of the app
-     *
-     * @return The app id of the app
-     *
-     * @see android.os.UserHandle#getAppId
-     */
-    private static int getAppId(int uid) {
-        return uid % 100000;
-    }
-
-    public void testRoleHolder() throws Exception {
-        // Make device side test package a role holder
-        String callScreenAppRole = "android.app.role.CALL_SCREENING";
-        getDevice().executeShellCommand(
-                "cmd role add-role-holder " + callScreenAppRole + " " + DEVICE_SIDE_TEST_PACKAGE);
-
-        // Set up what to collect
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.ROLE_HOLDER_FIELD_NUMBER, null);
-        uploadConfig(config);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        boolean verifiedKnowRoleState = false;
-
-        // Pull a report
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        int testAppId = getAppId(getUid());
-
-        for (Atom atom : getGaugeMetricDataList()) {
-            AtomsProto.RoleHolder roleHolder = atom.getRoleHolder();
-
-            assertThat(roleHolder.getPackageName()).isNotNull();
-            assertThat(roleHolder.getUid()).isAtLeast(0);
-            assertThat(roleHolder.getRole()).isNotNull();
-
-            if (roleHolder.getPackageName().equals(DEVICE_SIDE_TEST_PACKAGE)) {
-                assertThat(getAppId(roleHolder.getUid())).isEqualTo(testAppId);
-                assertThat(roleHolder.getPackageName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
-                assertThat(roleHolder.getRole()).isEqualTo(callScreenAppRole);
-
-                verifiedKnowRoleState = true;
-            }
-        }
-
-        assertThat(verifiedKnowRoleState).isTrue();
-    }
-
-    public void testDangerousPermissionState() throws Exception {
-        final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED =  1 << 8;
-        final int FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED =  1 << 9;
-
-        // Set up what to collect
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.DANGEROUS_PERMISSION_STATE_FIELD_NUMBER, null);
-        uploadConfig(config);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        boolean verifiedKnowPermissionState = false;
-
-        // Pull a report
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        int testAppId = getAppId(getUid());
-
-        for (Atom atom : getGaugeMetricDataList()) {
-            DangerousPermissionState permissionState = atom.getDangerousPermissionState();
-
-            assertThat(permissionState.getPermissionName()).isNotNull();
-            assertThat(permissionState.getUid()).isAtLeast(0);
-            assertThat(permissionState.getPackageName()).isNotNull();
-
-            if (getAppId(permissionState.getUid()) == testAppId) {
-
-                if (permissionState.getPermissionName().contains(
-                        "ACCESS_FINE_LOCATION")) {
-                    assertThat(permissionState.getIsGranted()).isTrue();
-                    assertThat(permissionState.getPermissionFlags() & ~(
-                            FLAG_PERMISSION_USER_SENSITIVE_WHEN_DENIED
-                            | FLAG_PERMISSION_USER_SENSITIVE_WHEN_GRANTED))
-                        .isEqualTo(0);
-
-                    verifiedKnowPermissionState = true;
-                }
-            }
-        }
-
-        assertThat(verifiedKnowPermissionState).isTrue();
-    }
-
-    public void testDangerousPermissionStateSampled() throws Exception {
-        // get full atom for reference
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.DANGEROUS_PERMISSION_STATE_FIELD_NUMBER, null);
-        uploadConfig(config);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        List<DangerousPermissionState> fullDangerousPermissionState = new ArrayList<>();
-        for (Atom atom : getGaugeMetricDataList()) {
-            fullDangerousPermissionState.add(atom.getDangerousPermissionState());
-        }
-
-        removeConfig(CONFIG_ID);
-        getReportList(); // Clears data.
-        List<Atom> gaugeMetricDataList = null;
-
-        // retries in case sampling returns full list or empty list - which should be extremely rare
-        for (int attempt = 0; attempt < 10; attempt++) {
-            // Set up what to collect
-            config = createConfigBuilder();
-            addGaugeAtomWithDimensions(config, Atom.DANGEROUS_PERMISSION_STATE_SAMPLED_FIELD_NUMBER,
-                    null);
-            uploadConfig(config);
-            Thread.sleep(WAIT_TIME_SHORT);
-
-            // Pull a report
-            setAppBreadcrumbPredicate();
-            Thread.sleep(WAIT_TIME_SHORT);
-
-            gaugeMetricDataList = getGaugeMetricDataList();
-            if (gaugeMetricDataList.size() > 0
-                    && gaugeMetricDataList.size() < fullDangerousPermissionState.size()) {
-                break;
-            }
-            removeConfig(CONFIG_ID);
-            getReportList(); // Clears data.
-        }
-        assertThat(gaugeMetricDataList.size()).isGreaterThan(0);
-        assertThat(gaugeMetricDataList.size()).isLessThan(fullDangerousPermissionState.size());
-
-        long lastUid = -1;
-        int fullIndex = 0;
-
-        for (Atom atom : getGaugeMetricDataList()) {
-            DangerousPermissionStateSampled permissionState =
-                    atom.getDangerousPermissionStateSampled();
-
-            DangerousPermissionState referenceState = fullDangerousPermissionState.get(fullIndex);
-
-            if (referenceState.getUid() != permissionState.getUid()) {
-                // atoms are sampled on uid basis if uid is present, all related permissions must
-                // be logged.
-                assertThat(permissionState.getUid()).isNotEqualTo(lastUid);
-                continue;
-            }
-
-            lastUid = permissionState.getUid();
-
-            assertThat(permissionState.getPermissionFlags()).isEqualTo(
-                    referenceState.getPermissionFlags());
-            assertThat(permissionState.getIsGranted()).isEqualTo(referenceState.getIsGranted());
-            assertThat(permissionState.getPermissionName()).isEqualTo(
-                    referenceState.getPermissionName());
-
-            fullIndex++;
-        }
-    }
-
-    public void testAppOps() throws Exception {
-        // Set up what to collect
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.APP_OPS_FIELD_NUMBER, null);
-        uploadConfig(config);
-
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testAppOps");
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Pull a report
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        ArrayList<Integer> expectedOps = new ArrayList<>();
-        for (int i = 0; i < NUM_APP_OPS; i++) {
-            expectedOps.add(i);
-        }
-
-        for (Descriptors.EnumValueDescriptor valueDescriptor :
-                AttributedAppOps.getDefaultInstance().getOp().getDescriptorForType().getValues()) {
-            if (valueDescriptor.getOptions().hasDeprecated()) {
-                // Deprecated app op, remove from list of expected ones.
-                expectedOps.remove(expectedOps.indexOf(valueDescriptor.getNumber()));
-            }
-        }
-        for (Atom atom : getGaugeMetricDataList()) {
-
-            AppOps appOps = atom.getAppOps();
-            if (appOps.getPackageName().equals(TEST_PACKAGE_NAME)) {
-                if (appOps.getOpId().getNumber() == -1) {
-                    continue;
-                }
-                long totalNoted = appOps.getTrustedForegroundGrantedCount()
-                        + appOps.getTrustedBackgroundGrantedCount()
-                        + appOps.getTrustedForegroundRejectedCount()
-                        + appOps.getTrustedBackgroundRejectedCount();
-                assertWithMessage("Operation in APP_OPS_ENUM_MAP: " + appOps.getOpId().getNumber())
-                        .that(totalNoted - 1).isEqualTo(appOps.getOpId().getNumber());
-                assertWithMessage("Unexpected Op reported").that(expectedOps).contains(
-                        appOps.getOpId().getNumber());
-                expectedOps.remove(expectedOps.indexOf(appOps.getOpId().getNumber()));
-            }
-        }
-        assertWithMessage("Logging app op ids are missing in report.").that(expectedOps).isEmpty();
-    }
-
-    public void testANROccurred() throws Exception {
-        final int atomTag = Atom.ANR_OCCURRED_FIELD_NUMBER;
-        createAndUploadConfig(atomTag, false);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        try (AutoCloseable a = withActivity("ANRActivity", null, null)) {
-            Thread.sleep(WAIT_TIME_SHORT);
-            getDevice().executeShellCommand(
-                    "am broadcast -a action_anr -p " + DEVICE_SIDE_TEST_PACKAGE);
-            Thread.sleep(20_000);
-        }
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-
-        assertThat(data).hasSize(1);
-        assertThat(data.get(0).getAtom().hasAnrOccurred()).isTrue();
-        ANROccurred atom = data.get(0).getAtom().getAnrOccurred();
-        assertThat(atom.getIsInstantApp().getNumber())
-            .isEqualTo(ANROccurred.InstantApp.FALSE_VALUE);
-        assertThat(atom.getForegroundState().getNumber())
-            .isEqualTo(ANROccurred.ForegroundState.FOREGROUND_VALUE);
-        assertThat(atom.getErrorSource()).isEqualTo(ErrorSource.DATA_APP);
-        assertThat(atom.getPackageName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
-    }
-
-    public void testWriteRawTestAtom() throws Exception {
-        final int atomTag = Atom.TEST_ATOM_REPORTED_FIELD_NUMBER;
-        createAndUploadConfig(atomTag, true);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testWriteRawTestAtom");
-
-        Thread.sleep(WAIT_TIME_SHORT);
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-        assertThat(data).hasSize(4);
-
-        TestAtomReported atom = data.get(0).getAtom().getTestAtomReported();
-        List<AttributionNode> attrChain = atom.getAttributionNodeList();
-        assertThat(attrChain).hasSize(2);
-        assertThat(attrChain.get(0).getUid()).isEqualTo(1234);
-        assertThat(attrChain.get(0).getTag()).isEqualTo("tag1");
-        assertThat(attrChain.get(1).getUid()).isEqualTo(getUid());
-        assertThat(attrChain.get(1).getTag()).isEqualTo("tag2");
-
-        assertThat(atom.getIntField()).isEqualTo(42);
-        assertThat(atom.getLongField()).isEqualTo(Long.MAX_VALUE);
-        assertThat(atom.getFloatField()).isEqualTo(3.14f);
-        assertThat(atom.getStringField()).isEqualTo("This is a basic test!");
-        assertThat(atom.getBooleanField()).isFalse();
-        assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.ON_VALUE);
-        assertThat(atom.getBytesField().getExperimentIdList())
-            .containsExactly(1L, 2L, 3L).inOrder();
-
-
-        atom = data.get(1).getAtom().getTestAtomReported();
-        attrChain = atom.getAttributionNodeList();
-        assertThat(attrChain).hasSize(2);
-        assertThat(attrChain.get(0).getUid()).isEqualTo(9999);
-        assertThat(attrChain.get(0).getTag()).isEqualTo("tag9999");
-        assertThat(attrChain.get(1).getUid()).isEqualTo(getUid());
-        assertThat(attrChain.get(1).getTag()).isEmpty();
-
-        assertThat(atom.getIntField()).isEqualTo(100);
-        assertThat(atom.getLongField()).isEqualTo(Long.MIN_VALUE);
-        assertThat(atom.getFloatField()).isEqualTo(-2.5f);
-        assertThat(atom.getStringField()).isEqualTo("Test null uid");
-        assertThat(atom.getBooleanField()).isTrue();
-        assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.UNKNOWN_VALUE);
-        assertThat(atom.getBytesField().getExperimentIdList())
-            .containsExactly(1L, 2L, 3L).inOrder();
-
-        atom = data.get(2).getAtom().getTestAtomReported();
-        attrChain = atom.getAttributionNodeList();
-        assertThat(attrChain).hasSize(1);
-        assertThat(attrChain.get(0).getUid()).isEqualTo(getUid());
-        assertThat(attrChain.get(0).getTag()).isEqualTo("tag1");
-
-        assertThat(atom.getIntField()).isEqualTo(-256);
-        assertThat(atom.getLongField()).isEqualTo(-1234567890L);
-        assertThat(atom.getFloatField()).isEqualTo(42.01f);
-        assertThat(atom.getStringField()).isEqualTo("Test non chained");
-        assertThat(atom.getBooleanField()).isTrue();
-        assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.OFF_VALUE);
-        assertThat(atom.getBytesField().getExperimentIdList())
-            .containsExactly(1L, 2L, 3L).inOrder();
-
-        atom = data.get(3).getAtom().getTestAtomReported();
-        attrChain = atom.getAttributionNodeList();
-        assertThat(attrChain).hasSize(1);
-        assertThat(attrChain.get(0).getUid()).isEqualTo(getUid());
-        assertThat(attrChain.get(0).getTag()).isEmpty();
-
-        assertThat(atom.getIntField()).isEqualTo(0);
-        assertThat(atom.getLongField()).isEqualTo(0L);
-        assertThat(atom.getFloatField()).isEqualTo(0f);
-        assertThat(atom.getStringField()).isEmpty();
-        assertThat(atom.getBooleanField()).isTrue();
-        assertThat(atom.getState().getNumber()).isEqualTo(TestAtomReported.State.OFF_VALUE);
-        assertThat(atom.getBytesField().getExperimentIdList()).isEmpty();
-    }
-
-    public void testNotificationPackagePreferenceExtraction() throws Exception {
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config,
-                    Atom.PACKAGE_NOTIFICATION_PREFERENCES_FIELD_NUMBER,
-                    null);
-        uploadConfig(config);
-        Thread.sleep(WAIT_TIME_SHORT);
-        runActivity("StatsdCtsForegroundActivity", "action", "action.show_notification");
-        Thread.sleep(WAIT_TIME_SHORT);
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        List<PackageNotificationPreferences> allPreferences = new ArrayList<>();
-        for (Atom atom : getGaugeMetricDataList()){
-            if(atom.hasPackageNotificationPreferences()) {
-                allPreferences.add(atom.getPackageNotificationPreferences());
-            }
-        }
-        assertThat(allPreferences.size()).isGreaterThan(0);
-
-        boolean foundTestPackagePreferences = false;
-        int uid = getUid();
-        for (PackageNotificationPreferences pref : allPreferences) {
-            assertThat(pref.getUid()).isGreaterThan(0);
-            assertTrue(pref.hasImportance());
-            assertTrue(pref.hasVisibility());
-            assertTrue(pref.hasUserLockedFields());
-            if(pref.getUid() == uid){
-                assertThat(pref.getImportance()).isEqualTo(-1000);  //UNSPECIFIED_IMPORTANCE
-                assertThat(pref.getVisibility()).isEqualTo(-1000);  //UNSPECIFIED_VISIBILITY
-                foundTestPackagePreferences = true;
-            }
-        }
-        assertTrue(foundTestPackagePreferences);
-    }
-
-    public void testNotificationChannelPreferencesExtraction() throws Exception {
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config,
-                    Atom.PACKAGE_NOTIFICATION_CHANNEL_PREFERENCES_FIELD_NUMBER,
-                    null);
-        uploadConfig(config);
-        Thread.sleep(WAIT_TIME_SHORT);
-        runActivity("StatsdCtsForegroundActivity", "action", "action.show_notification");
-        Thread.sleep(WAIT_TIME_SHORT);
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        List<PackageNotificationChannelPreferences> allChannelPreferences = new ArrayList<>();
-        for(Atom atom : getGaugeMetricDataList()) {
-            if (atom.hasPackageNotificationChannelPreferences()) {
-               allChannelPreferences.add(atom.getPackageNotificationChannelPreferences());
-            }
-        }
-        assertThat(allChannelPreferences.size()).isGreaterThan(0);
-
-        boolean foundTestPackagePreferences = false;
-        int uid = getUid();
-        for (PackageNotificationChannelPreferences pref : allChannelPreferences) {
-            assertThat(pref.getUid()).isGreaterThan(0);
-            assertTrue(pref.hasChannelId());
-            assertTrue(pref.hasChannelName());
-            assertTrue(pref.hasDescription());
-            assertTrue(pref.hasImportance());
-            assertTrue(pref.hasUserLockedFields());
-            assertTrue(pref.hasIsDeleted());
-            if(uid == pref.getUid() && pref.getChannelId().equals("StatsdCtsChannel")) {
-                assertThat(pref.getChannelName()).isEqualTo("Statsd Cts");
-                assertThat(pref.getDescription()).isEqualTo("Statsd Cts Channel");
-                assertThat(pref.getImportance()).isEqualTo(3);  // IMPORTANCE_DEFAULT
-                foundTestPackagePreferences = true;
-            }
-        }
-        assertTrue(foundTestPackagePreferences);
-    }
-
-    public void testNotificationChannelGroupPreferencesExtraction() throws Exception {
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config,
-                    Atom.PACKAGE_NOTIFICATION_CHANNEL_GROUP_PREFERENCES_FIELD_NUMBER,
-                    null);
-        uploadConfig(config);
-        Thread.sleep(WAIT_TIME_SHORT);
-        runActivity("StatsdCtsForegroundActivity", "action", "action.create_channel_group");
-        Thread.sleep(WAIT_TIME_SHORT);
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        List<PackageNotificationChannelGroupPreferences> allGroupPreferences = new ArrayList<>();
-        for(Atom atom : getGaugeMetricDataList()) {
-            if (atom.hasPackageNotificationChannelGroupPreferences()) {
-                allGroupPreferences.add(atom.getPackageNotificationChannelGroupPreferences());
-            }
-        }
-        assertThat(allGroupPreferences.size()).isGreaterThan(0);
-
-        boolean foundTestPackagePreferences = false;
-        int uid = getUid();
-        for(PackageNotificationChannelGroupPreferences pref : allGroupPreferences) {
-            assertThat(pref.getUid()).isGreaterThan(0);
-            assertTrue(pref.hasGroupId());
-            assertTrue(pref.hasGroupName());
-            assertTrue(pref.hasDescription());
-            assertTrue(pref.hasIsBlocked());
-            assertTrue(pref.hasUserLockedFields());
-            if(uid == pref.getUid() && pref.getGroupId().equals("StatsdCtsGroup")) {
-                assertThat(pref.getGroupName()).isEqualTo("Statsd Cts Group");
-                assertThat(pref.getDescription()).isEqualTo("StatsdCtsGroup Description");
-                assertThat(pref.getIsBlocked()).isFalse();
-                foundTestPackagePreferences = true;
-            }
-        }
-        assertTrue(foundTestPackagePreferences);
-    }
-
-    public void testNotificationReported() throws Exception {
-        StatsdConfig.Builder config = getPulledConfig();
-        addAtomEvent(config, Atom.NOTIFICATION_REPORTED_FIELD_NUMBER,
-            Arrays.asList(createFvm(NotificationReported.PACKAGE_NAME_FIELD_NUMBER)
-                              .setEqString(DEVICE_SIDE_TEST_PACKAGE)));
-        uploadConfig(config);
-        Thread.sleep(WAIT_TIME_SHORT);
-        runActivity("StatsdCtsForegroundActivity", "action", "action.show_notification");
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Sorted list of events in order in which they occurred.
-        List<EventMetricData> data = getEventMetricDataList();
-        assertThat(data).hasSize(1);
-        assertThat(data.get(0).getAtom().hasNotificationReported()).isTrue();
-        AtomsProto.NotificationReported n = data.get(0).getAtom().getNotificationReported();
-        assertThat(n.getPackageName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
-        assertThat(n.getUid()).isEqualTo(getUid());
-        assertThat(n.getNotificationIdHash()).isEqualTo(1);  // smallHash(0x7f080001)
-        assertThat(n.getChannelIdHash()).isEqualTo(SmallHash.hash("StatsdCtsChannel"));
-        assertThat(n.getGroupIdHash()).isEqualTo(0);
-        assertFalse(n.getIsGroupSummary());
-        assertThat(n.getCategory()).isEmpty();
-        assertThat(n.getStyle()).isEqualTo(0);
-        assertThat(n.getNumPeople()).isEqualTo(0);
-    }
-
-    public void testSettingsStatsReported() throws Exception {
-        // Base64 encoded proto com.android.service.nano.StringListParamProto,
-        // which contains five strings 'low_power_trigger_level', 'preferred_network_mode1',
-        // 'preferred_network_mode1_int', 'wfc_ims_mode','zen_mode'
-        final String encoded =
-            "Chdsb3dfcG93ZXJfdHJpZ2dlcl9sZXZlbAoQd2ZjX2ltc19tb2RlID0gMgoXcHJlZmVycmVkX25ldHdvcmtfbW9kZTEKG3ByZWZlcnJlZF9uZXR3b3JrX21vZGUxX2ludAoIemVuX21vZGU";
-        final String network_mode1 = "preferred_network_mode1";
-
-        int originalNetworkMode;
-        try {
-            originalNetworkMode = Integer.parseInt(
-                getDevice().executeShellCommand("settings get global " + network_mode1));
-        } catch (NumberFormatException e) {
-            // The default value, zen mode is not enabled
-            originalNetworkMode = 0;
-        }
-
-        // Clear settings_stats device config.
-        Thread.sleep(WAIT_TIME_SHORT);
-        getDevice().executeShellCommand(
-            "device_config reset untrusted_clear settings_stats");
-        // Set whitelist through device config.
-        Thread.sleep(WAIT_TIME_SHORT);
-        getDevice().executeShellCommand(
-            "device_config put settings_stats GlobalFeature__integer_whitelist " + encoded);
-        Thread.sleep(WAIT_TIME_SHORT);
-        // Set network_mode1 value
-        getDevice().executeShellCommand("settings put global " + network_mode1 + " 15");
-
-        // Get SettingSnapshot as a simple gauge metric.
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config, Atom.SETTING_SNAPSHOT_FIELD_NUMBER, null);
-        uploadConfig(config);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Start test app and trigger a pull while it is running.
-        try (AutoCloseable a = withActivity("StatsdCtsForegroundActivity", "action",
-                "action.show_notification")) {
-            Thread.sleep(WAIT_TIME_SHORT);
-            // Trigger a pull and wait for new pull before killing the process.
-            setAppBreadcrumbPredicate();
-            Thread.sleep(WAIT_TIME_LONG);
-        }
-
-        // Test the size of atoms. It should contain 5 atoms
-        List<Atom> atoms = getGaugeMetricDataList();
-        assertThat(atoms.size()).isEqualTo(5);
-        SettingSnapshot snapshot = null;
-        for (Atom atom : atoms) {
-            SettingSnapshot settingSnapshot = atom.getSettingSnapshot();
-            if (network_mode1.equals(settingSnapshot.getName())) {
-                snapshot = settingSnapshot;
-                break;
-            }
-        }
-
-        Thread.sleep(WAIT_TIME_SHORT);
-        // Test the data of atom.
-        assertNotNull(snapshot);
-        // Get setting value and test value type.
-        final int newNetworkMode = Integer.parseInt(
-            getDevice().executeShellCommand("settings get global " + network_mode1).trim());
-        assertThat(snapshot.getType()).isEqualTo(
-            SettingSnapshot.SettingsValueType.ASSIGNED_INT_TYPE);
-        assertThat(snapshot.getBoolValue()).isEqualTo(false);
-        assertThat(snapshot.getIntValue()).isEqualTo(newNetworkMode);
-        assertThat(snapshot.getFloatValue()).isEqualTo(0f);
-        assertThat(snapshot.getStrValue()).isEqualTo("");
-        assertThat(snapshot.getUserId()).isEqualTo(0);
-
-        // Restore the setting value.
-        getDevice().executeShellCommand(
-            "settings put global " + network_mode1 + " " + originalNetworkMode);
-    }
-
-    public void testIntegrityCheckAtomReportedDuringInstall() throws Exception {
-        createAndUploadConfig(AtomsProto.Atom.INTEGRITY_CHECK_RESULT_REPORTED_FIELD_NUMBER);
-
-        getDevice().uninstallPackage(DEVICE_SIDE_TEST_PACKAGE);
-        installTestApp();
-
-        List<EventMetricData> data = getEventMetricDataList();
-
-        assertThat(data.size()).isEqualTo(1);
-        assertThat(data.get(0).getAtom().hasIntegrityCheckResultReported()).isTrue();
-        IntegrityCheckResultReported result = data.get(0)
-                .getAtom().getIntegrityCheckResultReported();
-        assertThat(result.getPackageName()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE);
-        // we do not assert on certificates since it seem to differ by device.
-        assertThat(result.getInstallerPackageName()).isEqualTo("adb");
-        assertThat(result.getVersionCode()).isEqualTo(DEVICE_SIDE_TEST_PACKAGE_VERSION);
-        assertThat(result.getResponse()).isEqualTo(ALLOWED);
-        assertThat(result.getCausedByAppCertRule()).isFalse();
-        assertThat(result.getCausedByInstallerRule()).isFalse();
-    }
-
-    public void testMobileBytesTransfer() throws Throwable {
-        final int appUid = getUid();
-
-        // Verify MobileBytesTransfer, passing a ThrowingPredicate that verifies contents of
-        // corresponding atom type to prevent code duplication. The passed predicate returns
-        // true if the atom of appUid is found, false otherwise, and throws an exception if
-        // contents are not expected.
-        doTestMobileBytesTransferThat(Atom.MOBILE_BYTES_TRANSFER_FIELD_NUMBER, (atom) -> {
-            final AtomsProto.MobileBytesTransfer data = ((Atom) atom).getMobileBytesTransfer();
-            if (data.getUid() == appUid) {
-                assertDataUsageAtomDataExpected(data.getRxBytes(), data.getTxBytes(),
-                        data.getRxPackets(), data.getTxPackets());
-                return true; // found
-            }
-            return false;
-        });
-    }
-
-    public void testMobileBytesTransferByFgBg() throws Throwable {
-        final int appUid = getUid();
-
-        doTestMobileBytesTransferThat(Atom.MOBILE_BYTES_TRANSFER_BY_FG_BG_FIELD_NUMBER, (atom) -> {
-            final AtomsProto.MobileBytesTransferByFgBg data =
-                    ((Atom) atom).getMobileBytesTransferByFgBg();
-            if (data.getUid() == appUid && data.getIsForeground()) {
-                assertDataUsageAtomDataExpected(data.getRxBytes(), data.getTxBytes(),
-                        data.getRxPackets(), data.getTxPackets());
-                return true; // found
-            }
-            return false;
-        });
-    }
-
-    public void testDataUsageBytesTransfer() throws Throwable {
-        final boolean subtypeCombined = getNetworkStatsCombinedSubTypeEnabled();
-
-        doTestMobileBytesTransferThat(Atom.DATA_USAGE_BYTES_TRANSFER_FIELD_NUMBER, (atom) -> {
-            final AtomsProto.DataUsageBytesTransfer data =
-                    ((Atom) atom).getDataUsageBytesTransfer();
-            if (data.getState() == 1 /*NetworkStats.SET_FOREGROUND*/) {
-                assertDataUsageAtomDataExpected(data.getRxBytes(), data.getTxBytes(),
-                        data.getRxPackets(), data.getTxPackets());
-                // TODO: verify the RAT type field with the value gotten from device.
-                if (subtypeCombined) {
-                    assertThat(data.getRatType()).isEqualTo(
-                            NetworkTypeEnum.NETWORK_TYPE_UNKNOWN_VALUE);
-                } else {
-                    assertThat(data.getRatType()).isGreaterThan(
-                            NetworkTypeEnum.NETWORK_TYPE_UNKNOWN_VALUE);
-                }
-
-                // Assert that subscription info is valid.
-                assertThat(data.getSimMcc()).matches("^\\d{3}$");
-                assertThat(data.getSimMnc()).matches("^\\d{2,3}$");
-                assertThat(data.getCarrierId()).isNotEqualTo(
-                        -1); // TelephonyManager#UNKNOWN_CARRIER_ID
-
-                return true; // found
-            }
-            return false;
-        });
-    }
-
-    // TODO(b/157651730): Determine how to test tag and metered state within atom.
-    public void testBytesTransferByTagAndMetered() throws Throwable {
-        final int appUid = getUid();
-        final int atomId = Atom.BYTES_TRANSFER_BY_TAG_AND_METERED_FIELD_NUMBER;
-
-        doTestMobileBytesTransferThat(atomId, (atom) -> {
-            final AtomsProto.BytesTransferByTagAndMetered data =
-                    ((Atom) atom).getBytesTransferByTagAndMetered();
-            if (data.getUid() == appUid && data.getTag() == 0 /*app traffic generated on tag 0*/) {
-                assertDataUsageAtomDataExpected(data.getRxBytes(), data.getTxBytes(),
-                        data.getRxPackets(), data.getTxPackets());
-                return true; // found
-            }
-            return false;
-        });
-    }
-
-    public void testIsolatedToHostUidMapping() throws Exception {
-        createAndUploadConfig(Atom.APP_BREADCRUMB_REPORTED_FIELD_NUMBER, /*useAttribution=*/false);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Create an isolated service from which An AppBreadcrumbReported atom is written.
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testIsolatedProcessService");
-
-        List<EventMetricData> data = getEventMetricDataList();
-        assertThat(data).hasSize(1);
-        AppBreadcrumbReported atom = data.get(0).getAtom().getAppBreadcrumbReported();
-        assertThat(atom.getUid()).isEqualTo(getUid());
-        assertThat(atom.getLabel()).isEqualTo(0);
-        assertThat(atom.getState()).isEqualTo(AppBreadcrumbReported.State.START);
-    }
-
-    public void testPushedBlobStoreStats() throws Exception {
-        StatsdConfig.Builder conf = createConfigBuilder();
-        addAtomEvent(conf, Atom.BLOB_COMMITTED_FIELD_NUMBER, false);
-        addAtomEvent(conf, Atom.BLOB_LEASED_FIELD_NUMBER, false);
-        addAtomEvent(conf, Atom.BLOB_OPENED_FIELD_NUMBER, false);
-        uploadConfig(conf);
-
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testBlobStore");
-
-        List<EventMetricData> data = getEventMetricDataList();
-        assertThat(data).hasSize(3);
-
-        BlobCommitted blobCommitted = data.get(0).getAtom().getBlobCommitted();
-        final long blobId = blobCommitted.getBlobId();
-        final long blobSize = blobCommitted.getSize();
-        assertThat(blobCommitted.getUid()).isEqualTo(getUid());
-        assertThat(blobId).isNotEqualTo(0);
-        assertThat(blobSize).isNotEqualTo(0);
-        assertThat(blobCommitted.getResult()).isEqualTo(BlobCommitted.Result.SUCCESS);
-
-        BlobLeased blobLeased = data.get(1).getAtom().getBlobLeased();
-        assertThat(blobLeased.getUid()).isEqualTo(getUid());
-        assertThat(blobLeased.getBlobId()).isEqualTo(blobId);
-        assertThat(blobLeased.getSize()).isEqualTo(blobSize);
-        assertThat(blobLeased.getResult()).isEqualTo(BlobLeased.Result.SUCCESS);
-
-        BlobOpened blobOpened = data.get(2).getAtom().getBlobOpened();
-        assertThat(blobOpened.getUid()).isEqualTo(getUid());
-        assertThat(blobOpened.getBlobId()).isEqualTo(blobId);
-        assertThat(blobOpened.getSize()).isEqualTo(blobSize);
-        assertThat(blobOpened.getResult()).isEqualTo(BlobOpened.Result.SUCCESS);
-    }
-
-    // Constants that match the constants for AtomTests#testBlobStore
-    private static final long BLOB_COMMIT_CALLBACK_TIMEOUT_SEC = 5;
-    private static final long BLOB_EXPIRY_DURATION_MS = 24 * 60 * 60 * 1000;
-    private static final long BLOB_FILE_SIZE_BYTES = 23 * 1024L;
-    private static final long BLOB_LEASE_EXPIRY_DURATION_MS = 60 * 60 * 1000;
-
-    public void testPulledBlobStoreStats() throws Exception {
-        StatsdConfig.Builder config = createConfigBuilder();
-        addGaugeAtomWithDimensions(config,
-                Atom.BLOB_INFO_FIELD_NUMBER,
-                null);
-        uploadConfig(config);
-
-        final long testStartTimeMs = System.currentTimeMillis();
-        Thread.sleep(WAIT_TIME_SHORT);
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testBlobStore");
-        Thread.sleep(WAIT_TIME_LONG);
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Add commit callback time to test end time to account for async execution
-        final long testEndTimeMs =
-                System.currentTimeMillis() + BLOB_COMMIT_CALLBACK_TIMEOUT_SEC * 1000;
-
-        // Find the BlobInfo for the blob created in the test run
-        AtomsProto.BlobInfo blobInfo = null;
-        for (Atom atom : getGaugeMetricDataList()) {
-            if (atom.hasBlobInfo()) {
-                final AtomsProto.BlobInfo temp = atom.getBlobInfo();
-                if (temp.getCommitters().getCommitter(0).getUid() == getUid()) {
-                    blobInfo = temp;
-                    break;
-                }
-            }
-        }
-        assertThat(blobInfo).isNotNull();
-
-        assertThat(blobInfo.getSize()).isEqualTo(BLOB_FILE_SIZE_BYTES);
-
-        // Check that expiry time is reasonable
-        assertThat(blobInfo.getExpiryTimestampMillis()).isGreaterThan(
-                testStartTimeMs + BLOB_EXPIRY_DURATION_MS);
-        assertThat(blobInfo.getExpiryTimestampMillis()).isLessThan(
-                testEndTimeMs + BLOB_EXPIRY_DURATION_MS);
-
-        // Check that commit time is reasonable
-        final long commitTimeMs = blobInfo.getCommitters().getCommitter(
-                0).getCommitTimestampMillis();
-        assertThat(commitTimeMs).isGreaterThan(testStartTimeMs);
-        assertThat(commitTimeMs).isLessThan(testEndTimeMs);
-
-        // Check that WHITELIST and PRIVATE access mode flags are set
-        assertThat(blobInfo.getCommitters().getCommitter(0).getAccessMode()).isEqualTo(0b1001);
-        assertThat(blobInfo.getCommitters().getCommitter(0).getNumWhitelistedPackage()).isEqualTo(
-                1);
-
-        assertThat(blobInfo.getLeasees().getLeaseeCount()).isGreaterThan(0);
-        assertThat(blobInfo.getLeasees().getLeasee(0).getUid()).isEqualTo(getUid());
-
-        // Check that lease expiry time is reasonable
-        final long leaseExpiryMs = blobInfo.getLeasees().getLeasee(
-                0).getLeaseExpiryTimestampMillis();
-        assertThat(leaseExpiryMs).isGreaterThan(testStartTimeMs + BLOB_LEASE_EXPIRY_DURATION_MS);
-        assertThat(leaseExpiryMs).isLessThan(testEndTimeMs + BLOB_LEASE_EXPIRY_DURATION_MS);
-    }
-
-    private void assertDataUsageAtomDataExpected(long rxb, long txb, long rxp, long txp) {
-        assertThat(rxb).isGreaterThan(0L);
-        assertThat(txb).isGreaterThan(0L);
-        assertThat(rxp).isGreaterThan(0L);
-        assertThat(txp).isGreaterThan(0L);
-    }
-
-    private void doTestMobileBytesTransferThat(int atomTag, ThrowingPredicate p)
-            throws Throwable {
-        if (!hasFeature(FEATURE_TELEPHONY, true)) return;
-
-        // Get MobileBytesTransfer as a simple gauge metric.
-        final StatsdConfig.Builder config = getPulledConfig();
-        addGaugeAtomWithDimensions(config, atomTag, null);
-        uploadConfig(config);
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Generate some traffic on mobile network.
-        runDeviceTests(DEVICE_SIDE_TEST_PACKAGE, ".AtomTests", "testGenerateMobileTraffic");
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Force polling NetworkStatsService to get most updated network stats from lower layer.
-        runActivity("StatsdCtsForegroundActivity", "action", "action.poll_network_stats");
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        // Pull a report
-        setAppBreadcrumbPredicate();
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        final List<Atom> atoms = getGaugeMetricDataList(/*checkTimestampTruncated=*/true);
-        assertThat(atoms.size()).isAtLeast(1);
-
-        boolean foundAppStats = false;
-        for (final Atom atom : atoms) {
-            if (p.accept(atom)) {
-                foundAppStats = true;
-            }
-        }
-        assertWithMessage("uid " + getUid() + " is not found in " + atoms.size() + " atoms")
-                .that(foundAppStats).isTrue();
-    }
-
-    @FunctionalInterface
-    private interface ThrowingPredicate<S, T extends Throwable> {
-        boolean accept(S s) throws T;
-    }
-
-    public void testPackageInstallerV2MetricsReported() throws Throwable {
-        if (!hasFeature(FEATURE_INCREMENTAL_DELIVERY, true)) return;
-        final AtomsProto.PackageInstallerV2Reported report = installPackageUsingV2AndGetReport(
-                new String[]{TEST_INSTALL_APK});
-        assertTrue(report.getIsIncremental());
-        // tests are ran using SHELL_UID and installation will be treated as adb install
-        assertEquals("", report.getPackageName());
-        assertEquals(1, report.getReturnCode());
-        assertTrue(report.getDurationMillis() > 0);
-        assertEquals(getTestFileSize(TEST_INSTALL_APK), report.getApksSizeBytes());
-
-        getDevice().uninstallPackage(TEST_INSTALL_PACKAGE);
-    }
-
-    public void testPackageInstallerV2MetricsReportedForSplits() throws Throwable {
-        if (!hasFeature(FEATURE_INCREMENTAL_DELIVERY, true)) return;
-
-        final AtomsProto.PackageInstallerV2Reported report = installPackageUsingV2AndGetReport(
-                new String[]{TEST_INSTALL_APK_BASE, TEST_INSTALL_APK_SPLIT});
-        assertTrue(report.getIsIncremental());
-        // tests are ran using SHELL_UID and installation will be treated as adb install
-        assertEquals("", report.getPackageName());
-        assertEquals(1, report.getReturnCode());
-        assertTrue(report.getDurationMillis() > 0);
-        assertEquals(
-                getTestFileSize(TEST_INSTALL_APK_BASE) + getTestFileSize(TEST_INSTALL_APK_SPLIT),
-                report.getApksSizeBytes());
-
-        getDevice().uninstallPackage(TEST_INSTALL_PACKAGE);
-    }
-
-    public void testAppForegroundBackground() throws Exception {
-        Set<Integer> onStates = new HashSet<>(Arrays.asList(
-                AppUsageEventOccurred.EventType.MOVE_TO_FOREGROUND_VALUE));
-        Set<Integer> offStates = new HashSet<>(Arrays.asList(
-                AppUsageEventOccurred.EventType.MOVE_TO_BACKGROUND_VALUE));
-
-        List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
-        createAndUploadConfig(Atom.APP_USAGE_EVENT_OCCURRED_FIELD_NUMBER, false);
-        Thread.sleep(WAIT_TIME_FOR_CONFIG_UPDATE_MS);
-
-        getDevice().executeShellCommand(String.format(
-                "am start -n '%s' -e %s %s",
-                "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity",
-                "action", ACTION_SHOW_APPLICATION_OVERLAY));
-        final int waitTime = EXTRA_WAIT_TIME_MS + 5_000; // Overlay may need to sit there a while.
-        Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS);
-
-        List<EventMetricData> data = getEventMetricDataList();
-        Function<Atom, Integer> appUsageStateFunction =
-                atom -> atom.getAppUsageEventOccurred().getEventType().getNumber();
-        popUntilFind(data, onStates, appUsageStateFunction); // clear out initial appusage states.s
-        assertStatesOccurred(stateSet, data, 0, appUsageStateFunction);
-    }
-
-    public void testAppForceStopUsageEvent() throws Exception {
-        Set<Integer> onStates = new HashSet<>(Arrays.asList(
-                AppUsageEventOccurred.EventType.MOVE_TO_FOREGROUND_VALUE));
-        Set<Integer> offStates = new HashSet<>(Arrays.asList(
-                AppUsageEventOccurred.EventType.MOVE_TO_BACKGROUND_VALUE));
-
-        List<Set<Integer>> stateSet = Arrays.asList(onStates, offStates); // state sets, in order
-        createAndUploadConfig(Atom.APP_USAGE_EVENT_OCCURRED_FIELD_NUMBER, false);
-        Thread.sleep(WAIT_TIME_FOR_CONFIG_UPDATE_MS);
-
-        getDevice().executeShellCommand(String.format(
-                "am start -n '%s' -e %s %s",
-                "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity",
-                "action", ACTION_LONG_SLEEP_WHILE_TOP));
-        final int waitTime = EXTRA_WAIT_TIME_MS + 5_000;
-        Thread.sleep(waitTime);
-
-        getDevice().executeShellCommand(String.format(
-                "am force-stop %s",
-                "com.android.server.cts.device.statsd/.StatsdCtsForegroundActivity"));
-        Thread.sleep(waitTime + STATSD_REPORT_WAIT_TIME_MS);
-
-        List<EventMetricData> data = getEventMetricDataList();
-        Function<Atom, Integer> appUsageStateFunction =
-                atom -> atom.getAppUsageEventOccurred().getEventType().getNumber();
-        popUntilFind(data, onStates, appUsageStateFunction); // clear out initial appusage states.
-        assertStatesOccurred(stateSet, data, 0, appUsageStateFunction);
-    }
-
-    private AtomsProto.PackageInstallerV2Reported installPackageUsingV2AndGetReport(
-            String[] apkNames) throws Exception {
-        createAndUploadConfig(Atom.PACKAGE_INSTALLER_V2_REPORTED_FIELD_NUMBER);
-        Thread.sleep(WAIT_TIME_SHORT);
-        installPackageUsingIncremental(apkNames, TEST_REMOTE_DIR);
-        assertTrue(getDevice().isPackageInstalled(TEST_INSTALL_PACKAGE));
-        Thread.sleep(WAIT_TIME_SHORT);
-
-        List<AtomsProto.PackageInstallerV2Reported> reports = new ArrayList<>();
-        for(EventMetricData data : getEventMetricDataList()) {
-            if (data.getAtom().hasPackageInstallerV2Reported()) {
-                reports.add(data.getAtom().getPackageInstallerV2Reported());
-            }
-        }
-        assertEquals(1, reports.size());
-        return reports.get(0);
-    }
-
-    private void installPackageUsingIncremental(String[] apkNames, String remoteDirPath)
-            throws Exception {
-        getDevice().executeShellCommand("mkdir " + remoteDirPath);
-        String[] remoteApkPaths = new String[apkNames.length];
-        for (int i = 0; i < remoteApkPaths.length; i++) {
-            remoteApkPaths[i] = pushApkToRemote(apkNames[i], remoteDirPath);
-        }
-        getDevice().executeShellCommand(
-                "pm install-incremental -t -g " + String.join(" ", remoteApkPaths));
-    }
-
-    private String pushApkToRemote(String apkName, String remoteDirPath)
-            throws Exception {
-        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
-        final File apk = buildHelper.getTestFile(apkName);
-        final String remoteApkPath = remoteDirPath + "/" + apk.getName();
-        assertTrue(getDevice().pushFile(apk, remoteApkPath));
-        assertNotNull(apk);
-        return remoteApkPath;
-    }
-
-    private long getTestFileSize(String fileName) throws Exception {
-        CompatibilityBuildHelper buildHelper = new CompatibilityBuildHelper(getBuild());
-        final File file = buildHelper.getTestFile(fileName);
-        return file.length();
-    }
-}
diff --git a/tests/src/android/cts/statsd/validation/ValidationTests.java b/tests/src/android/cts/statsd/validation/ValidationTests.java
index 87f6840..3e2de0a 100644
--- a/tests/src/android/cts/statsd/validation/ValidationTests.java
+++ b/tests/src/android/cts/statsd/validation/ValidationTests.java
@@ -143,7 +143,6 @@
         // ADB disconnection causes failure of getUid(). Move up here before turnScreenOff().
         final int EXPECTED_UID = getUid();
 
-
         turnScreenOn(); // To ensure that the ScreenOff later gets logged.
         // AoD needs to be turned off because the screen should go into an off state. But, if AoD is
         // on and the device doesn't support STATE_DOZE, the screen sadly goes back to STATE_ON.
@@ -190,7 +189,7 @@
         long statsdDurationMs = statsdWakelockData.get(EXPECTED_UID)
                 .get(EXPECTED_TAG_HASH) / 1_000_000;
         assertWithMessage(
-                "Wakelock in statsd with uid %s and tag %s was too short or too long", 
+                "Wakelock in statsd with uid %s and tag %s was too short or too long",
                 EXPECTED_UID, EXPECTED_TAG
         ).that(statsdDurationMs).isIn(Range.closed((long) MIN_DURATION, (long) MAX_DURATION));