Snap for 11390602 from 43c8cfd5b605aa6019ff96e7dca1d6bd8f40135f to mainline-wifi-release

Change-Id: I6300c6af17db8b5d86de85315b23993bb78de810
diff --git a/framework/Android.bp b/framework/Android.bp
index 6acc532..fe1a546 100644
--- a/framework/Android.bp
+++ b/framework/Android.bp
@@ -100,15 +100,6 @@
     min_sdk_version: "30",
 }
 
-java_api_contribution {
-    name: "framework-statsd-public-stubs",
-    api_surface: "public",
-    api_file: "api/current.txt",
-    visibility: [
-        "//build/orchestrator/apis",
-    ],
-}
-
 // JNI library for StatsLog.write
 cc_library_shared {
     name: "libstats_jni",
diff --git a/lib/libstatsgtestmatchers/include/gtest_matchers.h b/lib/libstatsgtestmatchers/include/gtest_matchers.h
index 084346d..3d9b792 100644
--- a/lib/libstatsgtestmatchers/include/gtest_matchers.h
+++ b/lib/libstatsgtestmatchers/include/gtest_matchers.h
@@ -206,13 +206,30 @@
 EQ_MATCHER(PluggedStateChanged, PROPERTY_EQ(PluggedStateChanged, state));
 TYPE_PRINTER(PluggedStateChanged, PROPERTY_PRINT(state));
 
+EQ_MATCHER(WakelockStateChanged,
+        REPEATED_PROPERTY_MATCHER(WakelockStateChanged, attribution_node, EqAttributionNode),
+        PROPERTY_EQ(WakelockStateChanged, type),
+        PROPERTY_EQ(WakelockStateChanged, tag),
+        PROPERTY_EQ(WakelockStateChanged, state),
+        PROPERTY_EQ(WakelockStateChanged, process_state)
+);
+TYPE_PRINTER(WakelockStateChanged,
+        REPEATED_PROPERTY_PRINT(attribution_node)
+        PROPERTY_PRINT(type)
+        PROPERTY_PRINT(tag)
+        PROPERTY_PRINT(state)
+        PROPERTY_PRINT(process_state)
+);
+
 EQ_MATCHER(Atom,
         PROPERTY_MATCHER(Atom, screen_state_changed, EqScreenStateChanged),
-        PROPERTY_MATCHER(Atom, test_atom_reported, EqTestAtomReported)
+        PROPERTY_MATCHER(Atom, test_atom_reported, EqTestAtomReported),
+        PROPERTY_MATCHER(Atom, wakelock_state_changed, EqWakelockStateChanged)
 );
 TYPE_PRINTER(Atom,
         PROPERTY_PRINT(screen_state_changed)
         PROPERTY_PRINT(test_atom_reported)
+        PROPERTY_PRINT(wakelock_state_changed)
 );
 
 EQ_MATCHER(ShellData,
diff --git a/lib/libstatspull/Android.bp b/lib/libstatspull/Android.bp
index de0ae47..d10e7e3 100644
--- a/lib/libstatspull/Android.bp
+++ b/lib/libstatspull/Android.bp
@@ -37,38 +37,21 @@
     shared_libs: [
         "libbinder_ndk",
         "liblog",
+        "libstatssocket",
     ],
     static_libs: [
         "libutils",
         "statsd-aidl-ndk",
     ],
-    target: {
-        android: {
-            shared_libs: ["libstatssocket"],
-        },
-        host: {
-            static_libs: ["libstatssocket"],
-        },
-    },
 }
 
-cc_library {
+cc_library_shared {
     name: "libstatspull",
     defaults: [
         "libstatspull_defaults",
     ],
     host_supported: true,
     target: {
-        android: {
-            static: {
-                enabled: false,
-            },
-        },
-        host: {
-            shared: {
-                enabled: false,
-            },
-        },
         darwin: {
             enabled: false,
         },
diff --git a/lib/libstatssocket/Android.bp b/lib/libstatssocket/Android.bp
index 374168d..36aa4db 100644
--- a/lib/libstatssocket/Android.bp
+++ b/lib/libstatssocket/Android.bp
@@ -52,25 +52,12 @@
     ],
 }
 
-cc_library {
+cc_library_shared {
     name: "libstatssocket",
     defaults: [
         "libstatssocket_defaults",
     ],
     host_supported: true,
-    target: {
-        // On android, libstatssocket should only be linked as a shared lib
-        android: {
-            static: {
-                enabled: false,
-            },
-        },
-        host: {
-            shared: {
-                enabled: false,
-            },
-        },
-    },
     stl: "libc++_static",
 
     // enumerate stable entry points for APEX use
diff --git a/statsd/Android.bp b/statsd/Android.bp
index 616b112..a3b3802 100644
--- a/statsd/Android.bp
+++ b/statsd/Android.bp
@@ -25,7 +25,34 @@
         "-Wno-deprecated-declarations",
         "-Wthread-safety",
     ],
+    tidy: true,
+    tidy_flags: [
+        // Only check our headers
+        "-header-filter=^packages/modules/StatsD/statsd",
+    ],
 
+    tidy_checks: [
+        "android-*",
+        "bugprone-*",
+        "cert-*",
+        "clang-analyzer-security*",
+        "google-*",
+        "misc-*",
+        "performance-*",
+        "-bugprone-narrowing-conversions", // lots of unsigned -> int conversions
+        "-cert-err34-c",
+        "-cert-msc30-c", // warning: rand() has limited randomness; use C++11 random library
+        "-cert-msc50-cpp", // warning: rand() has limited randomness; use C++11 random library
+    ],
+    tidy_checks_as_errors: [
+        "android-*",
+        "bugprone-*",
+        "cert-*",
+        "clang-analyzer-security*",
+        "google-*",
+        "misc-*",
+        "performance-*",
+    ],
     srcs: [
         "src/active_config_list.proto",
         "src/anomaly/AlarmMonitor.cpp",
@@ -96,6 +123,7 @@
         "src/uid_data.proto",
         "src/utils/MultiConditionTrigger.cpp",
         "src/utils/DbUtils.cpp",
+        "src/utils/Regex.cpp",
         "src/utils/RestrictedPolicyManager.cpp",
         "src/utils/ShardOffsetProvider.cpp",
     ],
@@ -130,7 +158,10 @@
 genrule {
     name: "statslog_statsd.h",
     tools: ["stats-log-api-gen"],
-    cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_statsd.h --module statsd --namespace android,os,statsd,util",
+    cmd: "$(location stats-log-api-gen) " +
+        "--header $(genDir)/statslog_statsd.h " +
+        "--module statsd " +
+        "--namespace android,os,statsd,util",
     out: [
         "statslog_statsd.h",
     ],
@@ -139,7 +170,11 @@
 genrule {
     name: "statslog_statsd.cpp",
     tools: ["stats-log-api-gen"],
-    cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_statsd.cpp --module statsd --namespace android,os,statsd,util --importHeader statslog_statsd.h",
+    cmd: "$(location stats-log-api-gen) " +
+        "--cpp $(genDir)/statslog_statsd.cpp " +
+        "--module statsd " +
+        "--namespace android,os,statsd,util " +
+        "--importHeader statslog_statsd.h",
     out: [
         "statslog_statsd.cpp",
     ],
@@ -148,7 +183,10 @@
 genrule {
     name: "statslog_statsdtest.h",
     tools: ["stats-log-api-gen"],
-    cmd: "$(location stats-log-api-gen) --header $(genDir)/statslog_statsdtest.h --module statsdtest --namespace android,os,statsd,util",
+    cmd: "$(location stats-log-api-gen) " +
+        "--header $(genDir)/statslog_statsdtest.h " +
+        "--module statsdtest " +
+        "--namespace android,os,statsd,util",
     out: [
         "statslog_statsdtest.h",
     ],
@@ -157,7 +195,11 @@
 genrule {
     name: "statslog_statsdtest.cpp",
     tools: ["stats-log-api-gen"],
-    cmd: "$(location stats-log-api-gen) --cpp $(genDir)/statslog_statsdtest.cpp --module statsdtest --namespace android,os,statsd,util --importHeader statslog_statsdtest.h",
+    cmd: "$(location stats-log-api-gen) " +
+        "--cpp $(genDir)/statslog_statsdtest.cpp " +
+        "--module statsdtest " +
+        "--namespace android,os,statsd,util " +
+        "--importHeader statslog_statsdtest.h",
     out: [
         "statslog_statsdtest.cpp",
     ],
@@ -231,13 +273,60 @@
     min_sdk_version: "30",
 }
 
+cc_defaults {
+    name: "statsd_test_defaults",
+    defaults: ["statsd_defaults"],
+    srcs: [
+        // atom_field_options.proto needs field_options.proto, but that is
+        // not included in libprotobuf-cpp-lite, so compile it here.
+        ":libprotobuf-internal-protos",
+        ":libstats_internal_protos",
+
+        "src/shell/shell_data.proto",
+        "src/stats_log.proto",
+    ],
+    cflags: [
+        "-Wall",
+        "-Werror",
+        "-Wno-enum-compare",
+        "-Wno-missing-field-initializers",
+        "-Wno-unused-function",
+        "-Wno-unused-parameter",
+        "-Wno-unused-variable",
+    ],
+    static_libs: [
+        "libgmock",
+        "libstatslog_statsdtest",
+        "libstatssocket_private",
+    ],
+    proto: {
+        type: "lite",
+        include_dirs: [
+            "external/protobuf/src",
+            "frameworks/proto_logging/stats",
+        ],
+        static: true,
+    },
+}
+
+cc_library_static {
+    name: "libstats_test_utils",
+    defaults: ["statsd_test_defaults"],
+    srcs: [
+        "tests/statsd_test_util.cpp",
+    ],
+    tidy_timeout_srcs: [
+        "tests/statsd_test_util.cpp",
+    ],
+}
+
 // ==============
 // statsd_test
 // ==============
 
 cc_test {
     name: "statsd_test",
-    defaults: ["statsd_defaults"],
+    defaults: ["statsd_test_defaults"],
     test_suites: [
         "device-tests",
         "mts-statsd",
@@ -256,16 +345,6 @@
         },
     },
 
-    cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wno-enum-compare",
-        "-Wno-missing-field-initializers",
-        "-Wno-unused-function",
-        "-Wno-unused-parameter",
-        "-Wno-unused-variable",
-    ],
-
     require_root: true,
 
     tidy_timeout_srcs: [
@@ -300,19 +379,11 @@
         "tests/metrics/parsing_utils/config_update_utils_test.cpp",
         "tests/metrics/parsing_utils/metrics_manager_util_test.cpp",
         "tests/state/StateTracker_test.cpp",
-        "tests/statsd_test_util.cpp",
         "tests/StatsLogProcessor_test.cpp",
         "tests/UidMap_test.cpp",
     ],
 
     srcs: [
-        // atom_field_options.proto needs field_options.proto, but that is
-        // not included in libprotobuf-cpp-lite, so compile it here.
-        ":libprotobuf-internal-protos",
-        ":libstats_internal_protos",
-
-        "src/shell/shell_data.proto",
-        "src/stats_log.proto",
         "tests/AlarmMonitor_test.cpp",
         "tests/anomaly/AlarmTracker_test.cpp",
         "tests/anomaly/AnomalyTracker_test.cpp",
@@ -337,9 +408,10 @@
         "tests/e2e/MetricConditionLink_e2e_test.cpp",
         "tests/e2e/PartialBucket_e2e_test.cpp",
         "tests/e2e/RestrictedConfig_e2e_test.cpp",
+        "tests/e2e/RestrictedEventMetric_e2e_test.cpp",
+        "tests/e2e/StringReplace_e2e_test.cpp",
         "tests/e2e/ValueMetric_pull_e2e_test.cpp",
         "tests/e2e/WakelockDuration_e2e_test.cpp",
-        "tests/e2e/RestrictedEventMetric_e2e_test.cpp",
         "tests/external/puller_util_test.cpp",
         "tests/external/StatsCallbackPuller_test.cpp",
         "tests/external/StatsPuller_test.cpp",
@@ -370,7 +442,6 @@
         "tests/MetricsManager_test.cpp",
         "tests/shell/ShellSubscriber_test.cpp",
         "tests/state/StateTracker_test.cpp",
-        "tests/statsd_test_util.cpp",
         "tests/statsd_test_util_test.cpp",
         "tests/SocketListener_test.cpp",
         "tests/StatsLogProcessor_test.cpp",
@@ -382,22 +453,11 @@
     ],
 
     static_libs: [
-        "libgmock",
         "libstatsgtestmatchers",
-        "libstatslog_statsdtest",
-        "libstatssocket_private",
+        "libstats_test_utils",
     ],
 
-    proto: {
-        type: "lite",
-        include_dirs: [
-            "external/protobuf/src",
-            "frameworks/proto_logging/stats",
-        ],
-        static: true,
-    },
     min_sdk_version: "30",
-
 }
 
 //#############################
@@ -406,14 +466,9 @@
 
 cc_benchmark {
     name: "statsd_benchmark",
-    defaults: ["statsd_defaults"],
+    defaults: ["statsd_test_defaults"],
 
     srcs: [
-        // atom_field_options.proto needs field_options.proto, but that is
-        // not included in libprotobuf-cpp-lite, so compile it here.
-        ":libprotobuf-internal-protos",
-        ":libstats_internal_protos",
-
         "benchmark/data_structures_benchmark.cpp",
         "benchmark/db_benchmark.cpp",
         "benchmark/duration_metric_benchmark.cpp",
@@ -423,43 +478,19 @@
         "benchmark/log_event_benchmark.cpp",
         "benchmark/log_event_filter_benchmark.cpp",
         "benchmark/main.cpp",
-        "benchmark/metric_util.cpp",
+        "benchmark/on_log_event_benchmark.cpp",
         "benchmark/stats_write_benchmark.cpp",
         "benchmark/loss_info_container_benchmark.cpp",
-        "src/stats_log.proto",
     ],
 
-    proto: {
-        type: "lite",
-        include_dirs: [
-            "external/protobuf/src",
-            "frameworks/proto_logging/stats",
-        ],
-    },
-
     cflags: [
-        "-Wall",
-        "-Werror",
-        "-Wno-unused-parameter",
-        "-Wno-unused-variable",
-        "-Wno-unused-function",
-
         // Bug: http://b/29823425 Disable -Wvarargs for Clang update to r271374
         "-Wno-varargs",
     ],
 
     static_libs: [
-        "libplatformprotos",
-        "libstatssocket_private",
-    ],
-
-    shared_libs: [
-        "libprotobuf-cpp-lite",
-        "libstatslog",
-    ],
-
-    header_libs: [
-        "libgtest_prod_headers",
+        "libgtest",
+        "libstats_test_utils",
     ],
 }
 
@@ -552,6 +583,37 @@
     ],
 }
 
+cc_fuzz {
+    name: "statsd_service_fuzzer",
+    defaults: [
+        "statsd_defaults",
+        "service_fuzzer_defaults",
+    ],
+    srcs: [
+        "fuzzers/statsd_service_fuzzer.cpp",
+    ],
+    shared_libs: [
+        "libstatssocket",
+        "libvndksupport",
+    ],
+    cflags: [
+        "-Wall",
+        "-Wextra",
+        "-Werror",
+        "-Wno-unused-parameter",
+    ],
+    fuzz_config: {
+        cc: [
+            "singhtejinder@google.com",
+            "sharaienko@google.com",
+        ],
+    },
+    proto: {
+        type: "lite",
+        static: true,
+    },
+}
+
 // Filegroup for subscription protos.
 filegroup {
     name: "libstats_subscription_protos",
@@ -562,4 +624,3 @@
         "src/shell/shell_data.proto",
     ],
 }
-
diff --git a/statsd/benchmark/db_benchmark.cpp b/statsd/benchmark/db_benchmark.cpp
index ebe751a..b9b1694 100644
--- a/statsd/benchmark/db_benchmark.cpp
+++ b/statsd/benchmark/db_benchmark.cpp
@@ -15,7 +15,7 @@
  */
 
 #include "benchmark/benchmark.h"
-#include "metric_util.h"
+#include "tests/statsd_test_util.h"
 #include "utils/DbUtils.h"
 
 using namespace std;
diff --git a/statsd/benchmark/duration_metric_benchmark.cpp b/statsd/benchmark/duration_metric_benchmark.cpp
index 2d315d9..a87b5ee 100644
--- a/statsd/benchmark/duration_metric_benchmark.cpp
+++ b/statsd/benchmark/duration_metric_benchmark.cpp
@@ -14,12 +14,13 @@
  * limitations under the License.
  */
 #include <vector>
-#include "benchmark/benchmark.h"
+
 #include "FieldValue.h"
 #include "HashableDimensionKey.h"
+#include "benchmark/benchmark.h"
 #include "logd/LogEvent.h"
 #include "stats_log_util.h"
-#include "metric_util.h"
+#include "tests/statsd_test_util.h"
 
 namespace android {
 namespace os {
@@ -39,15 +40,15 @@
 
     auto scheduledJobPredicate = CreateScheduledJobPredicate();
     auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
-    dimensions->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
+    dimensions->set_field(util::SCHEDULED_JOB_STATE_CHANGED);
     dimensions->add_child()->set_field(2);  // job name field.
 
     auto screenIsOffPredicate = CreateScreenIsOffPredicate();
 
     auto isSyncingPredicate = CreateIsSyncingPredicate();
     auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
-    *syncDimension = CreateAttributionUidAndTagDimensions(android::util::SYNC_STATE_CHANGED,
-                                                          {Position::FIRST});
+    *syncDimension =
+            CreateAttributionUidAndTagDimensions(util::SYNC_STATE_CHANGED, {Position::FIRST});
     if (addExtraDimensionInCondition) {
         syncDimension->add_child()->set_field(2 /* name field*/);
     }
@@ -68,10 +69,10 @@
     metric->set_condition(combinationPredicate->id());
     metric->set_aggregation_type(aggregationType);
     auto dimensionWhat = metric->mutable_dimensions_in_what();
-    dimensionWhat->set_field(android::util::SCHEDULED_JOB_STATE_CHANGED);
+    dimensionWhat->set_field(util::SCHEDULED_JOB_STATE_CHANGED);
     dimensionWhat->add_child()->set_field(2);  // job name field.
-    *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
-            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    *metric->mutable_dimensions_in_condition() =
+            CreateAttributionUidAndTagDimensions(util::SYNC_STATE_CHANGED, {Position::FIRST});
     return config;
 }
 
@@ -87,14 +88,13 @@
 
     auto scheduledJobPredicate = CreateScheduledJobPredicate();
     auto dimensions = scheduledJobPredicate.mutable_simple_predicate()->mutable_dimensions();
-    *dimensions = CreateAttributionUidDimensions(
-                android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
+    *dimensions =
+            CreateAttributionUidDimensions(util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
     dimensions->add_child()->set_field(2);  // job name field.
 
     auto isSyncingPredicate = CreateIsSyncingPredicate();
     auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
-    *syncDimension = CreateAttributionUidDimensions(
-            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    *syncDimension = CreateAttributionUidDimensions(util::SYNC_STATE_CHANGED, {Position::FIRST});
     if (addExtraDimensionInCondition) {
         syncDimension->add_child()->set_field(2 /* name field*/);
     }
@@ -116,16 +116,15 @@
     metric->set_what(scheduledJobPredicate.id());
     metric->set_condition(combinationPredicate->id());
     metric->set_aggregation_type(aggregationType);
-    *metric->mutable_dimensions_in_what() = CreateAttributionUidDimensions(
-            android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
+    *metric->mutable_dimensions_in_what() =
+            CreateAttributionUidDimensions(util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
 
     auto links = metric->add_links();
     links->set_condition(isSyncingPredicate.id());
     *links->mutable_fields_in_what() =
-            CreateAttributionUidDimensions(
-                android::util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
+            CreateAttributionUidDimensions(util::SCHEDULED_JOB_STATE_CHANGED, {Position::FIRST});
     *links->mutable_fields_in_condition() =
-            CreateAttributionUidDimensions(android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+            CreateAttributionUidDimensions(util::SYNC_STATE_CHANGED, {Position::FIRST});
     return config;
 }
 
@@ -208,8 +207,8 @@
     sortLogEventsByTimestamp(&events);
 
     while (state.KeepRunning()) {
-        auto processor = CreateStatsLogProcessor(
-                bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+        auto processor =
+                CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
         for (const auto& event : events) {
             processor->OnLogEvent(event.get());
         }
@@ -299,8 +298,8 @@
     sortLogEventsByTimestamp(&events);
 
     while (state.KeepRunning()) {
-        auto processor = CreateStatsLogProcessor(
-                bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+        auto processor =
+                CreateStatsLogProcessor(bucketStartTimeNs, bucketStartTimeNs, config, cfgKey);
         for (const auto& event : events) {
             processor->OnLogEvent(event.get());
         }
diff --git a/statsd/benchmark/filter_value_benchmark.cpp b/statsd/benchmark/filter_value_benchmark.cpp
index 743ccc4..0642070 100644
--- a/statsd/benchmark/filter_value_benchmark.cpp
+++ b/statsd/benchmark/filter_value_benchmark.cpp
@@ -19,9 +19,9 @@
 #include "HashableDimensionKey.h"
 #include "benchmark/benchmark.h"
 #include "logd/LogEvent.h"
-#include "metric_util.h"
 #include "stats_event.h"
 #include "stats_log_util.h"
+#include "tests/statsd_test_util.h"
 
 namespace android {
 namespace os {
diff --git a/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp b/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
index 7a45565..9957066 100644
--- a/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
+++ b/statsd/benchmark/get_dimensions_for_condition_benchmark.cpp
@@ -19,9 +19,9 @@
 #include "HashableDimensionKey.h"
 #include "benchmark/benchmark.h"
 #include "logd/LogEvent.h"
-#include "metric_util.h"
 #include "stats_event.h"
 #include "stats_log_util.h"
+#include "tests/statsd_test_util.h"
 
 namespace android {
 namespace os {
diff --git a/statsd/benchmark/metric_util.cpp b/statsd/benchmark/metric_util.cpp
deleted file mode 100644
index a1e978a..0000000
--- a/statsd/benchmark/metric_util.cpp
+++ /dev/null
@@ -1,381 +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 "metric_util.h"
-
-#include "stats_event.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId) {
-    AtomMatcher atom_matcher;
-    atom_matcher.set_id(StringToId(name));
-    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
-    simple_atom_matcher->set_atom_id(atomId);
-    return atom_matcher;
-}
-
-AtomMatcher CreateScheduledJobStateChangedAtomMatcher(const string& name,
-                                                      ScheduledJobStateChanged::State state) {
-    AtomMatcher atom_matcher;
-    atom_matcher.set_id(StringToId(name));
-    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
-    simple_atom_matcher->set_atom_id(android::util::SCHEDULED_JOB_STATE_CHANGED);
-    auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
-    field_value_matcher->set_field(3);  // State field.
-    field_value_matcher->set_eq_int(state);
-    return atom_matcher;
-}
-
-AtomMatcher CreateStartScheduledJobAtomMatcher() {
-    return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobStart",
-                                                     ScheduledJobStateChanged::STARTED);
-}
-
-AtomMatcher CreateFinishScheduledJobAtomMatcher() {
-    return CreateScheduledJobStateChangedAtomMatcher("ScheduledJobFinish",
-                                                     ScheduledJobStateChanged::FINISHED);
-}
-
-AtomMatcher CreateScreenBrightnessChangedAtomMatcher() {
-    AtomMatcher atom_matcher;
-    atom_matcher.set_id(StringToId("ScreenBrightnessChanged"));
-    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
-    simple_atom_matcher->set_atom_id(android::util::SCREEN_BRIGHTNESS_CHANGED);
-    return atom_matcher;
-}
-
-AtomMatcher CreateUidProcessStateChangedAtomMatcher() {
-    AtomMatcher atom_matcher;
-    atom_matcher.set_id(StringToId("UidProcessStateChanged"));
-    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
-    simple_atom_matcher->set_atom_id(android::util::UID_PROCESS_STATE_CHANGED);
-    return atom_matcher;
-}
-
-AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name,
-                                                  WakelockStateChanged::State state) {
-    AtomMatcher atom_matcher;
-    atom_matcher.set_id(StringToId(name));
-    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
-    simple_atom_matcher->set_atom_id(android::util::WAKELOCK_STATE_CHANGED);
-    auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
-    field_value_matcher->set_field(4);  // State field.
-    field_value_matcher->set_eq_int(state);
-    return atom_matcher;
-}
-
-AtomMatcher CreateAcquireWakelockAtomMatcher() {
-    return CreateWakelockStateChangedAtomMatcher("AcquireWakelock", WakelockStateChanged::ACQUIRE);
-}
-
-AtomMatcher CreateReleaseWakelockAtomMatcher() {
-    return CreateWakelockStateChangedAtomMatcher("ReleaseWakelock", WakelockStateChanged::RELEASE);
-}
-
-AtomMatcher CreateScreenStateChangedAtomMatcher(
-    const string& name, android::view::DisplayStateEnum state) {
-    AtomMatcher atom_matcher;
-    atom_matcher.set_id(StringToId(name));
-    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
-    simple_atom_matcher->set_atom_id(android::util::SCREEN_STATE_CHANGED);
-    auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
-    field_value_matcher->set_field(1);  // State field.
-    field_value_matcher->set_eq_int(state);
-    return atom_matcher;
-}
-
-AtomMatcher CreateScreenTurnedOnAtomMatcher() {
-    return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn",
-            android::view::DisplayStateEnum::DISPLAY_STATE_ON);
-}
-
-AtomMatcher CreateScreenTurnedOffAtomMatcher() {
-    return CreateScreenStateChangedAtomMatcher("ScreenTurnedOff",
-            ::android::view::DisplayStateEnum::DISPLAY_STATE_OFF);
-}
-
-AtomMatcher CreateSyncStateChangedAtomMatcher(
-    const string& name, SyncStateChanged::State state) {
-    AtomMatcher atom_matcher;
-    atom_matcher.set_id(StringToId(name));
-    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
-    simple_atom_matcher->set_atom_id(android::util::SYNC_STATE_CHANGED);
-    auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
-    field_value_matcher->set_field(3);  // State field.
-    field_value_matcher->set_eq_int(state);
-    return atom_matcher;
-}
-
-AtomMatcher CreateSyncStartAtomMatcher() {
-    return CreateSyncStateChangedAtomMatcher("SyncStart", SyncStateChanged::ON);
-}
-
-AtomMatcher CreateSyncEndAtomMatcher() {
-    return CreateSyncStateChangedAtomMatcher("SyncEnd", SyncStateChanged::OFF);
-}
-
-AtomMatcher CreateActivityForegroundStateChangedAtomMatcher(
-    const string& name, ActivityForegroundStateChanged::State state) {
-    AtomMatcher atom_matcher;
-    atom_matcher.set_id(StringToId(name));
-    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
-    simple_atom_matcher->set_atom_id(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
-    auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
-    field_value_matcher->set_field(4);  // Activity field.
-    field_value_matcher->set_eq_int(state);
-    return atom_matcher;
-}
-
-AtomMatcher CreateMoveToBackgroundAtomMatcher() {
-    return CreateActivityForegroundStateChangedAtomMatcher(
-        "MoveToBackground", ActivityForegroundStateChanged::BACKGROUND);
-}
-
-AtomMatcher CreateMoveToForegroundAtomMatcher() {
-    return CreateActivityForegroundStateChangedAtomMatcher(
-        "MoveToForeground", ActivityForegroundStateChanged::FOREGROUND);
-}
-
-Predicate CreateScheduledJobPredicate() {
-    Predicate predicate;
-    predicate.set_id(StringToId("ScheduledJobRunningPredicate"));
-    predicate.mutable_simple_predicate()->set_start(StringToId("ScheduledJobStart"));
-    predicate.mutable_simple_predicate()->set_stop(StringToId("ScheduledJobFinish"));
-    return predicate;
-}
-
-Predicate CreateBatterySaverModePredicate() {
-    Predicate predicate;
-    predicate.set_id(StringToId("BatterySaverIsOn"));
-    predicate.mutable_simple_predicate()->set_start(StringToId("BatterySaverModeStart"));
-    predicate.mutable_simple_predicate()->set_stop(StringToId("BatterySaverModeStop"));
-    return predicate;
-}
-
-Predicate CreateScreenIsOnPredicate() {
-    Predicate predicate;
-    predicate.set_id(StringToId("ScreenIsOn"));
-    predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOn"));
-    predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOff"));
-    return predicate;
-}
-
-Predicate CreateScreenIsOffPredicate() {
-    Predicate predicate;
-    predicate.set_id(1111123);
-    predicate.mutable_simple_predicate()->set_start(StringToId("ScreenTurnedOff"));
-    predicate.mutable_simple_predicate()->set_stop(StringToId("ScreenTurnedOn"));
-    return predicate;
-}
-
-Predicate CreateHoldingWakelockPredicate() {
-    Predicate predicate;
-    predicate.set_id(StringToId("HoldingWakelock"));
-    predicate.mutable_simple_predicate()->set_start(StringToId("AcquireWakelock"));
-    predicate.mutable_simple_predicate()->set_stop(StringToId("ReleaseWakelock"));
-    return predicate;
-}
-
-Predicate CreateIsSyncingPredicate() {
-    Predicate predicate;
-    predicate.set_id(33333333333333);
-    predicate.mutable_simple_predicate()->set_start(StringToId("SyncStart"));
-    predicate.mutable_simple_predicate()->set_stop(StringToId("SyncEnd"));
-    return predicate;
-}
-
-Predicate CreateIsInBackgroundPredicate() {
-    Predicate predicate;
-    predicate.set_id(StringToId("IsInBackground"));
-    predicate.mutable_simple_predicate()->set_start(StringToId("MoveToBackground"));
-    predicate.mutable_simple_predicate()->set_stop(StringToId("MoveToForeground"));
-    return predicate;
-}
-
-void addPredicateToPredicateCombination(const Predicate& predicate,
-                                        Predicate* combinationPredicate) {
-    combinationPredicate->mutable_combination()->add_predicate(predicate.id());
-}
-
-FieldMatcher CreateAttributionUidDimensions(const int atomId,
-                                            const std::vector<Position>& positions) {
-    FieldMatcher dimensions;
-    dimensions.set_field(atomId);
-    for (const auto position : positions) {
-        auto child = dimensions.add_child();
-        child->set_field(1);
-        child->set_position(position);
-        child->add_child()->set_field(1);
-    }
-    return dimensions;
-}
-
-FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId,
-                                                 const std::vector<Position>& positions) {
-    FieldMatcher dimensions;
-    dimensions.set_field(atomId);
-    for (const auto position : positions) {
-        auto child = dimensions.add_child();
-        child->set_field(1);
-        child->set_position(position);
-        child->add_child()->set_field(1);
-        child->add_child()->set_field(2);
-    }
-    return dimensions;
-}
-
-FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields) {
-    FieldMatcher dimensions;
-    dimensions.set_field(atomId);
-    for (const int field : fields) {
-        dimensions.add_child()->set_field(field);
-    }
-    return dimensions;
-}
-
-void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
-                      const vector<string>& attributionTags) {
-    vector<const char*> cTags(attributionTags.size());
-    for (int i = 0; i < cTags.size(); i++) {
-        cTags[i] = attributionTags[i].c_str();
-    }
-
-    AStatsEvent_writeAttributionChain(statsEvent,
-                                      reinterpret_cast<const uint32_t*>(attributionUids.data()),
-                                      cTags.data(), attributionUids.size());
-}
-
-void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent) {
-    AStatsEvent_build(statsEvent);
-
-    size_t size;
-    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
-    logEvent->parseBuffer(buf, size);
-
-    AStatsEvent_release(statsEvent);
-}
-
-std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
-        uint64_t timestampNs, const android::view::DisplayStateEnum state) {
-    AStatsEvent* statsEvent = AStatsEvent_obtain();
-    AStatsEvent_setAtomId(statsEvent, util::SCREEN_STATE_CHANGED);
-    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-    AStatsEvent_writeInt32(statsEvent, state);
-
-    std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
-    parseStatsEventToLogEvent(statsEvent, logEvent.get());
-    return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateScheduledJobStateChangedEvent(
-        const vector<int>& attributionUids, const vector<string>& attributionTags,
-        const string& jobName, const ScheduledJobStateChanged::State state, uint64_t timestampNs) {
-    AStatsEvent* statsEvent = AStatsEvent_obtain();
-    AStatsEvent_setAtomId(statsEvent, util::SCHEDULED_JOB_STATE_CHANGED);
-    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
-    writeAttribution(statsEvent, attributionUids, attributionTags);
-    AStatsEvent_writeString(statsEvent, jobName.c_str());
-    AStatsEvent_writeInt32(statsEvent, state);
-
-    std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
-    parseStatsEventToLogEvent(statsEvent, logEvent.get());
-    return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs,
-                                                       const vector<int>& attributionUids,
-                                                       const vector<string>& attributionTags,
-                                                       const string& jobName) {
-    return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName,
-                                               ScheduledJobStateChanged::STARTED, timestampNs);
-}
-
-// Create log event when scheduled job finishes.
-std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs,
-                                                        const vector<int>& attributionUids,
-                                                        const vector<string>& attributionTags,
-                                                        const string& jobName) {
-    return CreateScheduledJobStateChangedEvent(attributionUids, attributionTags, jobName,
-                                               ScheduledJobStateChanged::FINISHED, timestampNs);
-}
-
-std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(uint64_t timestampNs,
-                                                      const vector<int>& attributionUids,
-                                                      const vector<string>& attributionTags,
-                                                      const string& name,
-                                                      const SyncStateChanged::State state) {
-    AStatsEvent* statsEvent = AStatsEvent_obtain();
-    AStatsEvent_setAtomId(statsEvent, util::SYNC_STATE_CHANGED);
-    AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
-
-    writeAttribution(statsEvent, attributionUids, attributionTags);
-    AStatsEvent_writeString(statsEvent, name.c_str());
-    AStatsEvent_writeInt32(statsEvent, state);
-
-    std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
-    parseStatsEventToLogEvent(statsEvent, logEvent.get());
-    return logEvent;
-}
-
-std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs,
-                                               const vector<int>& attributionUids,
-                                               const vector<string>& attributionTags,
-                                               const string& name) {
-    return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name,
-                                       SyncStateChanged::ON);
-}
-
-std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs,
-                                             const vector<int>& attributionUids,
-                                             const vector<string>& attributionTags,
-                                             const string& name) {
-    return CreateSyncStateChangedEvent(timestampNs, attributionUids, attributionTags, name,
-                                       SyncStateChanged::OFF);
-}
-
-sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
-                                              const ConfigKey& key) {
-    sp<UidMap> uidMap = new UidMap();
-    sp<StatsPullerManager> pullerManager = new StatsPullerManager();
-    sp<AlarmMonitor> anomalyAlarmMonitor;
-    sp<AlarmMonitor> periodicAlarmMonitor;
-    sp<StatsLogProcessor> processor = new StatsLogProcessor(
-            uidMap, pullerManager, anomalyAlarmMonitor, periodicAlarmMonitor,
-            timeBaseSec * NS_PER_SEC, [](const ConfigKey&) { return true; },
-            [](const int&, const vector<int64_t>&) { return true; },
-            [](const ConfigKey&, const string&, const vector<int64_t>&) {},
-            std::make_shared<LogEventFilter>());
-    processor->OnConfigUpdated(timeBaseSec * NS_PER_SEC, key, config);
-    return processor;
-}
-
-void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events) {
-  std::sort(events->begin(), events->end(),
-            [](const std::unique_ptr<LogEvent>& a, const std::unique_ptr<LogEvent>& b) {
-              return a->GetElapsedTimestampNs() < b->GetElapsedTimestampNs();
-            });
-}
-
-int64_t StringToId(const string& str) {
-    return static_cast<int64_t>(std::hash<std::string>()(str));
-}
-
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/statsd/benchmark/metric_util.h b/statsd/benchmark/metric_util.h
deleted file mode 100644
index 693bf45..0000000
--- a/statsd/benchmark/metric_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 "src/stats_log.pb.h"
-#include "src/statsd_config.pb.h"
-#include "src/StatsLogProcessor.h"
-#include "src/logd/LogEvent.h"
-#include "stats_event.h"
-#include "statslog.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// Create AtomMatcher proto to simply match a specific atom type.
-AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId);
-
-// Create AtomMatcher proto for scheduled job state changed.
-AtomMatcher CreateScheduledJobStateChangedAtomMatcher();
-
-// Create AtomMatcher proto for starting a scheduled job.
-AtomMatcher CreateStartScheduledJobAtomMatcher();
-
-// Create AtomMatcher proto for a scheduled job is done.
-AtomMatcher CreateFinishScheduledJobAtomMatcher();
-
-// Create AtomMatcher proto for screen brightness state changed.
-AtomMatcher CreateScreenBrightnessChangedAtomMatcher();
-
-// Create AtomMatcher proto for acquiring wakelock.
-AtomMatcher CreateAcquireWakelockAtomMatcher();
-
-// Create AtomMatcher proto for releasing wakelock.
-AtomMatcher CreateReleaseWakelockAtomMatcher() ;
-
-// Create AtomMatcher proto for screen turned on.
-AtomMatcher CreateScreenTurnedOnAtomMatcher();
-
-// Create AtomMatcher proto for screen turned off.
-AtomMatcher CreateScreenTurnedOffAtomMatcher();
-
-// Create AtomMatcher proto for app sync turned on.
-AtomMatcher CreateSyncStartAtomMatcher();
-
-// Create AtomMatcher proto for app sync turned off.
-AtomMatcher CreateSyncEndAtomMatcher();
-
-// Create AtomMatcher proto for app sync moves to background.
-AtomMatcher CreateMoveToBackgroundAtomMatcher();
-
-// Create AtomMatcher proto for app sync moves to foreground.
-AtomMatcher CreateMoveToForegroundAtomMatcher();
-
-// Create Predicate proto for screen is off.
-Predicate CreateScreenIsOffPredicate();
-
-// Create Predicate proto for a running scheduled job.
-Predicate CreateScheduledJobPredicate();
-
-// Create Predicate proto for holding wakelock.
-Predicate CreateHoldingWakelockPredicate();
-
-// Create a Predicate proto for app syncing.
-Predicate CreateIsSyncingPredicate();
-
-// Create a Predicate proto for app is in background.
-Predicate CreateIsInBackgroundPredicate();
-
-// Add a predicate to the predicate combination.
-void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination);
-
-// Create dimensions from primitive fields.
-FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields);
-
-// Create dimensions by attribution uid and tag.
-FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId,
-                                                  const std::vector<Position>& positions);
-
-// Create dimensions by attribution uid only.
-FieldMatcher CreateAttributionUidDimensions(const int atomId,
-                                            const std::vector<Position>& positions);
-
-void writeAttribution(AStatsEvent* statsEvent, const vector<int>& attributionUids,
-                      const vector<string>& attributionTags);
-
-void parseStatsEventToLogEvent(AStatsEvent* statsEvent, LogEvent* logEvent);
-
-// Create log event for screen state changed.
-std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
-        uint64_t timestampNs, const android::view::DisplayStateEnum state);
-
-// Create log event when scheduled job starts.
-std::unique_ptr<LogEvent> CreateStartScheduledJobEvent(uint64_t timestampNs,
-                                                       const vector<int>& attributionUids,
-                                                       const vector<string>& attributionTags,
-                                                       const string& jobName);
-
-// Create log event when scheduled job finishes.
-std::unique_ptr<LogEvent> CreateFinishScheduledJobEvent(uint64_t timestampNs,
-                                                        const vector<int>& attributionUids,
-                                                        const vector<string>& attributionTags,
-                                                        const string& jobName);
-
-// Create log event when the app sync starts.
-std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs,
-                                               const vector<int>& attributionUids,
-                                               const vector<string>& attributionTags,
-                                               const string& name);
-
-// Create log event when the app sync ends.
-std::unique_ptr<LogEvent> CreateSyncEndEvent(uint64_t timestampNs,
-                                             const vector<int>& attributionUids,
-                                             const vector<string>& attributionTags,
-                                             const string& name);
-
-// Create a statsd log event processor upon the start time in seconds, config and key.
-sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
-                                              const ConfigKey& key);
-
-// Util function to sort the log events by timestamp.
-void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events);
-
-int64_t StringToId(const string& str);
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/statsd/benchmark/on_log_event_benchmark.cpp b/statsd/benchmark/on_log_event_benchmark.cpp
new file mode 100644
index 0000000..d3e5709
--- /dev/null
+++ b/statsd/benchmark/on_log_event_benchmark.cpp
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2024 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 "benchmark/benchmark.h"
+#include "tests/statsd_test_util.h"
+
+using namespace std;
+namespace android {
+namespace os {
+namespace statsd {
+
+static void BM_OnLogEvent(benchmark::State& state) {
+    StatsdConfig config;
+    auto wakelockAcquireMatcher = CreateAcquireWakelockAtomMatcher();
+    *config.add_atom_matcher() = wakelockAcquireMatcher;
+
+    *config.add_event_metric() =
+            createEventMetric("Event", wakelockAcquireMatcher.id(), /* condition */ nullopt);
+
+    for (int atomId = 1000; atomId < 2000; atomId++) {
+        auto matcher = CreateSimpleAtomMatcher("name" + to_string(atomId), atomId);
+        *config.add_atom_matcher() = CreateSimpleAtomMatcher("name" + to_string(atomId), atomId);
+        *config.add_event_metric() = createEventMetric("Event" + to_string(atomId), matcher.id(),
+                                                       /* condition */ nullopt);
+    }
+
+    ConfigKey cfgKey;
+    std::vector<std::unique_ptr<LogEvent>> events;
+    vector<int> attributionUids = {111};
+    vector<string> attributionTags = {"App1"};
+    for (int i = 1; i <= 10; i++) {
+        events.push_back(CreateAcquireWakelockEvent(2 + i, attributionUids, attributionTags,
+                                                    "wl" + to_string(i)));
+    }
+
+    sp<StatsLogProcessor> processor = CreateStatsLogProcessor(1, 1, config, cfgKey);
+
+    for (auto _ : state) {
+        for (const auto& event : events) {
+            processor->OnLogEvent(event.get());
+        }
+    }
+}
+BENCHMARK(BM_OnLogEvent);
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/statsd/benchmark/stats_write_benchmark.cpp b/statsd/benchmark/stats_write_benchmark.cpp
index d817b12..b84b0cd 100644
--- a/statsd/benchmark/stats_write_benchmark.cpp
+++ b/statsd/benchmark/stats_write_benchmark.cpp
@@ -13,7 +13,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#include <statslog.h>
+#include <statslog_statsdtest.h>
 
 #include "benchmark/benchmark.h"
 
@@ -31,16 +31,11 @@
 BENCHMARK(BM_StatsEventObtain);
 
 static void BM_StatsWrite(benchmark::State& state) {
-    const char* reason = "test";
-    int64_t boot_end_time = 1234567;
-    int64_t total_duration = 100;
-    int64_t bootloader_duration = 10;
-    int64_t time_since_last_boot = 99999999;
+    int32_t parent_uid = 0;
+    int32_t isolated_uid = 100;
+    int32_t event = 1;
     while (state.KeepRunning()) {
-        android::util::stats_write(
-                android::util::BOOT_SEQUENCE_REPORTED, reason, reason,
-                boot_end_time, total_duration, bootloader_duration, time_since_last_boot);
-        total_duration++;
+        util::stats_write(util::ISOLATED_UID_CHANGED, parent_uid, isolated_uid, event++);
     }
 }
 BENCHMARK(BM_StatsWrite);
@@ -50,10 +45,9 @@
     int32_t uid = 0;
     int32_t label = 100;
     int32_t a_state = 1;
-    // TODO: choose atom with a same structure as used in BM_StatsWrite
     while (state.KeepRunning()) {
-        benchmark::DoNotOptimize(android::util::stats_write(android::util::APP_BREADCRUMB_REPORTED,
-                                                            uid, label, a_state++));
+        benchmark::DoNotOptimize(
+                util::stats_write(util::APP_BREADCRUMB_REPORTED, uid, label, a_state++));
     }
 }
 BENCHMARK(BM_StatsWriteViaQueue);
diff --git a/statsd/fuzzers/statsd_service_fuzzer.cpp b/statsd/fuzzers/statsd_service_fuzzer.cpp
new file mode 100644
index 0000000..7baca73
--- /dev/null
+++ b/statsd/fuzzers/statsd_service_fuzzer.cpp
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2023 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 <android/binder_interface_utils.h>
+#include <fuzzbinder/libbinder_ndk_driver.h>
+
+#include "StatsService.h"
+#include "packages/UidMap.h"
+
+using namespace android;
+using namespace android::os::statsd;
+using ndk::SharedRefBase;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+    std::shared_ptr<LogEventFilter> logEventFilter = std::make_shared<LogEventFilter>();
+    std::shared_ptr<LogEventQueue> eventQueue =
+            std::make_shared<LogEventQueue>(8000 /*buffer limit. Same as StatsD binary*/);
+    sp<UidMap> uidMap = UidMap::getInstance();
+    shared_ptr<StatsService> binder =
+            SharedRefBase::make<StatsService>(uidMap, eventQueue, logEventFilter);
+    fuzzService(binder->asBinder().get(), FuzzedDataProvider(data, size));
+    return 0;
+}
diff --git a/statsd/src/FieldValue.cpp b/statsd/src/FieldValue.cpp
index f3c20c0..aa129a4 100644
--- a/statsd/src/FieldValue.cpp
+++ b/statsd/src/FieldValue.cpp
@@ -348,28 +348,30 @@
 }
 
 Value& Value::operator=(const Value& that) {
-    type = that.type;
-    switch (type) {
-        case INT:
-            int_value = that.int_value;
-            break;
-        case LONG:
-            long_value = that.long_value;
-            break;
-        case FLOAT:
-            float_value = that.float_value;
-            break;
-        case DOUBLE:
-            double_value = that.double_value;
-            break;
-        case STRING:
-            str_value = that.str_value;
-            break;
-        case STORAGE:
-            storage_value = that.storage_value;
-            break;
-        default:
-            break;
+    if (this != &that) {
+        type = that.type;
+        switch (type) {
+            case INT:
+                int_value = that.int_value;
+                break;
+            case LONG:
+                long_value = that.long_value;
+                break;
+            case FLOAT:
+                float_value = that.float_value;
+                break;
+            case DOUBLE:
+                double_value = that.double_value;
+                break;
+            case STRING:
+                str_value = that.str_value;
+                break;
+            case STORAGE:
+                storage_value = that.storage_value;
+                break;
+            default:
+                break;
+        }
     }
     return *this;
 }
diff --git a/statsd/src/HashableDimensionKey.h b/statsd/src/HashableDimensionKey.h
index 3b7e25a..5753d98 100644
--- a/statsd/src/HashableDimensionKey.h
+++ b/statsd/src/HashableDimensionKey.h
@@ -142,7 +142,7 @@
 
 class AtomDimensionKey {
 public:
-    explicit AtomDimensionKey(const int32_t atomTag, const HashableDimensionKey& atomFieldValues)
+    explicit AtomDimensionKey(int32_t atomTag, const HashableDimensionKey& atomFieldValues)
         : mAtomTag(atomTag), mAtomFieldValues(atomFieldValues){};
 
     AtomDimensionKey(){};
diff --git a/statsd/src/StatsLogProcessor.cpp b/statsd/src/StatsLogProcessor.cpp
index 841c5c1..f7d7091 100644
--- a/statsd/src/StatsLogProcessor.cpp
+++ b/statsd/src/StatsLogProcessor.cpp
@@ -72,7 +72,7 @@
 const int FIELD_ID_CURRENT_REPORT_WALL_CLOCK_NANOS = 6;
 const int FIELD_ID_DUMP_REPORT_REASON = 8;
 const int FIELD_ID_STRINGS = 9;
-const int FIELD_ID_DATA_CORRUPTED_REASON = 10;
+const int FIELD_ID_DATA_CORRUPTED_REASON = 11;
 
 // for ActiveConfigList
 const int FIELD_ID_ACTIVE_CONFIG_LIST_CONFIG = 1;
@@ -134,14 +134,14 @@
 }
 
 void StatsLogProcessor::processFiredAnomalyAlarmsLocked(
-        const int64_t& timestampNs,
+        const int64_t timestampNs,
         unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
     for (const auto& itr : mMetricsManagers) {
         itr.second->onAnomalyAlarmFired(timestampNs, alarmSet);
     }
 }
 void StatsLogProcessor::onPeriodicAlarmFired(
-        const int64_t& timestampNs,
+        const int64_t timestampNs,
         unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
     for (const auto& itr : mMetricsManagers) {
@@ -639,10 +639,10 @@
     return it->second->byteSize();
 }
 
-void StatsLogProcessor::dumpStates(int out, bool verbose) {
+void StatsLogProcessor::dumpStates(int out, bool verbose) const {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
     dprintf(out, "MetricsManager count: %lu\n", (unsigned long)mMetricsManagers.size());
-    for (auto metricsManager : mMetricsManagers) {
+    for (const auto& metricsManager : mMetricsManagers) {
         metricsManager.second->dumpStates(out, verbose);
     }
 }
@@ -866,7 +866,7 @@
 
     int uid = key.GetUid();
     bool lastConfigForUid = true;
-    for (auto it : mMetricsManagers) {
+    for (const auto& it : mMetricsManagers) {
         if (it.first.GetUid() == uid) {
             lastConfigForUid = false;
             break;
@@ -1415,7 +1415,7 @@
     }
 }
 
-void StatsLogProcessor::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk,
+void StatsLogProcessor::notifyAppUpgrade(const int64_t eventTimeNs, const string& apk,
                                          const int uid, const int64_t version) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
     VLOG("Received app upgrade");
@@ -1425,7 +1425,7 @@
     }
 }
 
-void StatsLogProcessor::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk,
+void StatsLogProcessor::notifyAppRemoved(const int64_t eventTimeNs, const string& apk,
                                          const int uid) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
     VLOG("Received app removed");
@@ -1435,7 +1435,7 @@
     }
 }
 
-void StatsLogProcessor::onUidMapReceived(const int64_t& eventTimeNs) {
+void StatsLogProcessor::onUidMapReceived(const int64_t eventTimeNs) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
     VLOG("Received uid map");
     StateManager::getInstance().updateLogSources(mUidMap);
@@ -1444,7 +1444,7 @@
     }
 }
 
-void StatsLogProcessor::onStatsdInitCompleted(const int64_t& elapsedTimeNs) {
+void StatsLogProcessor::onStatsdInitCompleted(const int64_t elapsedTimeNs) {
     std::lock_guard<std::mutex> lock(mMetricsMutex);
     VLOG("Received boot completed signal");
     for (const auto& it : mMetricsManagers) {
diff --git a/statsd/src/StatsLogProcessor.h b/statsd/src/StatsLogProcessor.h
index 1449911..31dea55 100644
--- a/statsd/src/StatsLogProcessor.h
+++ b/statsd/src/StatsLogProcessor.h
@@ -40,7 +40,7 @@
     StatsLogProcessor(
             const sp<UidMap>& uidMap, const sp<StatsPullerManager>& pullerManager,
             const sp<AlarmMonitor>& anomalyAlarmMonitor,
-            const sp<AlarmMonitor>& subscriberTriggerAlarmMonitor, const int64_t timeBaseNs,
+            const sp<AlarmMonitor>& subscriberTriggerAlarmMonitor, int64_t timeBaseNs,
             const std::function<bool(const ConfigKey&)>& sendBroadcast,
             const std::function<bool(const int&, const vector<int64_t>&)>& sendActivationBroadcast,
             const std::function<void(const ConfigKey&, const string&, const vector<int64_t>&)>&
@@ -51,7 +51,7 @@
 
     void OnLogEvent(LogEvent* event);
 
-    void OnConfigUpdated(const int64_t timestampNs, const int64_t wallClockNs, const ConfigKey& key,
+    void OnConfigUpdated(const int64_t timestampNs, int64_t wallClockNs, const ConfigKey& key,
                          const StatsdConfig& config, bool modularUpdate = true);
     // For testing only.
     void OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key,
@@ -62,28 +62,28 @@
 
     void GetActiveConfigs(const int uid, vector<int64_t>& outActiveConfigs);
 
-    void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs, const int64_t wallClockNs,
+    void onDumpReport(const ConfigKey& key, int64_t dumpTimeNs, int64_t wallClockNs,
                       const bool include_current_partial_bucket, const bool erase_data,
                       const DumpReportReason dumpReportReason, const DumpLatency dumpLatency,
                       vector<uint8_t>* outData);
-    void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs, const int64_t wallClockNs,
+    void onDumpReport(const ConfigKey& key, int64_t dumpTimeNs, int64_t wallClockNs,
                       const bool include_current_partial_bucket, const bool erase_data,
                       const DumpReportReason dumpReportReason, const DumpLatency dumpLatency,
                       ProtoOutputStream* proto);
     // For testing only.
-    void onDumpReport(const ConfigKey& key, const int64_t dumpTimeNs,
+    void onDumpReport(const ConfigKey& key, int64_t dumpTimeNs,
                       const bool include_current_partial_bucket, const bool erase_data,
                       const DumpReportReason dumpReportReason, const DumpLatency dumpLatency,
                       vector<uint8_t>* outData);
 
     /* Tells MetricsManager that the alarms in alarmSet have fired. Modifies periodic alarmSet. */
     void onPeriodicAlarmFired(
-            const int64_t& timestampNs,
+            int64_t timestampNs,
             unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
 
     /* Flushes data to disk. Data on memory will be gone after written to disk. */
     void WriteDataToDisk(const DumpReportReason dumpReportReason, const DumpLatency dumpLatency,
-                         const int64_t elapsedRealtimeNs, const int64_t wallClockNs);
+                         const int64_t elapsedRealtimeNs, int64_t wallClockNs);
 
     /* Persist configs containing metrics with active activations to disk. */
     void SaveActiveConfigsToDisk(int64_t currentTimeNs);
@@ -113,25 +113,25 @@
                           int64_t systemElapsedTimeNs);
 
     /* Enforces ttls for restricted metrics */
-    void EnforceDataTtls(const int64_t wallClockNs, const int64_t elapsedRealtimeNs);
+    void EnforceDataTtls(const int64_t wallClockNs, int64_t elapsedRealtimeNs);
 
     /* Sets the active status/ttl for all configs and metrics to the status in ActiveConfigList. */
     void SetConfigsActiveState(const ActiveConfigList& activeConfigList, int64_t currentTimeNs);
 
     /* Notify all MetricsManagers of app upgrades */
-    void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
-                          const int64_t version) override;
+    void notifyAppUpgrade(int64_t eventTimeNs, const string& apk, int uid,
+                          int64_t version) override;
 
     /* Notify all MetricsManagers of app removals */
-    void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid) override;
+    void notifyAppRemoved(int64_t eventTimeNs, const string& apk, int uid) override;
 
     /* Notify all MetricsManagers of uid map snapshots received */
-    void onUidMapReceived(const int64_t& eventTimeNs) override;
+    void onUidMapReceived(int64_t eventTimeNs) override;
 
     /* Notify all metrics managers of boot completed
      * This will force a bucket split when the boot is finished.
      */
-    void onStatsdInitCompleted(const int64_t& elapsedTimeNs);
+    void onStatsdInitCompleted(int64_t elapsedTimeNs);
 
     // Reset all configs.
     void resetConfigs();
@@ -140,7 +140,7 @@
         return mUidMap;
     }
 
-    void dumpStates(int outFd, bool verbose);
+    void dumpStates(int outFd, bool verbose) const;
 
     void informPullAlarmFired(const int64_t timestampNs);
 
@@ -248,15 +248,15 @@
                                     metadata::StatsMetadataList* metadataList);
 
     void WriteDataToDiskLocked(const DumpReportReason dumpReportReason,
-                               const DumpLatency dumpLatency, const int64_t elapsedRealtimeNs,
+                               const DumpLatency dumpLatency, int64_t elapsedRealtimeNs,
                                const int64_t wallClockNs);
 
-    void WriteDataToDiskLocked(const ConfigKey& key, const int64_t timestampNs,
-                               const int64_t wallClockNs, const DumpReportReason dumpReportReason,
+    void WriteDataToDiskLocked(const ConfigKey& key, int64_t timestampNs, const int64_t wallClockNs,
+                               const DumpReportReason dumpReportReason,
                                const DumpLatency dumpLatency);
 
     void onConfigMetricsReportLocked(
-            const ConfigKey& key, const int64_t dumpTimeStampNs, const int64_t wallClockNs,
+            const ConfigKey& key, int64_t dumpTimeStampNs, int64_t wallClockNs,
             const bool include_current_partial_bucket, const bool erase_data,
             const DumpReportReason dumpReportReason, const DumpLatency dumpLatency,
             /*if dataSavedToDisk is true, it indicates the caller will write the data to disk
@@ -269,7 +269,7 @@
                                           const int64_t elapsedRealtimeNs);
 
     // Enforces ttls on all restricted metrics.
-    void enforceDataTtlsLocked(const int64_t wallClockNs, const int64_t elapsedRealtimeNs);
+    void enforceDataTtlsLocked(const int64_t wallClockNs, int64_t elapsedRealtimeNs);
 
     // Enforces that dbs are within guardrail parameters.
     void enforceDbGuardrailsIfNecessaryLocked(const int64_t wallClockNs,
@@ -279,8 +279,7 @@
      * actually delete the data. */
     void flushIfNecessaryLocked(const ConfigKey& key, MetricsManager& metricsManager);
 
-    set<ConfigKey> getRestrictedConfigKeysToQueryLocked(const int32_t callingUid,
-                                                        const int64_t configId,
+    set<ConfigKey> getRestrictedConfigKeysToQueryLocked(int32_t callingUid, const int64_t configId,
                                                         const set<int32_t>& configPackageUids,
                                                         string& err,
                                                         InvalidQueryReason& invalidQueryReason);
@@ -304,8 +303,8 @@
     // Gets experiment ids on disk for associated train and updates them
     // depending on rollback type. Then writes them back to disk and returns
     // them.
-    std::vector<int64_t> processWatchdogRollbackOccurred(const int32_t rollbackTypeIn,
-                                                          const string& packageName);
+    std::vector<int64_t> processWatchdogRollbackOccurred(int32_t rollbackTypeIn,
+                                                         const string& packageName);
 
     // Reset all configs.
     void resetConfigsLocked(const int64_t timestampNs);
@@ -318,7 +317,7 @@
 
     /* Tells MetricsManager that the alarms in alarmSet have fired. Modifies anomaly alarmSet. */
     void processFiredAnomalyAlarmsLocked(
-            const int64_t& timestampNs,
+            int64_t timestampNs,
             unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
 
     void flushRestrictedDataLocked(const int64_t elapsedRealtimeNs);
@@ -488,6 +487,10 @@
     FRIEND_TEST(KllMetricE2eTest, TestInitWithKllFieldPositionALL);
 
     FRIEND_TEST(StatsServiceStatsdInitTest, StatsServiceStatsdInitTest);
+
+    FRIEND_TEST(StringReplaceE2eTest, TestPulledDimension);
+    FRIEND_TEST(StringReplaceE2eTest, TestPulledWhat);
+    FRIEND_TEST(StringReplaceE2eTest, TestMultipleMatchersForAtom);
 };
 
 }  // namespace statsd
diff --git a/statsd/src/StatsService.cpp b/statsd/src/StatsService.cpp
index 8b2c218..86c2de8 100644
--- a/statsd/src/StatsService.cpp
+++ b/statsd/src/StatsService.cpp
@@ -876,8 +876,8 @@
     int32_t state = atoi(args[6].c_str());
     vector<int64_t> experimentIds;
     if (argCount == 8) {
-        vector<string> experimentIdsString = android::base::Split(string(args[7].c_str()), ",");
-        for (string experimentIdString : experimentIdsString) {
+        vector<string> experimentIdsStrings = android::base::Split(string(args[7].c_str()), ",");
+        for (const string& experimentIdString : experimentIdsStrings) {
             int64_t experimentId = strtoll(experimentIdString.c_str(), nullptr, 10);
             experimentIds.push_back(experimentId);
         }
@@ -1514,9 +1514,8 @@
     mIsStopRequested = true;
     // Push this event so that readLogs will process and break out of the loop
     // after the stop is requested.
-    int64_t timeStamp;
     std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
-    mEventQueue->push(std::move(logEvent), &timeStamp);
+    mEventQueue->push(std::move(logEvent));
 }
 
 }  // namespace statsd
diff --git a/statsd/src/StatsService.h b/statsd/src/StatsService.h
index c170e00..3c34492 100644
--- a/statsd/src/StatsService.h
+++ b/statsd/src/StatsService.h
@@ -146,7 +146,7 @@
     /**
      * Binder call to remove the active configs changed operation for the specified package..
      */
-    virtual Status removeActiveConfigsChangedOperation(const int32_t callingUid) override;
+    virtual Status removeActiveConfigsChangedOperation(int32_t callingUid) override;
     /**
      * Binder call to allow clients to remove the specified configuration.
      */
diff --git a/statsd/src/anomaly/AlarmMonitor.cpp b/statsd/src/anomaly/AlarmMonitor.cpp
index 4822b54..6359c5f 100644
--- a/statsd/src/anomaly/AlarmMonitor.cpp
+++ b/statsd/src/anomaly/AlarmMonitor.cpp
@@ -36,7 +36,7 @@
 AlarmMonitor::~AlarmMonitor() {}
 
 void AlarmMonitor::setStatsCompanionService(
-        shared_ptr<IStatsCompanionService> statsCompanionService) {
+        const shared_ptr<IStatsCompanionService>& statsCompanionService) {
     std::lock_guard<std::mutex> lock(mLock);
     shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
     mStatsCompanionService = statsCompanionService;
@@ -51,7 +51,7 @@
     }
 }
 
-void AlarmMonitor::add(sp<const InternalAlarm> alarm) {
+void AlarmMonitor::add(const sp<const InternalAlarm>& alarm) {
     std::lock_guard<std::mutex> lock(mLock);
     if (alarm == nullptr) {
         ALOGW("Asked to add a null alarm.");
@@ -71,7 +71,7 @@
     }
 }
 
-void AlarmMonitor::remove(sp<const InternalAlarm> alarm) {
+void AlarmMonitor::remove(const sp<const InternalAlarm>& alarm) {
     std::lock_guard<std::mutex> lock(mLock);
     if (alarm == nullptr) {
         ALOGW("Asked to remove a null alarm.");
diff --git a/statsd/src/anomaly/AlarmMonitor.h b/statsd/src/anomaly/AlarmMonitor.h
index 5c34e38..ee955c9 100644
--- a/statsd/src/anomaly/AlarmMonitor.h
+++ b/statsd/src/anomaly/AlarmMonitor.h
@@ -49,7 +49,7 @@
 
     /** InternalAlarm a is smaller (higher priority) than b if its timestamp is sooner. */
     struct SmallerTimestamp {
-        bool operator()(sp<const InternalAlarm> a, sp<const InternalAlarm> b) const {
+        bool operator()(const sp<const InternalAlarm>& a, const sp<const InternalAlarm>& b) const {
             return (a->timestampSec < b->timestampSec);
         }
     };
@@ -77,19 +77,19 @@
      * If nullptr, AnomalyMonitor will continue to add/remove alarms, but won't
      * update IStatsCompanionService (until such time as it is set non-null).
      */
-    void setStatsCompanionService(shared_ptr<IStatsCompanionService> statsCompanionService);
+    void setStatsCompanionService(const shared_ptr<IStatsCompanionService>& statsCompanionService);
 
     /**
      * Adds the given alarm (reference) to the queue.
      */
-    void add(sp<const InternalAlarm> alarm);
+    void add(const sp<const InternalAlarm>& alarm);
 
     /**
      * Removes the given alarm (reference) from the queue.
      * Note that alarm comparison is reference-based; if another alarm exists
      * with the same timestampSec, that alarm will still remain in the queue.
      */
-    void remove(sp<const InternalAlarm> alarm);
+    void remove(const sp<const InternalAlarm>& alarm);
 
     /**
      * Returns and removes all alarms whose timestamp <= the given timestampSec.
diff --git a/statsd/src/anomaly/AlarmTracker.cpp b/statsd/src/anomaly/AlarmTracker.cpp
index 8e311d4..9d1aa10 100644
--- a/statsd/src/anomaly/AlarmTracker.cpp
+++ b/statsd/src/anomaly/AlarmTracker.cpp
@@ -69,7 +69,7 @@
 }
 
 void AlarmTracker::informAlarmsFired(
-        const int64_t& timestampNs,
+        const int64_t timestampNs,
         unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) {
     if (firedAlarms.empty() || mInternalAlarm == nullptr ||
         firedAlarms.find(mInternalAlarm) == firedAlarms.end()) {
diff --git a/statsd/src/anomaly/AlarmTracker.h b/statsd/src/anomaly/AlarmTracker.h
index 4b8fab3..c89099f 100644
--- a/statsd/src/anomaly/AlarmTracker.h
+++ b/statsd/src/anomaly/AlarmTracker.h
@@ -42,7 +42,8 @@
 
     void addSubscription(const Subscription& subscription);
 
-    void informAlarmsFired(const int64_t& timestampNs,
+    void informAlarmsFired(
+            int64_t timestampNs,
             unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms);
 
 protected:
diff --git a/statsd/src/anomaly/AnomalyTracker.cpp b/statsd/src/anomaly/AnomalyTracker.cpp
index 2e0d238..2829bff 100644
--- a/statsd/src/anomaly/AnomalyTracker.cpp
+++ b/statsd/src/anomaly/AnomalyTracker.cpp
@@ -63,7 +63,7 @@
     return bucketNum % mNumOfPastBuckets;
 }
 
-void AnomalyTracker::advanceMostRecentBucketTo(const int64_t& bucketNum) {
+void AnomalyTracker::advanceMostRecentBucketTo(const int64_t bucketNum) {
     VLOG("advanceMostRecentBucketTo() called.");
     if (mNumOfPastBuckets <= 0) {
         return;
@@ -89,9 +89,8 @@
     mMostRecentBucketNum = bucketNum;
 }
 
-void AnomalyTracker::addPastBucket(const MetricDimensionKey& key,
-                                   const int64_t& bucketValue,
-                                   const int64_t& bucketNum) {
+void AnomalyTracker::addPastBucket(const MetricDimensionKey& key, const int64_t bucketValue,
+                                   const int64_t bucketNum) {
     VLOG("addPastBucket(bucketValue) called.");
     if (mNumOfPastBuckets == 0 ||
         bucketNum < 0 || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets) {
@@ -119,8 +118,8 @@
     }
 }
 
-void AnomalyTracker::addPastBucket(std::shared_ptr<DimToValMap> bucket,
-                                   const int64_t& bucketNum) {
+void AnomalyTracker::addPastBucket(const std::shared_ptr<DimToValMap>& bucket,
+                                   const int64_t bucketNum) {
     VLOG("addPastBucket(bucket) called.");
     if (mNumOfPastBuckets == 0 ||
             bucketNum < 0 || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets) {
@@ -147,9 +146,8 @@
     }
 }
 
-
 void AnomalyTracker::subtractValueFromSum(const MetricDimensionKey& key,
-                                          const int64_t& bucketValue) {
+                                          const int64_t bucketValue) {
     auto itr = mSumOverPastBuckets.find(key);
     if (itr == mSumOverPastBuckets.end()) {
         return;
@@ -171,7 +169,7 @@
 }
 
 int64_t AnomalyTracker::getPastBucketValue(const MetricDimensionKey& key,
-                                           const int64_t& bucketNum) const {
+                                           const int64_t bucketNum) const {
     if (bucketNum < 0 || mMostRecentBucketNum < 0
             || bucketNum <= mMostRecentBucketNum - mNumOfPastBuckets
             || bucketNum > mMostRecentBucketNum) {
@@ -194,10 +192,8 @@
     return 0;
 }
 
-bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum,
-                                   const MetricDimensionKey& key,
-                                   const int64_t& currentBucketValue) {
-
+bool AnomalyTracker::detectAnomaly(const int64_t currentBucketNum, const MetricDimensionKey& key,
+                                   const int64_t currentBucketValue) {
     // currentBucketNum should be the next bucket after pastBuckets. If not, advance so that it is.
     if (currentBucketNum > mMostRecentBucketNum + 1) {
         advanceMostRecentBucketTo(currentBucketNum - 1);
@@ -206,7 +202,7 @@
            getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt();
 }
 
-void AnomalyTracker::declareAnomaly(const int64_t& timestampNs, int64_t metricId,
+void AnomalyTracker::declareAnomaly(const int64_t timestampNs, int64_t metricId,
                                     const MetricDimensionKey& key, int64_t metricValue) {
     // TODO(b/110563466): Why receive timestamp? RefractoryPeriod should always be based on
     // real time right now.
@@ -246,16 +242,15 @@
     StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id());
 }
 
-void AnomalyTracker::detectAndDeclareAnomaly(const int64_t& timestampNs,
-                                             const int64_t& currBucketNum, int64_t metricId,
-                                             const MetricDimensionKey& key,
-                                             const int64_t& currentBucketValue) {
+void AnomalyTracker::detectAndDeclareAnomaly(const int64_t timestampNs, const int64_t currBucketNum,
+                                             int64_t metricId, const MetricDimensionKey& key,
+                                             const int64_t currentBucketValue) {
     if (detectAnomaly(currBucketNum, key, currentBucketValue)) {
         declareAnomaly(timestampNs, metricId, key, currentBucketValue);
     }
 }
 
-bool AnomalyTracker::isInRefractoryPeriod(const int64_t& timestampNs,
+bool AnomalyTracker::isInRefractoryPeriod(const int64_t timestampNs,
                                           const MetricDimensionKey& key) const {
     const auto& it = mRefractoryPeriodEndsSec.find(key);
     if (it != mRefractoryPeriodEndsSec.end()) {
diff --git a/statsd/src/anomaly/AnomalyTracker.h b/statsd/src/anomaly/AnomalyTracker.h
index 92a7173..4a4f7ff 100644
--- a/statsd/src/anomaly/AnomalyTracker.h
+++ b/statsd/src/anomaly/AnomalyTracker.h
@@ -55,32 +55,30 @@
     // If a bucket for bucketNum already exists, it will be replaced.
     // Also, advances to bucketNum (if not in the past), effectively filling any intervening
     // buckets with 0s.
-    void addPastBucket(std::shared_ptr<DimToValMap> bucket, const int64_t& bucketNum);
+    void addPastBucket(const std::shared_ptr<DimToValMap>& bucket, const int64_t bucketNum);
 
     // Inserts (or replaces) the bucket entry for the given bucketNum at the given key to be the
     // given bucketValue. If the bucket does not exist, it will be created.
     // Also, advances to bucketNum (if not in the past), effectively filling any intervening
     // buckets with 0s.
-    void addPastBucket(const MetricDimensionKey& key, const int64_t& bucketValue,
-                       const int64_t& bucketNum);
+    void addPastBucket(const MetricDimensionKey& key, int64_t bucketValue, int64_t bucketNum);
 
     // Returns true if, based on past buckets plus the new currentBucketValue (which generally
     // represents the partially-filled current bucket), an anomaly has happened.
     // Also advances to currBucketNum-1.
-    bool detectAnomaly(const int64_t& currBucketNum, const MetricDimensionKey& key,
-                       const int64_t& currentBucketValue);
+    bool detectAnomaly(int64_t currBucketNum, const MetricDimensionKey& key,
+                       int64_t currentBucketValue);
 
     // Informs incidentd about the detected alert.
-    void declareAnomaly(const int64_t& timestampNs, int64_t metricId, const MetricDimensionKey& key,
+    void declareAnomaly(int64_t timestampNs, int64_t metricId, const MetricDimensionKey& key,
                         int64_t metricValue);
 
     // Detects if, based on past buckets plus the new currentBucketValue (which generally
     // represents the partially-filled current bucket), an anomaly has happened, and if so,
     // declares an anomaly and informs relevant subscribers.
     // Also advances to currBucketNum-1.
-    void detectAndDeclareAnomaly(const int64_t& timestampNs, const int64_t& currBucketNum,
-                                 int64_t metricId, const MetricDimensionKey& key,
-                                 const int64_t& currentBucketValue);
+    void detectAndDeclareAnomaly(int64_t timestampNs, int64_t currBucketNum, int64_t metricId,
+                                 const MetricDimensionKey& key, int64_t currentBucketValue);
 
     // Init the AlarmMonitor which is shared across anomaly trackers.
     virtual void setAlarmMonitor(const sp<AlarmMonitor>& alarmMonitor) {
@@ -91,7 +89,7 @@
     int64_t getSumOverPastBuckets(const MetricDimensionKey& key) const;
 
     // Returns the value for a past bucket, or 0 if that bucket doesn't exist.
-    int64_t getPastBucketValue(const MetricDimensionKey& key, const int64_t& bucketNum) const;
+    int64_t getPastBucketValue(const MetricDimensionKey& key, int64_t bucketNum) const;
 
     // Returns the anomaly threshold set in the configuration.
     inline int64_t getAnomalyThreshold() const {
@@ -115,14 +113,14 @@
 
     // Sets an alarm for the given timestamp.
     // Replaces previous alarm if one already exists.
-    virtual void startAlarm(const MetricDimensionKey& dimensionKey, const int64_t& eventTime) {
+    virtual void startAlarm(const MetricDimensionKey& dimensionKey, 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) {
+    virtual void stopAlarm(const MetricDimensionKey& dimensionKey, int64_t timestampNs) {
         return;  // The base AnomalyTracker class doesn't have alarms.
     }
 
@@ -133,7 +131,8 @@
 
     // 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,
+    virtual void informAlarmsFired(
+            int64_t timestampNs,
             unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) {
         return; // The base AnomalyTracker class doesn't have alarms.
     }
@@ -190,7 +189,7 @@
     // Advances mMostRecentBucketNum to bucketNum, deleting any data that is now too old.
     // Specifically, since it is now too old, removes the data for
     //   [mMostRecentBucketNum - mNumOfPastBuckets + 1, bucketNum - mNumOfPastBuckets].
-    void advanceMostRecentBucketTo(const int64_t& bucketNum);
+    void advanceMostRecentBucketTo(int64_t bucketNum);
 
     // Add the information in the given bucket to mSumOverPastBuckets.
     void addBucketToSum(const shared_ptr<DimToValMap>& bucket);
@@ -200,10 +199,10 @@
     void subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket);
 
     // From mSumOverPastBuckets[key], subtracts bucketValue, removing it if it is now 0.
-    void subtractValueFromSum(const MetricDimensionKey& key, const int64_t& bucketValue);
+    void subtractValueFromSum(const MetricDimensionKey& key, int64_t bucketValue);
 
     // Returns true if in the refractory period, else false.
-    bool isInRefractoryPeriod(const int64_t& timestampNs, const MetricDimensionKey& key) const;
+    bool isInRefractoryPeriod(int64_t timestampNs, const MetricDimensionKey& key) const;
 
     // Calculates the corresponding bucket index within the circular array.
     // Requires bucketNum >= 0.
diff --git a/statsd/src/anomaly/DurationAnomalyTracker.cpp b/statsd/src/anomaly/DurationAnomalyTracker.cpp
index 66c71ad..f3bfc05 100644
--- a/statsd/src/anomaly/DurationAnomalyTracker.cpp
+++ b/statsd/src/anomaly/DurationAnomalyTracker.cpp
@@ -36,7 +36,7 @@
 }
 
 void DurationAnomalyTracker::startAlarm(const MetricDimensionKey& dimensionKey,
-                                        const int64_t& timestampNs) {
+                                        const int64_t timestampNs) {
     // Alarms are stored in secs. Must round up, since if it fires early, it is ignored completely.
     uint32_t timestampSec = static_cast<uint32_t>((timestampNs -1) / NS_PER_SEC) + 1; // round up
     if (isInRefractoryPeriod(timestampNs, dimensionKey)) {
@@ -57,7 +57,7 @@
 }
 
 void DurationAnomalyTracker::stopAlarm(const MetricDimensionKey& dimensionKey,
-                                       const int64_t& timestampNs) {
+                                       const int64_t timestampNs) {
     const auto itr = mAlarms.find(dimensionKey);
     if (itr == mAlarms.end()) {
         return;
@@ -84,9 +84,9 @@
     mAlarms.clear();
 }
 
-void DurationAnomalyTracker::informAlarmsFired(const int64_t& timestampNs,
+void DurationAnomalyTracker::informAlarmsFired(
+        const int64_t timestampNs,
         unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) {
-
     if (firedAlarms.empty() || mAlarms.empty()) return;
     // Find the intersection of firedAlarms and mAlarms.
     // The for loop is inefficient, since it loops over all keys, but that's okay since it is very
diff --git a/statsd/src/anomaly/DurationAnomalyTracker.h b/statsd/src/anomaly/DurationAnomalyTracker.h
index a523782..ce06df0 100644
--- a/statsd/src/anomaly/DurationAnomalyTracker.h
+++ b/statsd/src/anomaly/DurationAnomalyTracker.h
@@ -34,12 +34,12 @@
 
     // Sets an alarm for the given timestamp.
     // Replaces previous alarm if one already exists.
-    void startAlarm(const MetricDimensionKey& dimensionKey, const int64_t& eventTime) override;
+    void startAlarm(const MetricDimensionKey& dimensionKey, 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) override;
+    void stopAlarm(const MetricDimensionKey& dimensionKey, int64_t timestampNs) override;
 
     // Stop all the alarms owned by this tracker. Does not declare any anomalies.
     void cancelAllAlarms() override;
@@ -48,7 +48,8 @@
     // and removes it from firedAlarms. The AlarmMonitor is not informed.
     // Note that this will generally be called from a different thread from the other functions;
     // the caller is responsible for thread safety.
-    void informAlarmsFired(const int64_t& timestampNs,
+    void informAlarmsFired(
+            int64_t timestampNs,
             unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& firedAlarms) override;
 
 protected:
diff --git a/statsd/src/anomaly/subscriber_util.h b/statsd/src/anomaly/subscriber_util.h
index 4d4c83b..d55581d 100644
--- a/statsd/src/anomaly/subscriber_util.h
+++ b/statsd/src/anomaly/subscriber_util.h
@@ -24,7 +24,7 @@
 namespace os {
 namespace statsd {
 
-void triggerSubscribers(const int64_t ruleId, const int64_t metricId,
+void triggerSubscribers(const int64_t ruleId, int64_t metricId,
                         const MetricDimensionKey& dimensionKey, int64_t metricValue,
                         const ConfigKey& configKey, const std::vector<Subscription>& subscriptions);
 
diff --git a/statsd/src/condition/CombinationConditionTracker.cpp b/statsd/src/condition/CombinationConditionTracker.cpp
index b3f30c7..af4a18a 100644
--- a/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/statsd/src/condition/CombinationConditionTracker.cpp
@@ -25,7 +25,7 @@
 using std::unordered_map;
 using std::vector;
 
-CombinationConditionTracker::CombinationConditionTracker(const int64_t& id, const int 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);
diff --git a/statsd/src/condition/CombinationConditionTracker.h b/statsd/src/condition/CombinationConditionTracker.h
index 77eeb60..3ed4a83 100644
--- a/statsd/src/condition/CombinationConditionTracker.h
+++ b/statsd/src/condition/CombinationConditionTracker.h
@@ -26,7 +26,7 @@
 
 class CombinationConditionTracker : public ConditionTracker {
 public:
-    CombinationConditionTracker(const int64_t& id, const int index, const uint64_t protoHash);
+    CombinationConditionTracker(int64_t id, int index, const uint64_t protoHash);
 
     ~CombinationConditionTracker();
 
@@ -37,7 +37,7 @@
             std::vector<uint8_t>& stack, std::vector<ConditionState>& conditionCache) override;
 
     optional<InvalidConfigReason> onConfigUpdated(
-            const std::vector<Predicate>& allConditionProtos, const int index,
+            const std::vector<Predicate>& allConditionProtos, 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;
diff --git a/statsd/src/condition/ConditionTracker.h b/statsd/src/condition/ConditionTracker.h
index adf8903..b09d350 100644
--- a/statsd/src/condition/ConditionTracker.h
+++ b/statsd/src/condition/ConditionTracker.h
@@ -31,7 +31,7 @@
 
 class ConditionTracker : public virtual RefBase {
 public:
-    ConditionTracker(const int64_t& id, const int index, const uint64_t protoHash)
+    ConditionTracker(int64_t id, int index, const uint64_t protoHash)
         : mConditionId(id),
           mIndex(index),
           mInitialized(false),
@@ -73,7 +73,7 @@
     // conditionTrackerMap: map of condition tracker id to index after the config update.
     // returns whether or not the update is successful.
     virtual optional<InvalidConfigReason> onConfigUpdated(
-            const std::vector<Predicate>& allConditionProtos, const int index,
+            const std::vector<Predicate>& allConditionProtos, int index,
             const std::vector<sp<ConditionTracker>>& allConditionTrackers,
             const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
             const std::unordered_map<int64_t, int>& conditionTrackerMap) {
diff --git a/statsd/src/condition/SimpleConditionTracker.cpp b/statsd/src/condition/SimpleConditionTracker.cpp
index e9373e5..5b87f13 100644
--- a/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/statsd/src/condition/SimpleConditionTracker.cpp
@@ -27,7 +27,7 @@
 using std::unordered_map;
 
 SimpleConditionTracker::SimpleConditionTracker(
-        const ConfigKey& key, const int64_t& id, const uint64_t protoHash, 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>& atomMatchingTrackerMap)
     : ConditionTracker(id, index, protoHash),
diff --git a/statsd/src/condition/SimpleConditionTracker.h b/statsd/src/condition/SimpleConditionTracker.h
index 5e3e4e4..8af89cb 100644
--- a/statsd/src/condition/SimpleConditionTracker.h
+++ b/statsd/src/condition/SimpleConditionTracker.h
@@ -29,7 +29,7 @@
 
 class SimpleConditionTracker : public ConditionTracker {
 public:
-    SimpleConditionTracker(const ConfigKey& key, const int64_t& id, const uint64_t protoHash,
+    SimpleConditionTracker(const ConfigKey& key, int64_t id, const uint64_t protoHash,
                            const int index, const SimplePredicate& simplePredicate,
                            const std::unordered_map<int64_t, int>& atomMatchingTrackerMap);
 
@@ -42,7 +42,7 @@
             std::vector<uint8_t>& stack, std::vector<ConditionState>& conditionCache) override;
 
     optional<InvalidConfigReason> onConfigUpdated(
-            const std::vector<Predicate>& allConditionProtos, const int index,
+            const std::vector<Predicate>& allConditionProtos, 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;
diff --git a/statsd/src/config/ConfigKey.cpp b/statsd/src/config/ConfigKey.cpp
index 4a2bd27..3b0b835 100644
--- a/statsd/src/config/ConfigKey.cpp
+++ b/statsd/src/config/ConfigKey.cpp
@@ -26,7 +26,7 @@
 ConfigKey::ConfigKey(const ConfigKey& that) : mId(that.mId), mUid(that.mUid) {
 }
 
-ConfigKey::ConfigKey(int uid, const int64_t& id) : mId(id), mUid(uid) {
+ConfigKey::ConfigKey(int uid, const int64_t id) : mId(id), mUid(uid) {
 }
 
 ConfigKey::~ConfigKey() {
diff --git a/statsd/src/config/ConfigKey.h b/statsd/src/config/ConfigKey.h
index b0d015f..e8df355 100644
--- a/statsd/src/config/ConfigKey.h
+++ b/statsd/src/config/ConfigKey.h
@@ -34,13 +34,13 @@
 public:
     ConfigKey();
     ConfigKey(const ConfigKey& that);
-    ConfigKey(int uid, const int64_t& id);
+    ConfigKey(int uid, int64_t id);
     ~ConfigKey();
 
     inline int GetUid() const {
         return mUid;
     }
-    inline const int64_t& GetId() const {
+    inline int64_t GetId() const {
         return mId;
     }
 
diff --git a/statsd/src/config/ConfigKeyWithPackage.h b/statsd/src/config/ConfigKeyWithPackage.h
index 85e95d5..9cf507b 100644
--- a/statsd/src/config/ConfigKeyWithPackage.h
+++ b/statsd/src/config/ConfigKeyWithPackage.h
@@ -31,7 +31,7 @@
  */
 class ConfigKeyWithPackage {
 public:
-    ConfigKeyWithPackage(const string& package, const int64_t id) : mPackage(package), mId(id) {
+    ConfigKeyWithPackage(const string& package, int64_t id) : mPackage(package), mId(id) {
     }
 
     inline string GetPackage() const {
diff --git a/statsd/src/config/ConfigListener.cpp b/statsd/src/config/ConfigListener.cpp
index 21a3f16..f0406fc 100644
--- a/statsd/src/config/ConfigListener.cpp
+++ b/statsd/src/config/ConfigListener.cpp
@@ -20,12 +20,6 @@
 namespace os {
 namespace statsd {
 
-ConfigListener::ConfigListener() {
-}
-
-ConfigListener::~ConfigListener() {
-}
-
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/statsd/src/config/ConfigListener.h b/statsd/src/config/ConfigListener.h
index b29e0be..fbebf82 100644
--- a/statsd/src/config/ConfigListener.h
+++ b/statsd/src/config/ConfigListener.h
@@ -30,13 +30,13 @@
  */
 class ConfigListener : public virtual RefBase {
 public:
-    ConfigListener();
-    virtual ~ConfigListener();
+    ConfigListener() = default;
+    virtual ~ConfigListener() = default;
 
     /**
      * A configuration was added or updated.
      */
-    virtual void OnConfigUpdated(const int64_t timestampNs, const ConfigKey& key,
+    virtual void OnConfigUpdated(int64_t timestampNs, const ConfigKey& key,
                                  const StatsdConfig& config, bool modularUpdate = true) = 0;
 
     /**
diff --git a/statsd/src/config/ConfigManager.cpp b/statsd/src/config/ConfigManager.cpp
index 16c4e75..d1f93c7 100644
--- a/statsd/src/config/ConfigManager.cpp
+++ b/statsd/src/config/ConfigManager.cpp
@@ -33,7 +33,6 @@
 namespace os {
 namespace statsd {
 
-using std::pair;
 using std::string;
 using std::vector;
 
@@ -42,7 +41,6 @@
 #define STATS_SERVICE_DIR "/data/misc/stats-service"
 
 using android::base::StringPrintf;
-using std::unique_ptr;
 
 ConfigManager::ConfigManager() {
 }
@@ -53,8 +51,8 @@
 void ConfigManager::Startup() {
     map<ConfigKey, StatsdConfig> configsFromDisk;
     StorageManager::readConfigFromDisk(configsFromDisk);
-    for (const auto& pair : configsFromDisk) {
-        UpdateConfig(pair.first, pair.second);
+    for (const auto& config : configsFromDisk) {
+        UpdateConfig(config.first, config.second);
     }
 }
 
diff --git a/statsd/src/config/ConfigManager.h b/statsd/src/config/ConfigManager.h
index dd7d5bb..e881949 100644
--- a/statsd/src/config/ConfigManager.h
+++ b/statsd/src/config/ConfigManager.h
@@ -116,14 +116,14 @@
     /**
      * Sets the pending intent that is notified whenever the list of restricted metrics changes
      */
-    void SetRestrictedMetricsChangedReceiver(const string& configPackage, const int64_t configId,
+    void SetRestrictedMetricsChangedReceiver(const string& configPackage, int64_t configId,
                                              const int32_t callingUid,
                                              const shared_ptr<IPendingIntentRef>& pir);
 
     /**
      * Erase any restricted metrics changed pending intents associated with this config key & uid.
      */
-    void RemoveRestrictedMetricsChangedReceiver(const string& configPackage, const int64_t configId,
+    void RemoveRestrictedMetricsChangedReceiver(const string& configPackage, int64_t configId,
                                                 const int32_t callingUid);
 
     /**
diff --git a/statsd/src/external/PullResultReceiver.h b/statsd/src/external/PullResultReceiver.h
index ceaae80..f2d0a4e 100644
--- a/statsd/src/external/PullResultReceiver.h
+++ b/statsd/src/external/PullResultReceiver.h
@@ -29,8 +29,8 @@
 
 class PullResultReceiver : public BnPullAtomResultReceiver {
 public:
-    PullResultReceiver(function<void(int32_t, bool, const vector<StatsEventParcel>&)>
-                               pullFinishCallback);
+    PullResultReceiver(
+            function<void(int32_t, bool, const vector<StatsEventParcel>&)> pullFinishCallback);
     ~PullResultReceiver();
 
     /**
diff --git a/statsd/src/external/StatsCallbackPuller.cpp b/statsd/src/external/StatsCallbackPuller.cpp
index 7d5d04b..a1c8df2 100644
--- a/statsd/src/external/StatsCallbackPuller.cpp
+++ b/statsd/src/external/StatsCallbackPuller.cpp
@@ -37,7 +37,7 @@
 
 StatsCallbackPuller::StatsCallbackPuller(int tagId, const shared_ptr<IPullAtomCallback>& callback,
                                          const int64_t coolDownNs, int64_t timeoutNs,
-                                         const vector<int> additiveFields)
+                                         const vector<int>& additiveFields)
     : StatsPuller(tagId, coolDownNs, timeoutNs, additiveFields), mCallback(callback) {
     VLOG("StatsCallbackPuller created for tag %d", tagId);
 }
diff --git a/statsd/src/external/StatsCallbackPuller.h b/statsd/src/external/StatsCallbackPuller.h
index 43d35fc..65acb00 100644
--- a/statsd/src/external/StatsCallbackPuller.h
+++ b/statsd/src/external/StatsCallbackPuller.h
@@ -29,8 +29,8 @@
 class StatsCallbackPuller : public StatsPuller {
 public:
     explicit StatsCallbackPuller(int tagId, const shared_ptr<IPullAtomCallback>& callback,
-                                 const int64_t coolDownNs, const int64_t timeoutNs,
-                                 const std::vector<int> additiveFields);
+                                 const int64_t coolDownNs, int64_t timeoutNs,
+                                 const std::vector<int>& additiveFields);
 
 private:
     PullErrorCode PullInternal(vector<std::shared_ptr<LogEvent>>* data) override;
diff --git a/statsd/src/external/StatsPuller.cpp b/statsd/src/external/StatsPuller.cpp
index 395e660..0051b0c 100644
--- a/statsd/src/external/StatsPuller.cpp
+++ b/statsd/src/external/StatsPuller.cpp
@@ -33,7 +33,7 @@
 void StatsPuller::SetUidMap(const sp<UidMap>& uidMap) { mUidMap = uidMap; }
 
 StatsPuller::StatsPuller(const int tagId, const int64_t coolDownNs, const int64_t pullTimeoutNs,
-                         const std::vector<int> additiveFields)
+                         const std::vector<int>& additiveFields)
     : mTagId(tagId),
       mPullTimeoutNs(pullTimeoutNs),
       mCoolDownNs(coolDownNs),
diff --git a/statsd/src/external/StatsPuller.h b/statsd/src/external/StatsPuller.h
index d8c7eb3..4a38382 100644
--- a/statsd/src/external/StatsPuller.h
+++ b/statsd/src/external/StatsPuller.h
@@ -41,10 +41,9 @@
 
 class StatsPuller : public virtual RefBase {
 public:
-    explicit StatsPuller(const int tagId,
-                         const int64_t coolDownNs = NS_PER_SEC,
+    explicit StatsPuller(const int tagId, int64_t coolDownNs = NS_PER_SEC,
                          const int64_t pullTimeoutNs = StatsdStats::kPullMaxDelayNs,
-                         const std::vector<int> additiveFields = std::vector<int>());
+                         const std::vector<int>& additiveFields = std::vector<int>());
 
     virtual ~StatsPuller() {}
 
@@ -69,7 +68,7 @@
     static void SetUidMap(const sp<UidMap>& uidMap);
 
     virtual void SetStatsCompanionService(
-            shared_ptr<IStatsCompanionService> statsCompanionService) {};
+            const shared_ptr<IStatsCompanionService>& statsCompanionService){};
 
 protected:
     const int mTagId;
diff --git a/statsd/src/external/StatsPullerManager.cpp b/statsd/src/external/StatsPullerManager.cpp
index 8fabcad..4fb63a6 100644
--- a/statsd/src/external/StatsPullerManager.cpp
+++ b/statsd/src/external/StatsPullerManager.cpp
@@ -135,7 +135,7 @@
 }
 
 void StatsPullerManager::SetStatsCompanionService(
-        shared_ptr<IStatsCompanionService> statsCompanionService) {
+        const shared_ptr<IStatsCompanionService>& statsCompanionService) {
     std::lock_guard<std::mutex> _l(mLock);
     shared_ptr<IStatsCompanionService> tmpForLock = mStatsCompanionService;
     mStatsCompanionService = statsCompanionService;
@@ -148,8 +148,8 @@
 }
 
 void StatsPullerManager::RegisterReceiver(int tagId, const ConfigKey& configKey,
-                                          wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
-                                          int64_t intervalNs) {
+                                          const wp<PullDataReceiver>& receiver,
+                                          int64_t nextPullTimeNs, int64_t intervalNs) {
     std::lock_guard<std::mutex> _l(mLock);
     auto& receivers = mReceivers[{.atomTag = tagId, .configKey = configKey}];
     for (auto it = receivers.begin(); it != receivers.end(); it++) {
@@ -184,7 +184,7 @@
 }
 
 void StatsPullerManager::UnRegisterReceiver(int tagId, const ConfigKey& configKey,
-                                            wp<PullDataReceiver> receiver) {
+                                            const wp<PullDataReceiver>& receiver) {
     std::lock_guard<std::mutex> _l(mLock);
     auto receiversIt = mReceivers.find({.atomTag = tagId, .configKey = configKey});
     if (receiversIt == mReceivers.end()) {
@@ -202,13 +202,13 @@
 }
 
 void StatsPullerManager::RegisterPullUidProvider(const ConfigKey& configKey,
-                                                 wp<PullUidProvider> provider) {
+                                                 const wp<PullUidProvider>& provider) {
     std::lock_guard<std::mutex> _l(mLock);
     mPullUidProviders[configKey] = provider;
 }
 
 void StatsPullerManager::UnregisterPullUidProvider(const ConfigKey& configKey,
-                                                   wp<PullUidProvider> provider) {
+                                                   const wp<PullUidProvider>& provider) {
     std::lock_guard<std::mutex> _l(mLock);
     const auto& it = mPullUidProviders.find(configKey);
     if (it != mPullUidProviders.end() && it->second == provider) {
diff --git a/statsd/src/external/StatsPullerManager.h b/statsd/src/external/StatsPullerManager.h
index 80a1331..9f5a41c 100644
--- a/statsd/src/external/StatsPullerManager.h
+++ b/statsd/src/external/StatsPullerManager.h
@@ -70,20 +70,21 @@
     // Registers a receiver for tagId. It will be pulled on the nextPullTimeNs
     // and then every intervalNs thereafter.
     virtual void RegisterReceiver(int tagId, const ConfigKey& configKey,
-                                  wp<PullDataReceiver> receiver, int64_t nextPullTimeNs,
+                                  const wp<PullDataReceiver>& receiver, int64_t nextPullTimeNs,
                                   int64_t intervalNs);
 
     // Stop listening on a tagId.
     virtual void UnRegisterReceiver(int tagId, const ConfigKey& configKey,
-                                    wp<PullDataReceiver> receiver);
+                                    const wp<PullDataReceiver>& receiver);
 
     // Registers a pull uid provider for the config key. When pulling atoms, it will be used to
     // determine which uids to pull from.
-    virtual void RegisterPullUidProvider(const ConfigKey& configKey, wp<PullUidProvider> provider);
+    virtual void RegisterPullUidProvider(const ConfigKey& configKey,
+                                         const wp<PullUidProvider>& provider);
 
     // Unregister a pull uid provider.
     virtual void UnregisterPullUidProvider(const ConfigKey& configKey,
-                                           wp<PullUidProvider> provider);
+                                           const wp<PullUidProvider>& provider);
 
     // Verify if we know how to pull for this matcher
     bool PullerForMatcherExists(int tagId) const;
@@ -101,11 +102,11 @@
     //      registered for any of the uids for this atom.
     // 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,
+    virtual bool Pull(int tagId, const ConfigKey& configKey, int64_t eventTimeNs,
                       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,
+    virtual bool Pull(int tagId, const vector<int32_t>& uids, int64_t eventTimeNs,
                       vector<std::shared_ptr<LogEvent>>* data);
 
     // Clear pull data cache immediately.
@@ -114,9 +115,9 @@
     // Clear pull data cache if it is beyond respective cool down time.
     int ClearPullerCacheIfNecessary(int64_t timestampNs);
 
-    void SetStatsCompanionService(shared_ptr<IStatsCompanionService> statsCompanionService);
+    void SetStatsCompanionService(const shared_ptr<IStatsCompanionService>& statsCompanionService);
 
-    void RegisterPullAtomCallback(const int uid, const int32_t atomTag, const int64_t coolDownNs,
+    void RegisterPullAtomCallback(const int uid, const int32_t atomTag, int64_t coolDownNs,
                                   const int64_t timeoutNs, const vector<int32_t>& additiveFields,
                                   const shared_ptr<IPullAtomCallback>& callback);
 
@@ -151,10 +152,10 @@
     // mapping from Config Key to the PullUidProvider for that config
     std::map<ConfigKey, wp<PullUidProvider>> mPullUidProviders;
 
-    bool PullLocked(int tagId, const ConfigKey& configKey, const int64_t eventTimeNs,
+    bool PullLocked(int tagId, const ConfigKey& configKey, int64_t eventTimeNs,
                     vector<std::shared_ptr<LogEvent>>* data);
 
-    bool PullLocked(int tagId, const vector<int32_t>& uids, const int64_t eventTimeNs,
+    bool PullLocked(int tagId, const vector<int32_t>& uids, int64_t eventTimeNs,
                     vector<std::shared_ptr<LogEvent>>* data);
 
     // locks for data receiver and StatsCompanionService changes
diff --git a/statsd/src/guardrail/StatsdStats.cpp b/statsd/src/guardrail/StatsdStats.cpp
index 939d39f..191ea3f 100644
--- a/statsd/src/guardrail/StatsdStats.cpp
+++ b/statsd/src/guardrail/StatsdStats.cpp
@@ -62,6 +62,7 @@
 const int FIELD_ID_STATSD_STATS_ID = 22;
 const int FIELD_ID_SUBSCRIPTION_STATS = 23;
 const int FIELD_ID_SOCKET_LOSS_STATS = 24;
+const int FIELD_ID_QUEUE_STATS = 25;
 
 const int FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_CALLING_UID = 1;
 const int FIELD_ID_RESTRICTED_METRIC_QUERY_STATS_CONFIG_ID = 2;
@@ -93,6 +94,9 @@
 const int FIELD_ID_OVERFLOW_MAX_HISTORY = 2;
 const int FIELD_ID_OVERFLOW_MIN_HISTORY = 3;
 
+const int FIELD_ID_QUEUE_MAX_SIZE_OBSERVED = 1;
+const int FIELD_ID_QUEUE_MAX_SIZE_OBSERVED_ELAPSED_NANOS = 2;
+
 const int FIELD_ID_CONFIG_STATS_UID = 1;
 const int FIELD_ID_CONFIG_STATS_ID = 2;
 const int FIELD_ID_CONFIG_STATS_CREATION = 3;
@@ -361,6 +365,15 @@
     noteAtomDroppedLocked(atomId);
 }
 
+void StatsdStats::noteEventQueueSize(int32_t size, int64_t eventTimestampNs) {
+    lock_guard<std::mutex> lock(mLock);
+
+    if (mEventQueueMaxSizeObserved < size) {
+        mEventQueueMaxSizeObserved = size;
+        mEventQueueMaxSizeObservedElapsedNanos = eventTimestampNs;
+    }
+}
+
 void StatsdStats::noteAtomDroppedLocked(int32_t atomId) {
     constexpr int kMaxPushedAtomDroppedStatsSize = kMaxPushedAtomId + kMaxNonPlatformPushedAtoms;
     if (mPushedAtomDropsStats.size() < kMaxPushedAtomDroppedStatsSize ||
@@ -532,7 +545,7 @@
     mUidMapStats.bytes_used = bytes;
 }
 
-void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const int64_t& id, int size) {
+void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const int64_t id, int size) {
     lock_guard<std::mutex> lock(mLock);
     // if name doesn't exist before, it will create the key with count 0.
     auto statsIt = mConfigStats.find(key);
@@ -546,7 +559,7 @@
     }
 }
 
-void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size) {
+void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const int64_t id, int size) {
     lock_guard<std::mutex> lock(mLock);
     // if name doesn't exist before, it will create the key with count 0.
     auto statsIt = mConfigStats.find(key);
@@ -559,8 +572,8 @@
     }
 }
 
-void StatsdStats::noteMetricDimensionInConditionSize(
-        const ConfigKey& key, const int64_t& id, int size) {
+void StatsdStats::noteMetricDimensionInConditionSize(const ConfigKey& key, const int64_t id,
+                                                     int size) {
     lock_guard<std::mutex> lock(mLock);
     // if name doesn't exist before, it will create the key with count 0.
     auto statsIt = mConfigStats.find(key);
@@ -573,7 +586,7 @@
     }
 }
 
-void StatsdStats::noteMatcherMatched(const ConfigKey& key, const int64_t& id) {
+void StatsdStats::noteMatcherMatched(const ConfigKey& key, const int64_t id) {
     lock_guard<std::mutex> lock(mLock);
 
     auto statsIt = mConfigStats.find(key);
@@ -583,7 +596,7 @@
     statsIt->second->matcher_stats[id]++;
 }
 
-void StatsdStats::noteAnomalyDeclared(const ConfigKey& key, const int64_t& id) {
+void StatsdStats::noteAnomalyDeclared(const ConfigKey& key, const int64_t id) {
     lock_guard<std::mutex> lock(mLock);
     auto statsIt = mConfigStats.find(key);
     if (statsIt == mConfigStats.end()) {
@@ -1029,6 +1042,8 @@
     mOverflowCount = 0;
     mMinQueueHistoryNs = kInt64Max;
     mMaxQueueHistoryNs = 0;
+    mEventQueueMaxSizeObserved = 0;
+    mEventQueueMaxSizeObservedElapsedNanos = 0;
     for (auto& config : mConfigStats) {
         config.second->broadcast_sent_time_sec.clear();
         config.second->activation_time_sec.clear();
@@ -1124,7 +1139,7 @@
     }
 }
 
-bool StatsdStats::hasRestrictedConfigErrors(std::shared_ptr<ConfigStats> configStats) const {
+bool StatsdStats::hasRestrictedConfigErrors(const std::shared_ptr<ConfigStats>& configStats) const {
     return configStats->device_info_table_creation_failed || configStats->db_corrupted_count ||
            configStats->db_deletion_size_exceeded_limit || configStats->db_deletion_stat_failed ||
            configStats->db_deletion_config_invalid || configStats->db_deletion_too_old ||
@@ -1428,6 +1443,8 @@
     dprintf(out, "********EventQueueOverflow stats***********\n");
     dprintf(out, "Event queue overflow: %d; MaxHistoryNs: %lld; MinHistoryNs: %lld\n",
             mOverflowCount, (long long)mMaxQueueHistoryNs, (long long)mMinQueueHistoryNs);
+    dprintf(out, "Event queue max size: %d; Observed at : %lld\n", mEventQueueMaxSizeObserved,
+            (long long)mEventQueueMaxSizeObservedElapsedNanos);
 
     if (mActivationBroadcastGuardrailStats.size() > 0) {
         dprintf(out, "********mActivationBroadcastGuardrail stats***********\n");
@@ -1783,6 +1800,13 @@
         proto.end(token);
     }
 
+    uint64_t queueStatsToken = proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_QUEUE_STATS);
+    proto.write(FIELD_TYPE_INT32 | FIELD_ID_QUEUE_MAX_SIZE_OBSERVED,
+                (int32_t)mEventQueueMaxSizeObserved);
+    proto.write(FIELD_TYPE_INT64 | FIELD_ID_QUEUE_MAX_SIZE_OBSERVED_ELAPSED_NANOS,
+                (long long)mEventQueueMaxSizeObservedElapsedNanos);
+    proto.end(queueStatsToken);
+
     for (const auto& restart : mSystemServerRestartSec) {
         proto.write(FIELD_TYPE_INT32 | FIELD_ID_SYSTEM_SERVER_RESTART | FIELD_COUNT_REPEATED,
                     restart);
diff --git a/statsd/src/guardrail/StatsdStats.h b/statsd/src/guardrail/StatsdStats.h
index c7ca8d0..e3ab443 100644
--- a/statsd/src/guardrail/StatsdStats.h
+++ b/statsd/src/guardrail/StatsdStats.h
@@ -395,7 +395,7 @@
      * [id]: The id of the condition.
      * [size]: The output tuple size.
      */
-    void noteConditionDimensionSize(const ConfigKey& key, const int64_t& id, int size);
+    void noteConditionDimensionSize(const ConfigKey& key, int64_t id, int size);
 
     /**
      * Report the size of output tuple of a metric.
@@ -407,7 +407,7 @@
      * [id]: The id of the metric.
      * [size]: The output tuple size.
      */
-    void noteMetricDimensionSize(const ConfigKey& key, const int64_t& id, int size);
+    void noteMetricDimensionSize(const ConfigKey& key, int64_t id, int size);
 
     /**
      * Report the max size of output tuple of dimension in condition across dimensions in what.
@@ -419,7 +419,7 @@
      * [id]: The id of the metric.
      * [size]: The output tuple size.
      */
-    void noteMetricDimensionInConditionSize(const ConfigKey& key, const int64_t& id, int size);
+    void noteMetricDimensionInConditionSize(const ConfigKey& key, int64_t id, int size);
 
     /**
      * Report a matcher has been matched.
@@ -427,7 +427,7 @@
      * [key]: The config key that this matcher belongs to.
      * [id]: The id of the matcher.
      */
-    void noteMatcherMatched(const ConfigKey& key, const int64_t& id);
+    void noteMatcherMatched(const ConfigKey& key, int64_t id);
 
     /**
      * Report that an anomaly detection alert has been declared.
@@ -435,7 +435,7 @@
      * [key]: The config key that this alert belongs to.
      * [id]: The id of the alert.
      */
-    void noteAnomalyDeclared(const ConfigKey& key, const int64_t& id);
+    void noteAnomalyDeclared(const ConfigKey& key, int64_t id);
 
     /**
      * Report an atom event has been logged.
@@ -614,6 +614,9 @@
      * in the queue */
     void noteEventQueueOverflow(int64_t oldestEventTimestampNs, int32_t atomId, bool isSkipped);
 
+    /* Notes queue max size seen so far and associated timestamp */
+    void noteEventQueueSize(int32_t size, int64_t eventTimestampNs);
+
     /**
      * Reports that the activation broadcast guardrail was hit for this uid. Namely, the broadcast
      * should have been sent, but instead was skipped due to hitting the guardrail.
@@ -633,7 +636,7 @@
     /** Report query of restricted metric succeed **/
     void noteQueryRestrictedMetricSucceed(const int64_t configId, const string& configPackage,
                                           const std::optional<int32_t> configUid,
-                                          const int32_t callingUid, const int64_t queryLatencyNs);
+                                          const int32_t callingUid, int64_t queryLatencyNs);
 
     /** Report query of restricted metric failed **/
     void noteQueryRestrictedMetricFailed(const int64_t configId, const string& configPackage,
@@ -650,24 +653,24 @@
     void noteRestrictedMetricInsertError(const ConfigKey& configKey, int64_t metricId);
 
     // Reports that a restricted metric fails to create table in database.
-    void noteRestrictedMetricTableCreationError(const ConfigKey& configKey, const int64_t metricId);
+    void noteRestrictedMetricTableCreationError(const ConfigKey& configKey, int64_t metricId);
 
     // Reports that a restricted metric fails to delete table in database.
-    void noteRestrictedMetricTableDeletionError(const ConfigKey& configKey, const int64_t metricId);
+    void noteRestrictedMetricTableDeletionError(const ConfigKey& configKey, int64_t metricId);
 
     // Reports the time it takes for a restricted metric to flush the data to the database.
-    void noteRestrictedMetricFlushLatency(const ConfigKey& configKey, const int64_t metricId,
+    void noteRestrictedMetricFlushLatency(const ConfigKey& configKey, int64_t metricId,
                                           const int64_t flushLatencyNs);
 
     // Reports that a restricted metric had a category change.
-    void noteRestrictedMetricCategoryChanged(const ConfigKey& configKey, const int64_t metricId);
+    void noteRestrictedMetricCategoryChanged(const ConfigKey& configKey, int64_t metricId);
 
     // Reports the time is takes to flush a restricted config to the database.
     void noteRestrictedConfigFlushLatency(const ConfigKey& configKey,
                                           const int64_t totalFlushLatencyNs);
 
     // Reports the size of the internal sqlite db.
-    void noteRestrictedConfigDbSize(const ConfigKey& configKey, const int64_t elapsedTimeNs,
+    void noteRestrictedConfigDbSize(const ConfigKey& configKey, int64_t elapsedTimeNs,
                                     const int64_t dbSize);
 
     /**
@@ -824,7 +827,7 @@
 
     // Stores the stats for the configs that are no longer in use.
     // The size of the vector is capped by kMaxIceBoxSize.
-    std::list<const std::shared_ptr<ConfigStats>> mIceBox;
+    std::list<std::shared_ptr<ConfigStats>> mIceBox;
 
     // Stores the number of times a pushed atom is logged and skipped (if skipped).
     // The size of the vector is the largest pushed atom id in atoms.proto + 1. Atoms
@@ -922,6 +925,12 @@
     // Total number of events that are lost due to queue overflow.
     int32_t mOverflowCount = 0;
 
+    // Max number of events stored into the queue seen so far.
+    int32_t mEventQueueMaxSizeObserved = 0;
+
+    // Event timestamp for associated max size hit.
+    int64_t mEventQueueMaxSizeObservedElapsedNanos = 0;
+
     // Timestamps when we detect log loss, and the number of logs lost.
     std::list<LogLossStats> mLogLossStats;
 
@@ -1001,7 +1010,7 @@
 
     int getPushedAtomDropsLocked(int atomId) const;
 
-    bool hasRestrictedConfigErrors(std::shared_ptr<ConfigStats> configStats) const;
+    bool hasRestrictedConfigErrors(const std::shared_ptr<ConfigStats>& configStats) const;
 
     /**
      * Get a reference to AtomMetricStats for a metric. If none exists, create it. The reference
@@ -1009,40 +1018,42 @@
      */
     StatsdStats::AtomMetricStats& getAtomMetricStats(int64_t metricId);
 
-    FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd);
+    FRIEND_TEST(LogEventQueue_test, TestQueueMaxSize);
+    FRIEND_TEST(SocketParseMessageTest, TestProcessMessage);
+    FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved);
+    FRIEND_TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit);
+    FRIEND_TEST(StatsdStatsTest, TestAnomalyMonitor);
+    FRIEND_TEST(StatsdStatsTest, TestAtomDroppedStats);
+    FRIEND_TEST(StatsdStatsTest, TestAtomErrorStats);
+    FRIEND_TEST(StatsdStatsTest, TestAtomLog);
+    FRIEND_TEST(StatsdStatsTest, TestAtomLoggedAndDroppedAndSkippedStats);
+    FRIEND_TEST(StatsdStatsTest, TestAtomLoggedAndDroppedStats);
+    FRIEND_TEST(StatsdStatsTest, TestAtomMetricsStats);
+    FRIEND_TEST(StatsdStatsTest, TestAtomSkippedStats);
+    FRIEND_TEST(StatsdStatsTest, TestConfigRemove);
+    FRIEND_TEST(StatsdStatsTest, TestHasHitDimensionGuardrail);
     FRIEND_TEST(StatsdStatsTest, TestInvalidConfigAdd);
     FRIEND_TEST(StatsdStatsTest, TestInvalidConfigMissingMetricId);
     FRIEND_TEST(StatsdStatsTest, TestInvalidConfigOnlyMetricId);
-    FRIEND_TEST(StatsdStatsTest, TestConfigRemove);
-    FRIEND_TEST(StatsdStatsTest, TestSubStats);
-    FRIEND_TEST(StatsdStatsTest, TestAtomLog);
     FRIEND_TEST(StatsdStatsTest, TestNonPlatformAtomLog);
-    FRIEND_TEST(StatsdStatsTest, TestTimestampThreshold);
-    FRIEND_TEST(StatsdStatsTest, TestAnomalyMonitor);
-    FRIEND_TEST(StatsdStatsTest, TestSystemServerCrash);
     FRIEND_TEST(StatsdStatsTest, TestPullAtomStats);
-    FRIEND_TEST(StatsdStatsTest, TestAtomMetricsStats);
-    FRIEND_TEST(StatsdStatsTest, TestActivationBroadcastGuardrailHit);
-    FRIEND_TEST(StatsdStatsTest, TestAtomErrorStats);
-    FRIEND_TEST(StatsdStatsTest, TestAtomSkippedStats);
-    FRIEND_TEST(StatsdStatsTest, TestRestrictedMetricsStats);
+    FRIEND_TEST(StatsdStatsTest, TestQueueStats);
     FRIEND_TEST(StatsdStatsTest, TestRestrictedMetricsQueryStats);
-    FRIEND_TEST(StatsdStatsTest, TestAtomDroppedStats);
-    FRIEND_TEST(StatsdStatsTest, TestAtomLoggedAndDroppedStats);
-    FRIEND_TEST(StatsdStatsTest, TestAtomLoggedAndDroppedAndSkippedStats);
+    FRIEND_TEST(StatsdStatsTest, TestRestrictedMetricsStats);
     FRIEND_TEST(StatsdStatsTest, TestShardOffsetProvider);
-    FRIEND_TEST(StatsdStatsTest, TestHasHitDimensionGuardrail);
-    FRIEND_TEST(StatsdStatsTest, TestSubscriptionStarted);
-    FRIEND_TEST(StatsdStatsTest, TestSubscriptionFlushed);
-    FRIEND_TEST(StatsdStatsTest, TestSubscriptionEnded);
-    FRIEND_TEST(StatsdStatsTest, TestSubscriptionAtomPulled);
-    FRIEND_TEST(StatsdStatsTest, TestSubscriptionPullThreadWakeup);
-    FRIEND_TEST(StatsdStatsTest, TestSubscriptionStartedMaxActiveSubscriptions);
-    FRIEND_TEST(StatsdStatsTest, TestSubscriptionStartedRemoveFinishedSubscription);
     FRIEND_TEST(StatsdStatsTest, TestSocketLossStats);
     FRIEND_TEST(StatsdStatsTest, TestSocketLossStatsOverflowCounter);
-
-    FRIEND_TEST(StatsLogProcessorTest, InvalidConfigRemoved);
+    FRIEND_TEST(StatsdStatsTest, TestSubStats);
+    FRIEND_TEST(StatsdStatsTest, TestSubscriptionAtomPulled);
+    FRIEND_TEST(StatsdStatsTest, TestSubscriptionEnded);
+    FRIEND_TEST(StatsdStatsTest, TestSubscriptionFlushed);
+    FRIEND_TEST(StatsdStatsTest, TestSubscriptionPullThreadWakeup);
+    FRIEND_TEST(StatsdStatsTest, TestSubscriptionStarted);
+    FRIEND_TEST(StatsdStatsTest, TestSubscriptionStartedMaxActiveSubscriptions);
+    FRIEND_TEST(StatsdStatsTest, TestSubscriptionStartedRemoveFinishedSubscription);
+    FRIEND_TEST(StatsdStatsTest, TestSystemServerCrash);
+    FRIEND_TEST(StatsdStatsTest, TestTimestampThreshold);
+    FRIEND_TEST(StatsdStatsTest, TestValidConfigAdd);
 };
 
 InvalidConfigReason createInvalidConfigReasonWithMatcher(const InvalidConfigReasonEnum reason,
@@ -1077,10 +1088,10 @@
                                                               const int64_t subscriptionId);
 
 InvalidConfigReason createInvalidConfigReasonWithSubscriptionAndAlarm(
-        const InvalidConfigReasonEnum reason, const int64_t subscriptionId, const int64_t alarmId);
+        const InvalidConfigReasonEnum reason, int64_t subscriptionId, int64_t alarmId);
 
 InvalidConfigReason createInvalidConfigReasonWithSubscriptionAndAlert(
-        const InvalidConfigReasonEnum reason, const int64_t subscriptionId, const int64_t alertId);
+        const InvalidConfigReasonEnum reason, int64_t subscriptionId, int64_t alertId);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/statsd/src/guardrail/stats_log_enums.proto b/statsd/src/guardrail/stats_log_enums.proto
index 3db1e08..d47bc80 100644
--- a/statsd/src/guardrail/stats_log_enums.proto
+++ b/statsd/src/guardrail/stats_log_enums.proto
@@ -142,6 +142,12 @@
     INVALID_CONFIG_REASON_RESTRICTED_METRIC_NOT_ENABLED = 84;
     INVALID_CONFIG_REASON_RESTRICTED_METRIC_NOT_SUPPORTED = 85;
     INVALID_CONFIG_REASON_METRIC_INCORRECT_SAMPLING_PERCENTAGE = 86;
+    INVALID_CONFIG_REASON_GAUGE_METRIC_PULLED_WITH_SAMPLING = 87;
+    INVALID_CONFIG_REASON_MATCHER_NO_VALUE_MATCHER_NOR_STRING_REPLACER = 88;
+    INVALID_CONFIG_REASON_MATCHER_VALUE_MATCHER_WITH_POSITION_ALL = 89;
+    INVALID_CONFIG_REASON_MATCHER_INVALID_VALUE_MATCHER_WITH_STRING_REPLACE = 90;
+    INVALID_CONFIG_REASON_MATCHER_COMBINATION_WITH_STRING_REPLACE = 91;
+    INVALID_CONFIG_REASON_MATCHER_STRING_REPLACE_WITH_NO_VALUE_MATCHER_WITH_POSITION_ANY = 92;
 };
 
 enum InvalidQueryReason {
diff --git a/statsd/src/logd/LogEventQueue.cpp b/statsd/src/logd/LogEventQueue.cpp
index 96b7f01..a83fcf8 100644
--- a/statsd/src/logd/LogEventQueue.cpp
+++ b/statsd/src/logd/LogEventQueue.cpp
@@ -39,22 +39,23 @@
     return item;
 }
 
-bool LogEventQueue::push(unique_ptr<LogEvent> item, int64_t* oldestTimestampNs) {
-    bool success;
+LogEventQueue::Result LogEventQueue::push(unique_ptr<LogEvent> item) {
+    Result result;
     {
         std::unique_lock<std::mutex> lock(mMutex);
         if (mQueue.size() < mQueueLimit) {
             mQueue.push(std::move(item));
-            success = true;
+            result.success = true;
         } else {
             // safe operation as queue must not be empty.
-            *oldestTimestampNs = mQueue.front()->GetElapsedTimestampNs();
-            success = false;
+            result.oldestTimestampNs = mQueue.front()->GetElapsedTimestampNs();
+            result.success = false;
         }
+        result.size = mQueue.size();
     }
 
     mCondition.notify_one();
-    return success;
+    return result;
 }
 
 }  // namespace statsd
diff --git a/statsd/src/logd/LogEventQueue.h b/statsd/src/logd/LogEventQueue.h
index 53f2adf..d01f3a9 100644
--- a/statsd/src/logd/LogEventQueue.h
+++ b/statsd/src/logd/LogEventQueue.h
@@ -40,12 +40,18 @@
      */
     std::unique_ptr<LogEvent> waitPop();
 
+    struct Result {
+        bool success = false;
+        int64_t oldestTimestampNs = 0;
+        int32_t size = 0;
+    };
+
     /**
      * Puts a LogEvent ptr to the end of the queue.
      * Returns false on failure when the queue is full, and output the oldest event timestamp
-     * in the queue.
+     * in the queue. Returns true on success and new queue size.
      */
-    bool push(std::unique_ptr<LogEvent> event, int64_t* oldestTimestampNs);
+    Result push(std::unique_ptr<LogEvent> event);
 
 private:
     const size_t mQueueLimit;
diff --git a/statsd/src/matchers/AtomMatchingTracker.h b/statsd/src/matchers/AtomMatchingTracker.h
index 308f7e1..49c35b2 100644
--- a/statsd/src/matchers/AtomMatchingTracker.h
+++ b/statsd/src/matchers/AtomMatchingTracker.h
@@ -32,6 +32,11 @@
 namespace os {
 namespace statsd {
 
+struct MatcherInitResult {
+    optional<InvalidConfigReason> invalidConfigReason;
+    bool hasStringTransformation;
+};
+
 class AtomMatchingTracker : public virtual RefBase {
 public:
     AtomMatchingTracker(const int64_t id, const uint64_t protoHash)
@@ -49,7 +54,7 @@
     //                          CombinationAtomMatchingTrackers using DFS.
     // stack: a bit map to record which matcher has been visited on the stack. This is for detecting
     //        circle dependency.
-    virtual optional<InvalidConfigReason> init(
+    virtual MatcherInitResult init(
             int matcherIndex, const std::vector<AtomMatcher>& allAtomMatchers,
             const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
             const std::unordered_map<int64_t, int>& matcherMap, std::vector<uint8_t>& stack) = 0;
@@ -70,9 +75,11 @@
     // 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.
+    // matcherTransformations: the cached transformations for all matchers for this event.
     virtual void onLogEvent(const LogEvent& event, int matcherIndex,
                             const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
-                            std::vector<MatchingState>& matcherResults) = 0;
+                            std::vector<MatchingState>& matcherResults,
+                            std::vector<std::shared_ptr<LogEvent>>& matcherTransformations) = 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
diff --git a/statsd/src/matchers/CombinationAtomMatchingTracker.cpp b/statsd/src/matchers/CombinationAtomMatchingTracker.cpp
index 68811cf..e97188f 100644
--- a/statsd/src/matchers/CombinationAtomMatchingTracker.cpp
+++ b/statsd/src/matchers/CombinationAtomMatchingTracker.cpp
@@ -25,6 +25,7 @@
 namespace statsd {
 
 using std::set;
+using std::shared_ptr;
 using std::unordered_map;
 using std::vector;
 
@@ -36,12 +37,16 @@
 CombinationAtomMatchingTracker::~CombinationAtomMatchingTracker() {
 }
 
-optional<InvalidConfigReason> CombinationAtomMatchingTracker::init(
+MatcherInitResult CombinationAtomMatchingTracker::init(
         int matcherIndex, const vector<AtomMatcher>& allAtomMatchers,
         const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
         const unordered_map<int64_t, int>& matcherMap, vector<uint8_t>& stack) {
+    MatcherInitResult result{nullopt /* invalidConfigReason */,
+                             false /* hasStringTransformation */};
     if (mInitialized) {
-        return nullopt;
+        // CombinationMatchers do not support string transformations so if mInitialized = true,
+        // we know that there is no string transformation and we do not need to check for it again.
+        return result;
     }
 
     // mark this node as visited in the recursion stack.
@@ -51,26 +56,27 @@
 
     // LogicalOperation is missing in the config
     if (!matcher.has_operation()) {
-        return createInvalidConfigReasonWithMatcher(INVALID_CONFIG_REASON_MATCHER_NO_OPERATION,
-                                                    mId);
+        result.invalidConfigReason = createInvalidConfigReasonWithMatcher(
+                INVALID_CONFIG_REASON_MATCHER_NO_OPERATION, mId);
+        return result;
     }
 
     mLogicalOperation = matcher.operation();
 
     if (mLogicalOperation == LogicalOperation::NOT && matcher.matcher_size() != 1) {
-        return createInvalidConfigReasonWithMatcher(
+        result.invalidConfigReason = createInvalidConfigReasonWithMatcher(
                 INVALID_CONFIG_REASON_MATCHER_NOT_OPERATION_IS_NOT_UNARY, mId);
+        return result;
     }
 
     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);
-            optional<InvalidConfigReason> invalidConfigReason =
-                    createInvalidConfigReasonWithMatcher(
-                            INVALID_CONFIG_REASON_MATCHER_CHILD_NOT_FOUND, mId);
-            invalidConfigReason->matcherIds.push_back(child);
-            return invalidConfigReason;
+            result.invalidConfigReason = createInvalidConfigReasonWithMatcher(
+                    INVALID_CONFIG_REASON_MATCHER_CHILD_NOT_FOUND, mId);
+            result.invalidConfigReason->matcherIds.push_back(child);
+            return result;
         }
 
         int childIndex = pair->second;
@@ -78,18 +84,27 @@
         // if the child is a visited node in the recursion -> circle detected.
         if (stack[childIndex]) {
             ALOGE("Circle detected in matcher config");
-            optional<InvalidConfigReason> invalidConfigReason =
+            result.invalidConfigReason =
                     createInvalidConfigReasonWithMatcher(INVALID_CONFIG_REASON_MATCHER_CYCLE, mId);
-            invalidConfigReason->matcherIds.push_back(child);
-            return invalidConfigReason;
+            result.invalidConfigReason->matcherIds.push_back(child);
+            return result;
         }
-        optional<InvalidConfigReason> invalidConfigReason =
+        auto [invalidConfigReason, hasStringTransformation] =
                 allAtomMatchingTrackers[childIndex]->init(
                         childIndex, allAtomMatchers, allAtomMatchingTrackers, matcherMap, stack);
+        if (hasStringTransformation) {
+            ALOGE("String transformation detected in CombinationMatcher");
+            result.invalidConfigReason = createInvalidConfigReasonWithMatcher(
+                    INVALID_CONFIG_REASON_MATCHER_COMBINATION_WITH_STRING_REPLACE, mId);
+            result.hasStringTransformation = true;
+            return result;
+        }
+
         if (invalidConfigReason.has_value()) {
             ALOGW("child matcher init failed %lld", (long long)child);
             invalidConfigReason->matcherIds.push_back(mId);
-            return invalidConfigReason;
+            result.invalidConfigReason = invalidConfigReason;
+            return result;
         }
 
         mChildren.push_back(childIndex);
@@ -101,13 +116,13 @@
     mInitialized = true;
     // unmark this node in the recursion stack.
     stack[matcherIndex] = false;
-    return nullopt;
+    return result;
 }
 
 optional<InvalidConfigReason> CombinationAtomMatchingTracker::onConfigUpdated(
         const AtomMatcher& matcher, const unordered_map<int64_t, int>& atomMatchingTrackerMap) {
     mChildren.clear();
-    AtomMatcher_Combination combinationMatcher = matcher.combination();
+    const AtomMatcher_Combination& combinationMatcher = matcher.combination();
     for (const int64_t child : combinationMatcher.matcher()) {
         const auto& pair = atomMatchingTrackerMap.find(child);
         if (pair == atomMatchingTrackerMap.end()) {
@@ -126,7 +141,8 @@
 void CombinationAtomMatchingTracker::onLogEvent(
         const LogEvent& event, int matcherIndex,
         const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
-        vector<MatchingState>& matcherResults) {
+        vector<MatchingState>& matcherResults,
+        vector<shared_ptr<LogEvent>>& matcherTransformations) {
     // this event has been processed.
     if (matcherResults[matcherIndex] != MatchingState::kNotComputed) {
         return;
@@ -141,7 +157,8 @@
     for (const int childIndex : mChildren) {
         if (matcherResults[childIndex] == MatchingState::kNotComputed) {
             const sp<AtomMatchingTracker>& child = allAtomMatchingTrackers[childIndex];
-            child->onLogEvent(event, childIndex, allAtomMatchingTrackers, matcherResults);
+            child->onLogEvent(event, childIndex, allAtomMatchingTrackers, matcherResults,
+                              matcherTransformations);
         }
     }
 
diff --git a/statsd/src/matchers/CombinationAtomMatchingTracker.h b/statsd/src/matchers/CombinationAtomMatchingTracker.h
index 1a6146c..9e06533 100644
--- a/statsd/src/matchers/CombinationAtomMatchingTracker.h
+++ b/statsd/src/matchers/CombinationAtomMatchingTracker.h
@@ -31,10 +31,10 @@
 public:
     CombinationAtomMatchingTracker(const int64_t id, const uint64_t protoHash);
 
-    optional<InvalidConfigReason> init(
-            int matcherIndex, const std::vector<AtomMatcher>& allAtomMatchers,
-            const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
-            const std::unordered_map<int64_t, int>& matcherMap, std::vector<uint8_t>& stack);
+    MatcherInitResult init(int matcherIndex, const std::vector<AtomMatcher>& allAtomMatchers,
+                           const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+                           const std::unordered_map<int64_t, int>& matcherMap,
+                           std::vector<uint8_t>& stack);
 
     optional<InvalidConfigReason> onConfigUpdated(
             const AtomMatcher& matcher,
@@ -44,7 +44,8 @@
 
     void onLogEvent(const LogEvent& event, int matcherIndex,
                     const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
-                    std::vector<MatchingState>& matcherResults) override;
+                    std::vector<MatchingState>& matcherResults,
+                    std::vector<std::shared_ptr<LogEvent>>& matcherTransformations) override;
 
 private:
     LogicalOperation mLogicalOperation;
diff --git a/statsd/src/matchers/EventMatcherWizard.cpp b/statsd/src/matchers/EventMatcherWizard.cpp
index 18a9074..07f6f4c 100644
--- a/statsd/src/matchers/EventMatcherWizard.cpp
+++ b/statsd/src/matchers/EventMatcherWizard.cpp
@@ -19,16 +19,16 @@
 namespace os {
 namespace statsd {
 
-using std::vector;
-
-MatchingState EventMatcherWizard::matchLogEvent(const LogEvent& event, int matcherIndex) {
+MatchLogEventResult EventMatcherWizard::matchLogEvent(const LogEvent& event, int matcherIndex) {
     if (matcherIndex < 0 || matcherIndex >= (int)mAllEventMatchers.size()) {
-        return MatchingState::kNotComputed;
+        return {MatchingState::kNotComputed, nullptr};
     }
     std::fill(mMatcherCache.begin(), mMatcherCache.end(), MatchingState::kNotComputed);
+    std::fill(mMatcherTransformations.begin(), mMatcherTransformations.end(), nullptr);
     mAllEventMatchers[matcherIndex]->onLogEvent(event, matcherIndex, mAllEventMatchers,
-                                                mMatcherCache);
-    return mMatcherCache[matcherIndex];
+                                                mMatcherCache, mMatcherTransformations);
+
+    return {mMatcherCache[matcherIndex], mMatcherTransformations[matcherIndex]};
 }
 
 }  // namespace statsd
diff --git a/statsd/src/matchers/EventMatcherWizard.h b/statsd/src/matchers/EventMatcherWizard.h
index c4ad150..b2ef311 100644
--- a/statsd/src/matchers/EventMatcherWizard.h
+++ b/statsd/src/matchers/EventMatcherWizard.h
@@ -22,20 +22,27 @@
 namespace os {
 namespace statsd {
 
+struct MatchLogEventResult {
+    MatchingState matchingState;
+    std::shared_ptr<LogEvent> transformedEvent;
+};
+
 class EventMatcherWizard : public virtual RefBase {
 public:
     EventMatcherWizard(){};  // for testing
     EventMatcherWizard(const std::vector<sp<AtomMatchingTracker>>& eventTrackers)
         : mAllEventMatchers(eventTrackers),
-          mMatcherCache(eventTrackers.size(), MatchingState::kNotComputed){};
+          mMatcherCache(eventTrackers.size(), MatchingState::kNotComputed),
+          mMatcherTransformations(eventTrackers.size(), nullptr){};
 
     virtual ~EventMatcherWizard(){};
 
-    MatchingState matchLogEvent(const LogEvent& event, int matcherIndex);
+    MatchLogEventResult matchLogEvent(const LogEvent& event, int matcherIndex);
 
 private:
     std::vector<sp<AtomMatchingTracker>> mAllEventMatchers;
     std::vector<MatchingState> mMatcherCache;
+    std::vector<std::shared_ptr<LogEvent>> mMatcherTransformations;
 };
 
 }  // namespace statsd
diff --git a/statsd/src/matchers/SimpleAtomMatchingTracker.cpp b/statsd/src/matchers/SimpleAtomMatchingTracker.cpp
index 81d0710..db45560 100644
--- a/statsd/src/matchers/SimpleAtomMatchingTracker.cpp
+++ b/statsd/src/matchers/SimpleAtomMatchingTracker.cpp
@@ -23,6 +23,7 @@
 namespace os {
 namespace statsd {
 
+using std::shared_ptr;
 using std::unordered_map;
 using std::vector;
 
@@ -41,16 +42,27 @@
 SimpleAtomMatchingTracker::~SimpleAtomMatchingTracker() {
 }
 
-optional<InvalidConfigReason> SimpleAtomMatchingTracker::init(
+MatcherInitResult SimpleAtomMatchingTracker::init(
         int matcherIndex, const vector<AtomMatcher>& allAtomMatchers,
         const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
         const unordered_map<int64_t, int>& matcherMap, vector<uint8_t>& stack) {
+    MatcherInitResult result{nullopt /* invalidConfigReason */,
+                             false /* hasStringTransformation */};
     // no need to do anything.
     if (!mInitialized) {
-        return createInvalidConfigReasonWithMatcher(
+        result.invalidConfigReason = createInvalidConfigReasonWithMatcher(
                 INVALID_CONFIG_REASON_MATCHER_TRACKER_NOT_INITIALIZED, mId);
+        return result;
     }
-    return nullopt;
+
+    for (const FieldValueMatcher& fvm : mMatcher.field_value_matcher()) {
+        if (fvm.has_replace_string()) {
+            result.hasStringTransformation = true;
+            break;
+        }
+    }
+
+    return result;
 }
 
 optional<InvalidConfigReason> SimpleAtomMatchingTracker::onConfigUpdated(
@@ -66,7 +78,8 @@
 void SimpleAtomMatchingTracker::onLogEvent(
         const LogEvent& event, int matcherIndex,
         const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
-        vector<MatchingState>& matcherResults) {
+        vector<MatchingState>& matcherResults,
+        vector<shared_ptr<LogEvent>>& matcherTransformations) {
     if (matcherResults[matcherIndex] != MatchingState::kNotComputed) {
         VLOG("Matcher %lld already evaluated ", (long long)mId);
         return;
@@ -77,9 +90,13 @@
         return;
     }
 
-    bool matched = matchesSimple(mUidMap, mMatcher, event);
+    auto [matched, transformedEvent] = matchesSimple(mUidMap, mMatcher, event);
     matcherResults[matcherIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
     VLOG("Stats SimpleAtomMatcher %lld matched? %d", (long long)mId, matched);
+
+    if (matched && transformedEvent != nullptr) {
+        matcherTransformations[matcherIndex] = std::move(transformedEvent);
+    }
 }
 
 }  // namespace statsd
diff --git a/statsd/src/matchers/SimpleAtomMatchingTracker.h b/statsd/src/matchers/SimpleAtomMatchingTracker.h
index 0cfb248..641e9d8 100644
--- a/statsd/src/matchers/SimpleAtomMatchingTracker.h
+++ b/statsd/src/matchers/SimpleAtomMatchingTracker.h
@@ -35,11 +35,10 @@
 
     ~SimpleAtomMatchingTracker();
 
-    optional<InvalidConfigReason> init(
-            int matcherIndex, const std::vector<AtomMatcher>& allAtomMatchers,
-            const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
-            const std::unordered_map<int64_t, int>& matcherMap,
-            std::vector<uint8_t>& stack) override;
+    MatcherInitResult init(int matcherIndex, const std::vector<AtomMatcher>& allAtomMatchers,
+                           const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
+                           const std::unordered_map<int64_t, int>& matcherMap,
+                           std::vector<uint8_t>& stack) override;
 
     optional<InvalidConfigReason> onConfigUpdated(
             const AtomMatcher& matcher,
@@ -47,7 +46,8 @@
 
     void onLogEvent(const LogEvent& event, int matcherIndex,
                     const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
-                    std::vector<MatchingState>& matcherResults) override;
+                    std::vector<MatchingState>& matcherResults,
+                    std::vector<std::shared_ptr<LogEvent>>& matcherTransformations) override;
 
 private:
     const SimpleAtomMatcher mMatcher;
diff --git a/statsd/src/matchers/matcher_util.cpp b/statsd/src/matchers/matcher_util.cpp
index 20450dd..2f7ec81 100644
--- a/statsd/src/matchers/matcher_util.cpp
+++ b/statsd/src/matchers/matcher_util.cpp
@@ -23,9 +23,11 @@
 #include "matchers/AtomMatchingTracker.h"
 #include "src/statsd_config.pb.h"
 #include "stats_util.h"
+#include "utils/Regex.h"
 
 using std::set;
 using std::string;
+using std::unique_ptr;
 using std::vector;
 
 namespace android {
@@ -84,24 +86,23 @@
     return matched;
 }
 
-bool tryMatchString(const sp<UidMap>& uidMap, const FieldValue& fieldValue,
-                    const string& str_match) {
+static 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*/);
-        return packageNames.find(str_match) != packageNames.end();
+        return uidMap->hasApp(uid, str_match);
     } else if (fieldValue.mValue.getType() == STRING) {
         return fieldValue.mValue.str_value == str_match;
     }
     return false;
 }
 
-bool tryMatchWildcardString(const sp<UidMap>& uidMap, const FieldValue& fieldValue,
-                            const string& wildcardPattern) {
+static bool tryMatchWildcardString(const sp<UidMap>& uidMap, const FieldValue& fieldValue,
+                                   const string& wildcardPattern) {
     if (isAttributionUidField(fieldValue) || isUidField(fieldValue)) {
         int uid = fieldValue.mValue.int_value;
         // TODO(b/236886985): replace aid/uid mapping with efficient bidirectional container
@@ -115,7 +116,7 @@
                 }
             }
         }
-        std::set<string> packageNames = uidMap->getAppNamesFromUid(uid, true /* normalize*/);
+        std::set<string> packageNames = uidMap->getAppNamesFromUid(uid, false /* normalize*/);
         for (const auto& packageName : packageNames) {
             if (fnmatch(wildcardPattern.c_str(), packageName.c_str(), 0) == 0) {
                 return true;
@@ -127,17 +128,42 @@
     return false;
 }
 
-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");
-        return false;
+static unique_ptr<LogEvent> getTransformedEvent(const FieldValueMatcher& matcher,
+                                                const LogEvent& event, int start, int end) {
+    if (!matcher.has_replace_string()) {
+        return nullptr;
     }
 
-    if (start >= end) {
-        return false;
+    unique_ptr<Regex> re = Regex::create(matcher.replace_string().regex());
+
+    if (re == nullptr) {
+        return nullptr;
     }
 
+    const string& replacement = matcher.replace_string().replacement();
+    unique_ptr<LogEvent> transformedEvent = nullptr;
+    for (int i = start; i < end; i++) {
+        const LogEvent& eventRef = transformedEvent == nullptr ? event : *transformedEvent;
+        const FieldValue& fieldValue = eventRef.getValues()[i];
+        if (fieldValue.mValue.getType() != STRING) {
+            continue;
+        }
+        string str = fieldValue.mValue.str_value;
+        if (!re->replace(str, replacement) || str == fieldValue.mValue.str_value) {
+            continue;
+        }
+
+        // String transformation occurred, update the FieldValue in transformedEvent.
+        if (transformedEvent == nullptr) {
+            transformedEvent = std::make_unique<LogEvent>(event);
+        }
+        (*transformedEvent->getMutableValues())[i].mValue.str_value = str;
+    }
+    return transformedEvent;
+}
+
+static pair<int, int> getStartEndAtDepth(int targetField, int start, int end, int depth,
+                                         const vector<FieldValue>& values) {
     // Filter by entry field first
     int newStart = -1;
     int newEnd = end;
@@ -145,31 +171,45 @@
     // break when pos is larger than the one we are searching for.
     for (int i = start; i < end; i++) {
         int pos = values[i].mField.getPosAtDepth(depth);
-        if (pos == matcher.field()) {
+        if (pos == targetField) {
             if (newStart == -1) {
                 newStart = i;
             }
             newEnd = i + 1;
-        } else if (pos > matcher.field()) {
+        } else if (pos > targetField) {
             break;
         }
     }
 
+    return {newStart, newEnd};
+}
+
+/*
+ * Returns pairs of start-end indices in vector<FieldValue> that pariticipate in matching.
+ * The returned vector is empty if an error was encountered.
+ * If Position is ANY and value_matcher is matches_tuple, the vector contains a start/end pair
+ * corresponding for each child FieldValueMatcher in matches_tuple. For all other cases, the
+ * returned vector is of size 1.
+ *
+ * Also updates the depth reference parameter if matcher has Position specified.
+ */
+static vector<pair<int, int>> computeRanges(const FieldValueMatcher& matcher,
+                                            const vector<FieldValue>& values, int start, int end,
+                                            int& depth) {
     // Now we have zoomed in to a new range
-    start = newStart;
-    end = newEnd;
+    std::tie(start, end) = getStartEndAtDepth(matcher.field(), start, end, depth, values);
 
     if (start == -1) {
         // No such field found.
-        return false;
+        return {};
     }
 
-    vector<pair<int, int>> ranges; // the ranges are for matching ANY position
+    vector<pair<int, int>> ranges;
     if (matcher.has_position()) {
         // Repeated fields position is stored as a node in the path.
         depth++;
         if (depth > 2) {
-            return false;
+            return ranges;
         }
         switch (matcher.position()) {
             case Position::FIRST: {
@@ -196,27 +236,42 @@
                 ranges.push_back(std::make_pair(start, end));
                 break;
             }
+            case Position::ALL:
+                // ALL is only supported for string transformation. If a value_matcher other than
+                // matches_tuple is present, the matcher is invalid. This is enforced when
+                // the AtomMatchingTracker is initialized.
+
+                // fallthrough
             case Position::ANY: {
-                // ANY means all the children matchers match in any of the sub trees, it's a match
-                newStart = start;
-                newEnd = end;
-                // Here start is guaranteed to be a valid index.
-                int currentPos = values[start].mField.getPosAtDepth(depth);
-                // Now find all sub trees ranges.
-                for (int i = start; i < end; i++) {
-                    int newPos = values[i].mField.getPosAtDepth(depth);
-                    if (newPos != currentPos) {
-                        ranges.push_back(std::make_pair(newStart, i));
-                        newStart = i;
-                        currentPos = newPos;
+                // For string transformation, this case is treated the same as Position:ALL.
+                // Given a matcher on attribution_node[ANY].tag with a matches_tuple containing a
+                // child FieldValueMatcher with eq_string: "foo" and regex_replace: "[\d]+$" --> "",
+                // an event with attribution tags: ["bar123", "foo12", "abc230"] will transform to
+                // have attribution tags ["bar", "foo", "abc"] and will be a successful match.
+
+                // Note that if value_matcher is matches_tuple, there should be no string
+                // transformation on this matcher. However, child FieldValueMatchers in
+                // matches_tuple can have string transformations. This is enforced when
+                // AtomMatchingTracker is initialized.
+
+                if (matcher.value_matcher_case() == FieldValueMatcher::kMatchesTuple) {
+                    // For ANY with matches_tuple, if all the children matchers match in any of the
+                    // sub trees, it's a match.
+                    // Here start is guaranteed to be a valid index.
+                    int currentPos = values[start].mField.getPosAtDepth(depth);
+                    // Now find all sub trees ranges.
+                    for (int i = start; i < end; i++) {
+                        int newPos = values[i].mField.getPosAtDepth(depth);
+                        if (newPos != currentPos) {
+                            ranges.push_back(std::make_pair(start, i));
+                            start = i;
+                            currentPos = newPos;
+                        }
                     }
                 }
-                ranges.push_back(std::make_pair(newStart, end));
+                ranges.push_back(std::make_pair(start, end));
                 break;
             }
-            case Position::ALL:
-                ALOGE("Not supported: field matcher with ALL position.");
-                break;
             case Position::POSITION_UNKNOWN:
                 break;
         }
@@ -224,23 +279,60 @@
         // No position
         ranges.push_back(std::make_pair(start, end));
     }
-    // start and end are still pointing to the matched range.
+
+    return ranges;
+}
+
+static MatchResult matchesSimple(const sp<UidMap>& uidMap, const FieldValueMatcher& matcher,
+                                 const LogEvent& event, int start, int end, int depth) {
+    if (depth > 2) {
+        ALOGE("Depth >= 3 not supported");
+        return {false, nullptr};
+    }
+
+    if (start >= end) {
+        return {false, nullptr};
+    }
+
+    const vector<pair<int, int>> ranges =
+            computeRanges(matcher, event.getValues(), start, end, depth);
+
+    if (ranges.empty()) {
+        // No such field found.
+        return {false, nullptr};
+    }
+
+    // ranges should have exactly one start/end pair at this point unless position is ANY and
+    // value_matcher is matches_tuple.
+    std::tie(start, end) = ranges[0];
+
+    unique_ptr<LogEvent> transformedEvent = getTransformedEvent(matcher, event, start, end);
+
+    const vector<FieldValue>& values =
+            transformedEvent == nullptr ? event.getValues() : transformedEvent->getValues();
+
     switch (matcher.value_matcher_case()) {
         case FieldValueMatcher::kMatchesTuple: {
             ++depth;
             // If any range matches all matchers, good.
-            for (const auto& range : ranges) {
+            bool matchResult = false;
+            for (const auto& [rangeStart, rangeEnd] : ranges) {
                 bool matched = true;
                 for (const auto& subMatcher : matcher.matches_tuple().field_value_matcher()) {
-                    if (!matchesSimple(uidMap, subMatcher, values, range.first, range.second,
-                                       depth)) {
+                    const LogEvent& eventRef =
+                            transformedEvent == nullptr ? event : *transformedEvent;
+                    auto [hasMatched, newTransformedEvent] = matchesSimple(
+                            uidMap, subMatcher, eventRef, rangeStart, rangeEnd, depth);
+                    if (newTransformedEvent != nullptr) {
+                        transformedEvent = std::move(newTransformedEvent);
+                    }
+                    if (!hasMatched) {
                         matched = false;
-                        break;
                     }
                 }
-                if (matched) return true;
+                matchResult = matchResult || matched;
             }
-            return false;
+            return {matchResult, std::move(transformedEvent)};
         }
         // Finally, we get to the point of real value matching.
         // If the field matcher ends with ANY, then we have [start, end) range > 1.
@@ -251,18 +343,18 @@
                      (values[i].mValue.int_value != 0) == matcher.eq_bool()) ||
                     (values[i].mValue.getType() == LONG &&
                      (values[i].mValue.long_value != 0) == matcher.eq_bool())) {
-                    return true;
+                    return {true, std::move(transformedEvent)};
                 }
             }
-            return false;
+            return {false, std::move(transformedEvent)};
         }
         case FieldValueMatcher::ValueMatcherCase::kEqString: {
             for (int i = start; i < end; i++) {
                 if (tryMatchString(uidMap, values[i], matcher.eq_string())) {
-                    return true;
+                    return {true, std::move(transformedEvent)};
                 }
             }
-            return false;
+            return {false, std::move(transformedEvent)};
         }
         case FieldValueMatcher::ValueMatcherCase::kNeqAnyString: {
             const auto& str_list = matcher.neq_any_string();
@@ -275,40 +367,40 @@
                     }
                 }
                 if (notEqAll) {
-                    return true;
+                    return {true, std::move(transformedEvent)};
                 }
             }
-            return false;
+            return {false, std::move(transformedEvent)};
         }
         case FieldValueMatcher::ValueMatcherCase::kEqAnyString: {
             const auto& str_list = matcher.eq_any_string();
             for (int i = start; i < end; i++) {
                 for (const auto& str : str_list.str_value()) {
                     if (tryMatchString(uidMap, values[i], str)) {
-                        return true;
+                        return {true, std::move(transformedEvent)};
                     }
                 }
             }
-            return false;
+            return {false, std::move(transformedEvent)};
         }
         case FieldValueMatcher::ValueMatcherCase::kEqWildcardString: {
             for (int i = start; i < end; i++) {
                 if (tryMatchWildcardString(uidMap, values[i], matcher.eq_wildcard_string())) {
-                    return true;
+                    return {true, std::move(transformedEvent)};
                 }
             }
-            return false;
+            return {false, std::move(transformedEvent)};
         }
         case FieldValueMatcher::ValueMatcherCase::kEqAnyWildcardString: {
             const auto& str_list = matcher.eq_any_wildcard_string();
             for (int i = start; i < end; i++) {
                 for (const auto& str : str_list.str_value()) {
                     if (tryMatchWildcardString(uidMap, values[i], str)) {
-                        return true;
+                        return {true, std::move(transformedEvent)};
                     }
                 }
             }
-            return false;
+            return {false, std::move(transformedEvent)};
         }
         case FieldValueMatcher::ValueMatcherCase::kNeqAnyWildcardString: {
             const auto& str_list = matcher.neq_any_wildcard_string();
@@ -321,24 +413,24 @@
                     }
                 }
                 if (notEqAll) {
-                    return true;
+                    return {true, std::move(transformedEvent)};
                 }
             }
-            return false;
+            return {false, std::move(transformedEvent)};
         }
         case FieldValueMatcher::ValueMatcherCase::kEqInt: {
             for (int i = start; i < end; i++) {
                 if (values[i].mValue.getType() == INT &&
                     (matcher.eq_int() == values[i].mValue.int_value)) {
-                    return true;
+                    return {true, std::move(transformedEvent)};
                 }
                 // eq_int covers both int and long.
                 if (values[i].mValue.getType() == LONG &&
                     (matcher.eq_int() == values[i].mValue.long_value)) {
-                    return true;
+                    return {true, std::move(transformedEvent)};
                 }
             }
-            return false;
+            return {false, std::move(transformedEvent)};
         }
         case FieldValueMatcher::ValueMatcherCase::kEqAnyInt: {
             const auto& int_list = matcher.eq_any_int();
@@ -346,16 +438,16 @@
                 for (const int int_value : int_list.int_value()) {
                     if (values[i].mValue.getType() == INT &&
                         (int_value == values[i].mValue.int_value)) {
-                        return true;
+                        return {true, std::move(transformedEvent)};
                     }
                     // eq_any_int covers both int and long.
                     if (values[i].mValue.getType() == LONG &&
                         (int_value == values[i].mValue.long_value)) {
-                        return true;
+                        return {true, std::move(transformedEvent)};
                     }
                 }
             }
-            return false;
+            return {false, std::move(transformedEvent)};
         }
         case FieldValueMatcher::ValueMatcherCase::kNeqAnyInt: {
             const auto& int_list = matcher.neq_any_int();
@@ -375,102 +467,113 @@
                     }
                 }
                 if (notEqAll) {
-                    return true;
+                    return {true, std::move(transformedEvent)};
                 }
             }
-            return false;
+            return {false, std::move(transformedEvent)};
         }
         case FieldValueMatcher::ValueMatcherCase::kLtInt: {
             for (int i = start; i < end; i++) {
                 if (values[i].mValue.getType() == INT &&
                     (values[i].mValue.int_value < matcher.lt_int())) {
-                    return true;
+                    return {true, std::move(transformedEvent)};
                 }
                 // lt_int covers both int and long.
                 if (values[i].mValue.getType() == LONG &&
                     (values[i].mValue.long_value < matcher.lt_int())) {
-                    return true;
+                    return {true, std::move(transformedEvent)};
                 }
             }
-            return false;
+            return {false, std::move(transformedEvent)};
         }
         case FieldValueMatcher::ValueMatcherCase::kGtInt: {
             for (int i = start; i < end; i++) {
                 if (values[i].mValue.getType() == INT &&
                     (values[i].mValue.int_value > matcher.gt_int())) {
-                    return true;
+                    return {true, std::move(transformedEvent)};
                 }
                 // gt_int covers both int and long.
                 if (values[i].mValue.getType() == LONG &&
                     (values[i].mValue.long_value > matcher.gt_int())) {
-                    return true;
+                    return {true, std::move(transformedEvent)};
                 }
             }
-            return false;
+            return {false, std::move(transformedEvent)};
         }
         case FieldValueMatcher::ValueMatcherCase::kLtFloat: {
             for (int i = start; i < end; i++) {
                 if (values[i].mValue.getType() == FLOAT &&
                     (values[i].mValue.float_value < matcher.lt_float())) {
-                    return true;
+                    return {true, std::move(transformedEvent)};
                 }
             }
-            return false;
+            return {false, std::move(transformedEvent)};
         }
         case FieldValueMatcher::ValueMatcherCase::kGtFloat: {
             for (int i = start; i < end; i++) {
                 if (values[i].mValue.getType() == FLOAT &&
                     (values[i].mValue.float_value > matcher.gt_float())) {
-                    return true;
+                    return {true, std::move(transformedEvent)};
                 }
             }
-            return false;
+            return {false, std::move(transformedEvent)};
         }
         case FieldValueMatcher::ValueMatcherCase::kLteInt: {
             for (int i = start; i < end; i++) {
                 if (values[i].mValue.getType() == INT &&
                     (values[i].mValue.int_value <= matcher.lte_int())) {
-                    return true;
+                    return {true, std::move(transformedEvent)};
                 }
                 // lte_int covers both int and long.
                 if (values[i].mValue.getType() == LONG &&
                     (values[i].mValue.long_value <= matcher.lte_int())) {
-                    return true;
+                    return {true, std::move(transformedEvent)};
                 }
             }
-            return false;
+            return {false, std::move(transformedEvent)};
         }
         case FieldValueMatcher::ValueMatcherCase::kGteInt: {
             for (int i = start; i < end; i++) {
                 if (values[i].mValue.getType() == INT &&
                     (values[i].mValue.int_value >= matcher.gte_int())) {
-                    return true;
+                    return {true, std::move(transformedEvent)};
                 }
                 // gte_int covers both int and long.
                 if (values[i].mValue.getType() == LONG &&
                     (values[i].mValue.long_value >= matcher.gte_int())) {
-                    return true;
+                    return {true, std::move(transformedEvent)};
                 }
             }
-            return false;
+            return {false, std::move(transformedEvent)};
         }
         default:
-            return false;
+            // This only happens if the matcher has a string transformation and no value_matcher. So
+            // the default match result is true. If there is no string transformation either then
+            // this matcher is invalid, which is enforced when the AtomMatchingTracker is
+            // initialized.
+            return {true, std::move(transformedEvent)};
     }
 }
 
-bool matchesSimple(const sp<UidMap>& uidMap, const SimpleAtomMatcher& simpleMatcher,
-                   const LogEvent& event) {
+MatchResult matchesSimple(const sp<UidMap>& uidMap, const SimpleAtomMatcher& simpleMatcher,
+                          const LogEvent& event) {
     if (event.GetTagId() != simpleMatcher.atom_id()) {
-        return false;
+        return {false, nullptr};
     }
 
+    unique_ptr<LogEvent> transformedEvent = nullptr;
     for (const auto& matcher : simpleMatcher.field_value_matcher()) {
-        if (!matchesSimple(uidMap, matcher, event.getValues(), 0, event.getValues().size(), 0)) {
-            return false;
+        const LogEvent& inputEvent = transformedEvent == nullptr ? event : *transformedEvent;
+        auto [hasMatched, newTransformedEvent] =
+                matchesSimple(uidMap, matcher, inputEvent, 0, inputEvent.getValues().size(), 0);
+        if (newTransformedEvent != nullptr) {
+            transformedEvent = std::move(newTransformedEvent);
+        }
+        if (!hasMatched) {
+            return {false, std::move(transformedEvent)};
         }
     }
-    return true;
+    return {true, std::move(transformedEvent)};
 }
 
 }  // namespace statsd
diff --git a/statsd/src/matchers/matcher_util.h b/statsd/src/matchers/matcher_util.h
index 597b74f..5fb7c42 100644
--- a/statsd/src/matchers/matcher_util.h
+++ b/statsd/src/matchers/matcher_util.h
@@ -33,11 +33,16 @@
     kMatched = 1,
 };
 
+struct MatchResult {
+    bool matched;
+    std::unique_ptr<LogEvent> transformedEvent;
+};
+
 bool combinationMatch(const std::vector<int>& children, const LogicalOperation& operation,
                       const std::vector<MatchingState>& matcherResults);
 
-bool matchesSimple(const sp<UidMap>& uidMap, const SimpleAtomMatcher& simpleMatcher,
-                   const LogEvent& wrapper);
+MatchResult matchesSimple(const sp<UidMap>& uidMap, const SimpleAtomMatcher& simpleMatcher,
+                          const LogEvent& wrapper);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/statsd/src/metrics/CountMetricProducer.cpp b/statsd/src/metrics/CountMetricProducer.cpp
index 9a4d922..e7a331b 100644
--- a/statsd/src/metrics/CountMetricProducer.cpp
+++ b/statsd/src/metrics/CountMetricProducer.cpp
@@ -400,7 +400,7 @@
 
 // When a new matched event comes in, we check if event falls into the current
 // bucket. If not, flush the old counter to past buckets and initialize the new bucket.
-void CountMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
+void CountMetricProducer::flushIfNeededLocked(const int64_t eventTimeNs) {
     int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
     if (eventTimeNs < currentBucketEndTimeNs) {
         return;
@@ -416,7 +416,7 @@
          (long long)mCurrentBucketStartTimeNs);
 }
 
-bool CountMetricProducer::countPassesThreshold(const int64_t& count) {
+bool CountMetricProducer::countPassesThreshold(const int64_t count) {
     if (mUploadThreshold == nullopt) {
         return true;
     }
@@ -436,8 +436,8 @@
     }
 }
 
-void CountMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
-                                                   const int64_t& nextBucketStartTimeNs) {
+void CountMetricProducer::flushCurrentBucketLocked(const int64_t eventTimeNs,
+                                                   const int64_t nextBucketStartTimeNs) {
     int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
     CountBucket info;
     info.mBucketStartNs = mCurrentBucketStartTimeNs;
diff --git a/statsd/src/metrics/CountMetricProducer.h b/statsd/src/metrics/CountMetricProducer.h
index 3f67a18..7fa463f 100644
--- a/statsd/src/metrics/CountMetricProducer.h
+++ b/statsd/src/metrics/CountMetricProducer.h
@@ -44,9 +44,9 @@
 class CountMetricProducer : public MetricProducer {
 public:
     CountMetricProducer(
-            const ConfigKey& key, const CountMetric& countMetric, const int conditionIndex,
+            const ConfigKey& key, const CountMetric& countMetric, int conditionIndex,
             const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
-            const uint64_t protoHash, const int64_t timeBaseNs, const int64_t startTimeNs,
+            const uint64_t protoHash, int64_t timeBaseNs, 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 = {},
@@ -81,10 +81,10 @@
     void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
 
     // Internal interface to handle condition change.
-    void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
+    void onConditionChangedLocked(const bool conditionMet, int64_t eventTime) override;
 
     // Internal interface to handle sliced condition change.
-    void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
+    void onSlicedConditionMayChangeLocked(bool overallCondition, int64_t eventTime) override;
 
     // Internal function to calculate the current used bytes.
     size_t byteSizeLocked() const override;
@@ -94,15 +94,14 @@
     void dropDataLocked(const int64_t dropTimeNs) override;
 
     // Util function to flush the old packet.
-    void flushIfNeededLocked(const int64_t& newEventTime) override;
+    void flushIfNeededLocked(int64_t newEventTime) override;
 
-    void flushCurrentBucketLocked(const int64_t& eventTimeNs,
-                                  const int64_t& nextBucketStartTimeNs) override;
+    void flushCurrentBucketLocked(int64_t eventTimeNs, int64_t nextBucketStartTimeNs) override;
 
     void onActiveStateChangedLocked(const int64_t eventTimeNs, const bool isActive) override;
 
     optional<InvalidConfigReason> onConfigUpdatedLocked(
-            const StatsdConfig& config, const int configIndex, const int metricIndex,
+            const StatsdConfig& config, int configIndex, int metricIndex,
             const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
             const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
             const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
@@ -130,7 +129,7 @@
 
     bool hitGuardRailLocked(const MetricDimensionKey& newKey);
 
-    bool countPassesThreshold(const int64_t& count);
+    bool countPassesThreshold(int64_t count);
 
     // Tracks if the dimension guardrail has been hit in the current report.
     bool mDimensionGuardrailHit;
diff --git a/statsd/src/metrics/DurationMetricProducer.cpp b/statsd/src/metrics/DurationMetricProducer.cpp
index a7b886d..9973a9e 100644
--- a/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/statsd/src/metrics/DurationMetricProducer.cpp
@@ -603,7 +603,7 @@
     }
 }
 
-void DurationMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
+void DurationMetricProducer::flushIfNeededLocked(const int64_t eventTimeNs) {
     int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
 
     if (currentBucketEndTimeNs > eventTimeNs) {
@@ -617,8 +617,8 @@
     mCurrentBucketNum += numBucketsForward;
 }
 
-void DurationMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
-                                                      const int64_t& nextBucketStartTimeNs) {
+void DurationMetricProducer::flushCurrentBucketLocked(const int64_t eventTimeNs,
+                                                      const int64_t nextBucketStartTimeNs) {
     const auto [globalConditionTrueNs, globalConditionCorrectionNs] =
             mConditionTimer.newBucketStart(eventTimeNs, nextBucketStartTimeNs);
 
diff --git a/statsd/src/metrics/DurationMetricProducer.h b/statsd/src/metrics/DurationMetricProducer.h
index a366292..2f3d859 100644
--- a/statsd/src/metrics/DurationMetricProducer.h
+++ b/statsd/src/metrics/DurationMetricProducer.h
@@ -39,12 +39,11 @@
 class DurationMetricProducer : public MetricProducer {
 public:
     DurationMetricProducer(
-            const ConfigKey& key, const DurationMetric& durationMetric, const int conditionIndex,
-            const vector<ConditionState>& initialConditionCache, const int whatIndex,
-            const int startIndex, const int stopIndex, const int stopAllIndex, const bool nesting,
+            const ConfigKey& key, const DurationMetric& durationMetric, int conditionIndex,
+            const vector<ConditionState>& initialConditionCache, int whatIndex,
+            const int startIndex, int stopIndex, 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 FieldMatcher& internalDimensions, 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 = {},
@@ -57,7 +56,7 @@
                                          const UpdateStatus& updateStatus,
                                          const int64_t updateTimeNs) override;
 
-    void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker, const int64_t updateTimeNs) override;
+    void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker, int64_t updateTimeNs) override;
 
     void onStateChanged(const int64_t eventTimeNs, const int32_t atomId,
                         const HashableDimensionKey& primaryKey, const FieldValue& oldState,
@@ -77,13 +76,13 @@
 
 private:
     // Initializes true dimensions of the 'what' predicate. Only to be called during initialization.
-    void initTrueDimensions(const int whatIndex, const int64_t startTimeNs);
+    void initTrueDimensions(const int whatIndex, 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 int64_t eventTimeNs,
+                          bool condition, int64_t eventTimeNs,
                           const vector<FieldValue>& eventValues);
 
     void onDumpReportLocked(const int64_t dumpTimeNs,
@@ -96,13 +95,13 @@
     void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
 
     // Internal interface to handle condition change.
-    void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
+    void onConditionChangedLocked(const bool conditionMet, int64_t eventTime) override;
 
     // Internal interface to handle active state change.
     void onActiveStateChangedLocked(const int64_t eventTimeNs, const bool isActive) override;
 
     // Internal interface to handle sliced condition change.
-    void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
+    void onSlicedConditionMayChangeLocked(bool overallCondition, int64_t eventTime) override;
 
     void onSlicedConditionMayChangeInternalLocked(const int64_t eventTimeNs);
 
@@ -116,13 +115,12 @@
     void dropDataLocked(const int64_t dropTimeNs) override;
 
     // Util function to flush the old packet.
-    void flushIfNeededLocked(const int64_t& eventTime);
+    void flushIfNeededLocked(int64_t eventTime);
 
-    void flushCurrentBucketLocked(const int64_t& eventTimeNs,
-                                  const int64_t& nextBucketStartTimeNs) override;
+    void flushCurrentBucketLocked(int64_t eventTimeNs, int64_t nextBucketStartTimeNs) override;
 
     optional<InvalidConfigReason> onConfigUpdatedLocked(
-            const StatsdConfig& config, const int configIndex, const int metricIndex,
+            const StatsdConfig& config, int configIndex, int metricIndex,
             const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
             const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
             const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
@@ -138,7 +136,7 @@
             std::vector<int>& metricsWithActivation) override;
 
     void addAnomalyTrackerLocked(sp<AnomalyTracker>& anomalyTracker,
-                                 const UpdateStatus& updateStatus, const int64_t updateTimeNs);
+                                 const UpdateStatus& updateStatus, int64_t updateTimeNs);
 
     const DurationMetric_AggregationType mAggregationType;
 
diff --git a/statsd/src/metrics/EventMetricProducer.h b/statsd/src/metrics/EventMetricProducer.h
index f27f24b..6e02659 100644
--- a/statsd/src/metrics/EventMetricProducer.h
+++ b/statsd/src/metrics/EventMetricProducer.h
@@ -35,9 +35,9 @@
 class EventMetricProducer : public MetricProducer {
 public:
     EventMetricProducer(
-            const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex,
+            const ConfigKey& key, const EventMetric& eventMetric, int conditionIndex,
             const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
-            const uint64_t protoHash, const int64_t startTimeNs,
+            const uint64_t protoHash, 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 = {},
@@ -68,13 +68,13 @@
     void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
 
     // Internal interface to handle condition change.
-    void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
+    void onConditionChangedLocked(const bool conditionMet, int64_t eventTime) override;
 
     // Internal interface to handle sliced condition change.
-    void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
+    void onSlicedConditionMayChangeLocked(bool overallCondition, int64_t eventTime) override;
 
     optional<InvalidConfigReason> onConfigUpdatedLocked(
-            const StatsdConfig& config, const int configIndex, const int metricIndex,
+            const StatsdConfig& config, int configIndex, int metricIndex,
             const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
             const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
             const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
diff --git a/statsd/src/metrics/GaugeMetricProducer.cpp b/statsd/src/metrics/GaugeMetricProducer.cpp
index 66d8f45..10a02d6 100644
--- a/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -100,7 +100,8 @@
       mDimensionSoftLimit(dimensionSoftLimit),
       mDimensionHardLimit(dimensionHardLimit),
       mGaugeAtomsPerDimensionLimit(metric.max_num_gauge_atoms_per_bucket()),
-      mDimensionGuardrailHit(false) {
+      mDimensionGuardrailHit(false),
+      mSamplingPercentage(metric.sampling_percentage()) {
     mCurrentSlicedBucket = std::make_shared<DimToGaugeAtomsMap>();
     mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>();
     int64_t bucketSizeMills = 0;
@@ -409,9 +410,10 @@
         return;
     }
     for (const auto& data : allData) {
-        if (mEventMatcherWizard->matchLogEvent(*data, mWhatMatcherIndex) ==
-            MatchingState::kMatched) {
-            LogEvent localCopy = *data;
+        const auto [matchResult, transformedEvent] =
+                mEventMatcherWizard->matchLogEvent(*data, mWhatMatcherIndex);
+        if (matchResult == MatchingState::kMatched) {
+            LogEvent localCopy = transformedEvent == nullptr ? *data : *transformedEvent;
             localCopy.setElapsedTimestampNs(timestampNs);
             onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
         }
@@ -501,9 +503,11 @@
         return;
     }
     for (const auto& data : allData) {
-        if (mEventMatcherWizard->matchLogEvent(
-                *data, mWhatMatcherIndex) == MatchingState::kMatched) {
-            onMatchedLogEventLocked(mWhatMatcherIndex, *data);
+        const auto [matchResult, transformedEvent] =
+                mEventMatcherWizard->matchLogEvent(*data, mWhatMatcherIndex);
+        if (matchResult == MatchingState::kMatched) {
+            onMatchedLogEventLocked(mWhatMatcherIndex,
+                                    transformedEvent == nullptr ? *data : *transformedEvent);
         }
     }
 }
@@ -539,6 +543,12 @@
     if (condition == false) {
         return;
     }
+
+    if (mPullTagId == -1 && mSamplingPercentage < 100 &&
+        !shouldKeepRandomSample(mSamplingPercentage)) {
+        return;
+    }
+
     int64_t eventTimeNs = event.GetElapsedTimestampNs();
     if (eventTimeNs < mCurrentBucketStartTimeNs) {
         VLOG("Gauge Skip event due to late arrival: %lld vs %lld", (long long)eventTimeNs,
@@ -617,7 +627,7 @@
 // bucket.
 // if data is pushed, onMatchedLogEvent will only be called through onConditionChanged() inside
 // the GaugeMetricProducer while holding the lock.
-void GaugeMetricProducer::flushIfNeededLocked(const int64_t& eventTimeNs) {
+void GaugeMetricProducer::flushIfNeededLocked(const int64_t eventTimeNs) {
     int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
 
     if (eventTimeNs < currentBucketEndTimeNs) {
@@ -636,8 +646,8 @@
          (long long)mCurrentBucketStartTimeNs);
 }
 
-void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t& eventTimeNs,
-                                                   const int64_t& nextBucketStartTimeNs) {
+void GaugeMetricProducer::flushCurrentBucketLocked(const int64_t eventTimeNs,
+                                                   const int64_t nextBucketStartTimeNs) {
     int64_t fullBucketEndTimeNs = getCurrentBucketEndTimeNs();
     int64_t bucketEndTime = eventTimeNs < fullBucketEndTimeNs ? eventTimeNs : fullBucketEndTimeNs;
 
diff --git a/statsd/src/metrics/GaugeMetricProducer.h b/statsd/src/metrics/GaugeMetricProducer.h
index 51df9e6..7c737a4 100644
--- a/statsd/src/metrics/GaugeMetricProducer.h
+++ b/statsd/src/metrics/GaugeMetricProducer.h
@@ -34,7 +34,7 @@
 namespace statsd {
 
 struct GaugeAtom {
-    GaugeAtom(std::shared_ptr<vector<FieldValue>> fields, int64_t elapsedTimeNs)
+    GaugeAtom(const std::shared_ptr<vector<FieldValue>>& fields, int64_t elapsedTimeNs)
         : mFields(fields), mElapsedTimestampNs(elapsedTimeNs) {
     }
     std::shared_ptr<vector<FieldValue>> mFields;
@@ -60,13 +60,12 @@
 class GaugeMetricProducer : public MetricProducer, public virtual PullDataReceiver {
 public:
     GaugeMetricProducer(
-            const ConfigKey& key, const GaugeMetric& gaugeMetric, const int conditionIndex,
+            const ConfigKey& key, const GaugeMetric& gaugeMetric, int conditionIndex,
             const vector<ConditionState>& initialConditionCache,
             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 int pullTagId, int triggerAtomId, int atomId, const int64_t timeBaseNs,
+            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 = {},
@@ -86,7 +85,7 @@
     };
 
     // GaugeMetric needs to immediately trigger another pull when we create the partial bucket.
-    void notifyAppUpgradeInternalLocked(const int64_t eventTimeNs) override {
+    void notifyAppUpgradeInternalLocked(int64_t eventTimeNs) override {
         flushLocked(eventTimeNs);
         if (mIsPulled && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE && mIsActive) {
             pullAndMatchEventsLocked(eventTimeNs);
@@ -94,7 +93,7 @@
     };
 
     // GaugeMetric needs to immediately trigger another pull when we create the partial bucket.
-    void onStatsdInitCompleted(const int64_t& eventTimeNs) override {
+    void onStatsdInitCompleted(int64_t eventTimeNs) override {
         std::lock_guard<std::mutex> lock(mMutex);
 
         flushLocked(eventTimeNs);
@@ -123,13 +122,13 @@
     void clearPastBucketsLocked(const int64_t dumpTimeNs) override;
 
     // Internal interface to handle condition change.
-    void onConditionChangedLocked(const bool conditionMet, const int64_t eventTime) override;
+    void onConditionChangedLocked(const bool conditionMet, int64_t eventTime) override;
 
     // Internal interface to handle active state change.
     void onActiveStateChangedLocked(const int64_t eventTimeNs, const bool isActive) override;
 
     // Internal interface to handle sliced condition change.
-    void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
+    void onSlicedConditionMayChangeLocked(bool overallCondition, int64_t eventTime) override;
 
     // Internal function to calculate the current used bytes.
     size_t byteSizeLocked() const override;
@@ -139,10 +138,9 @@
     void dropDataLocked(const int64_t dropTimeNs) override;
 
     // Util function to flush the old packet.
-    void flushIfNeededLocked(const int64_t& eventTime) override;
+    void flushIfNeededLocked(int64_t eventTime) override;
 
-    void flushCurrentBucketLocked(const int64_t& eventTimeNs,
-                                  const int64_t& nextBucketStartTimeNs) override;
+    void flushCurrentBucketLocked(int64_t eventTimeNs, int64_t nextBucketStartTimeNs) override;
 
     void prepareFirstBucketLocked() override;
 
@@ -150,7 +148,7 @@
     void pullAndMatchEventsLocked(const int64_t timestampNs);
 
     optional<InvalidConfigReason> onConfigUpdatedLocked(
-            const StatsdConfig& config, const int configIndex, const int metricIndex,
+            const StatsdConfig& config, int configIndex, int metricIndex,
             const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
             const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
             const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
@@ -227,6 +225,8 @@
     // Tracks if the dimension guardrail has been hit in the current report.
     bool mDimensionGuardrailHit;
 
+    const int mSamplingPercentage;
+
     FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithCondition);
     FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsWithSlicedCondition);
     FRIEND_TEST(GaugeMetricProducerTest, TestPulledEventsNoCondition);
diff --git a/statsd/src/metrics/KllMetricProducer.cpp b/statsd/src/metrics/KllMetricProducer.cpp
index 91f22f4..8e50567 100644
--- a/statsd/src/metrics/KllMetricProducer.cpp
+++ b/statsd/src/metrics/KllMetricProducer.cpp
@@ -31,12 +31,9 @@
 using android::util::FIELD_TYPE_INT32;
 using android::util::FIELD_TYPE_MESSAGE;
 using android::util::ProtoOutputStream;
-using std::map;
 using std::nullopt;
 using std::optional;
-using std::shared_ptr;
 using std::string;
-using std::unordered_map;
 using zetasketch::android::AggregatorStateProto;
 
 namespace android {
diff --git a/statsd/src/metrics/MetricProducer.cpp b/statsd/src/metrics/MetricProducer.cpp
index bd69798..55c9adf 100644
--- a/statsd/src/metrics/MetricProducer.cpp
+++ b/statsd/src/metrics/MetricProducer.cpp
@@ -45,9 +45,9 @@
 const int FIELD_ID_ACTIVE_EVENT_ACTIVATION_STATE = 3;
 
 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 uint64_t protoHash,
+        int64_t metricId, const ConfigKey& key, const int64_t timeBaseNs, const int conditionIndex,
+        const vector<ConditionState>& initialConditionCache, 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,
@@ -243,7 +243,7 @@
     if (it == mEventDeactivationMap.end()) {
         return;
     }
-    for (auto activationToCancelIt : it->second)  {
+    for (auto& activationToCancelIt : it->second) {
         activationToCancelIt->state = ActivationState::kNotActive;
     }
 }
@@ -319,7 +319,7 @@
     }
 }
 
-void MetricProducer::queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
+void MetricProducer::queryStateValue(int32_t atomId, const HashableDimensionKey& queryKey,
                                      FieldValue* value) {
     if (!StateManager::getInstance().getStateValue(atomId, queryKey, value)) {
         value->mValue = Value(StateTracker::kStateUnknown);
@@ -329,7 +329,7 @@
     }
 }
 
-void MetricProducer::mapStateValue(const int32_t atomId, FieldValue* value) {
+void MetricProducer::mapStateValue(int32_t atomId, FieldValue* value) {
     // check if there is a state map for this atom
     auto atomIt = mStateGroupMap.find(atomId);
     if (atomIt == mStateGroupMap.end()) {
diff --git a/statsd/src/metrics/MetricProducer.h b/statsd/src/metrics/MetricProducer.h
index 24e761a..133aa32 100644
--- a/statsd/src/metrics/MetricProducer.h
+++ b/statsd/src/metrics/MetricProducer.h
@@ -73,11 +73,12 @@
 };
 
 struct Activation {
-    Activation(const ActivationType& activationType, const int64_t ttlNs)
+    Activation(const ActivationType& activationType, int64_t ttlNs)
         : ttl_ns(ttlNs),
           start_ns(0),
           state(ActivationState::kNotActive),
-          activationType(activationType) {}
+          activationType(activationType) {
+    }
 
     const int64_t ttl_ns;
     int64_t start_ns;
@@ -127,7 +128,7 @@
 // be a no-op.
 class MetricProducer : public virtual RefBase, public virtual StateListener {
 public:
-    MetricProducer(const int64_t& metricId, const ConfigKey& key, const int64_t timeBaseNs,
+    MetricProducer(int64_t metricId, const ConfigKey& key, int64_t timeBaseNs,
                    const int conditionIndex, const vector<ConditionState>& initialConditionCache,
                    const sp<ConditionWizard>& wizard, const uint64_t protoHash,
                    const std::unordered_map<int, std::shared_ptr<Activation>>& eventActivationMap,
@@ -149,7 +150,7 @@
     // This function also updates several maps used by metricsManager.
     // This function clears all anomaly trackers. All anomaly trackers need to be added again.
     optional<InvalidConfigReason> onConfigUpdated(
-            const StatsdConfig& config, const int configIndex, const int metricIndex,
+            const StatsdConfig& config, int configIndex, int metricIndex,
             const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
             const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
             const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
@@ -175,7 +176,7 @@
     /**
      * Force a partial bucket split on app upgrade
      */
-    void notifyAppUpgrade(const int64_t& eventTimeNs) {
+    void notifyAppUpgrade(int64_t eventTimeNs) {
         std::lock_guard<std::mutex> lock(mMutex);
         const bool splitBucket =
                 mSplitBucketForAppUpgrade ? mSplitBucketForAppUpgrade.value() : false;
@@ -185,7 +186,7 @@
         notifyAppUpgradeInternalLocked(eventTimeNs);
     };
 
-    void notifyAppRemoved(const int64_t& eventTimeNs) {
+    void notifyAppRemoved(int64_t eventTimeNs) {
         // Force buckets to split on removal also.
         notifyAppUpgrade(eventTimeNs);
     };
@@ -193,7 +194,7 @@
     /**
      * Force a partial bucket split on boot complete.
      */
-    virtual void onStatsdInitCompleted(const int64_t& eventTimeNs) {
+    virtual void onStatsdInitCompleted(int64_t eventTimeNs) {
         std::lock_guard<std::mutex> lock(mMutex);
         flushLocked(eventTimeNs);
     }
@@ -203,12 +204,12 @@
         onMatchedLogEventLocked(matcherIndex, event);
     }
 
-    void onConditionChanged(const bool condition, const int64_t eventTime) {
+    void onConditionChanged(const bool condition, int64_t eventTime) {
         std::lock_guard<std::mutex> lock(mMutex);
         onConditionChangedLocked(condition, eventTime);
     }
 
-    void onSlicedConditionMayChange(bool overallCondition, const int64_t eventTime) {
+    void onSlicedConditionMayChange(bool overallCondition, int64_t eventTime) {
         std::lock_guard<std::mutex> lock(mMutex);
         onSlicedConditionMayChangeLocked(overallCondition, eventTime);
     }
@@ -236,7 +237,7 @@
     }
 
     virtual optional<InvalidConfigReason> onConfigUpdatedLocked(
-            const StatsdConfig& config, const int configIndex, const int metricIndex,
+            const StatsdConfig& config, int configIndex, int metricIndex,
             const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
             const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
             const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
@@ -307,7 +308,7 @@
     void writeActiveMetricToProtoOutputStream(
             int64_t currentTimeNs, const DumpReportReason reason, ProtoOutputStream* proto);
 
-    virtual void enforceRestrictedDataTtl(sqlite3* db, const int64_t wallClockNs){};
+    virtual void enforceRestrictedDataTtl(sqlite3* db, int64_t wallClockNs){};
 
     virtual bool writeMetricMetadataToProto(metadata::MetricMetadata* metricMetadata) {
         return false;
@@ -364,7 +365,7 @@
     }
 
     /* Adds an AnomalyTracker that has already been created */
-    virtual void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker, const int64_t updateTimeNs) {
+    virtual void addAnomalyTracker(sp<AnomalyTracker>& anomalyTracker, int64_t updateTimeNs) {
         std::lock_guard<std::mutex> lock(mMutex);
         mAnomalyTrackers.push_back(anomalyTracker);
     }
@@ -379,7 +380,7 @@
     /**
      * Flushes the current bucket if the eventTime is after the current bucket's end time.
      */
-    virtual void flushIfNeededLocked(const int64_t& eventTime){};
+    virtual void flushIfNeededLocked(int64_t eventTime){};
 
     /**
      * For metrics that aggregate (ie, every metric producer except for EventMetricProducer),
@@ -391,13 +392,12 @@
      * flushIfNeededLocked or flushLocked or the app upgrade handler; the caller MUST update the
      * bucket timestamp and bucket number as needed.
      */
-    virtual void flushCurrentBucketLocked(const int64_t& eventTimeNs,
-                                          const int64_t& nextBucketStartTimeNs) {};
+    virtual void flushCurrentBucketLocked(int64_t eventTimeNs, int64_t nextBucketStartTimeNs){};
 
     /**
      * Flushes all the data including the current partial bucket.
      */
-    void flushLocked(const int64_t& eventTimeNs) {
+    void flushLocked(int64_t eventTimeNs) {
         flushIfNeededLocked(eventTimeNs);
         flushCurrentBucketLocked(eventTimeNs, eventTimeNs);
     };
@@ -428,7 +428,7 @@
 
     // Consume the parsed stats log entry that already matched the "what" of the metric.
     virtual void onMatchedLogEventLocked(const size_t matcherIndex, const LogEvent& event);
-    virtual void onConditionChangedLocked(const bool condition, const int64_t eventTime) = 0;
+    virtual void onConditionChangedLocked(const bool condition, int64_t eventTime) = 0;
     virtual void onSlicedConditionMayChangeLocked(bool overallCondition,
                                                   const int64_t eventTime) = 0;
     virtual void onDumpReportLocked(const int64_t dumpTimeNs,
@@ -470,13 +470,12 @@
 
     // Query StateManager for original state value using the queryKey.
     // The field and value are output.
-    void queryStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
-                         FieldValue* value);
+    void queryStateValue(int32_t atomId, const HashableDimensionKey& queryKey, FieldValue* value);
 
     // If a state map exists for the given atom, replace the original state
     // value with the group id mapped to the value.
     // If no state map exists, keep the original state value.
-    void mapStateValue(const int32_t atomId, FieldValue* value);
+    void mapStateValue(int32_t atomId, FieldValue* value);
 
     // Returns a HashableDimensionKey with unknown state value for each state
     // atom.
diff --git a/statsd/src/metrics/MetricsManager.cpp b/statsd/src/metrics/MetricsManager.cpp
index e7e3561..8a822d7 100644
--- a/statsd/src/metrics/MetricsManager.cpp
+++ b/statsd/src/metrics/MetricsManager.cpp
@@ -44,6 +44,7 @@
 
 using std::set;
 using std::string;
+using std::unique_ptr;
 using std::vector;
 
 namespace android {
@@ -364,7 +365,7 @@
     return !mInvalidConfigReason.has_value();
 }
 
-void MetricsManager::notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
+void MetricsManager::notifyAppUpgrade(const int64_t eventTimeNs, const string& apk, const int uid,
                                       const int64_t version) {
     // Inform all metric producers.
     for (const auto& it : mAllMetricProducers) {
@@ -385,8 +386,7 @@
     }
 }
 
-void MetricsManager::notifyAppRemoved(const int64_t& eventTimeNs, const string& apk,
-                                      const int uid) {
+void MetricsManager::notifyAppRemoved(const int64_t eventTimeNs, const string& apk, const int uid) {
     // Inform all metric producers.
     for (const auto& it : mAllMetricProducers) {
         it->notifyAppRemoved(eventTimeNs);
@@ -406,7 +406,7 @@
     }
 }
 
-void MetricsManager::onUidMapReceived(const int64_t& eventTimeNs) {
+void MetricsManager::onUidMapReceived(const int64_t eventTimeNs) {
     // Purposefully don't inform metric producers on a new snapshot
     // because we don't need to flush partial buckets.
     // This occurs if a new user is added/removed or statsd crashes.
@@ -418,7 +418,7 @@
     initAllowedLogSources();
 }
 
-void MetricsManager::onStatsdInitCompleted(const int64_t& eventTimeNs) {
+void MetricsManager::onStatsdInitCompleted(const int64_t eventTimeNs) {
     // Inform all metric producers.
     for (const auto& it : mAllMetricProducers) {
         it->onStatsdInitCompleted(eventTimeNs);
@@ -616,10 +616,12 @@
 
     vector<MatchingState> matcherCache(mAllAtomMatchingTrackers.size(),
                                        MatchingState::kNotComputed);
+    vector<shared_ptr<LogEvent>> matcherTransformations(matcherCache.size(), nullptr);
 
     for (const auto& matcherIndex : matchersIt->second) {
         mAllAtomMatchingTrackers[matcherIndex]->onLogEvent(event, matcherIndex,
-                                                           mAllAtomMatchingTrackers, matcherCache);
+                                                           mAllAtomMatchingTrackers, matcherCache,
+                                                           matcherTransformations);
     }
 
     // Set of metrics that received an activation cancellation.
@@ -661,12 +663,15 @@
 
     // A bitmap to see which ConditionTracker needs to be re-evaluated.
     vector<uint8_t> conditionToBeEvaluated(mAllConditionTrackers.size(), false);
+    vector<shared_ptr<LogEvent>> conditionToTransformedLogEvents(mAllConditionTrackers.size(),
+                                                                 nullptr);
 
-    for (const auto& pair : mTrackerToConditionMap) {
-        if (matcherCache[pair.first] == MatchingState::kMatched) {
-            const auto& conditionList = pair.second;
+    for (const auto& [matcherIndex, conditionList] : mTrackerToConditionMap) {
+        if (matcherCache[matcherIndex] == MatchingState::kMatched) {
             for (const int conditionIndex : conditionList) {
                 conditionToBeEvaluated[conditionIndex] = true;
+                conditionToTransformedLogEvents[conditionIndex] =
+                        matcherTransformations[matcherIndex];
             }
         }
     }
@@ -676,34 +681,38 @@
     // A bitmap to track if a condition has changed value.
     vector<uint8_t> changedCache(mAllConditionTrackers.size(), false);
     for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
-        if (conditionToBeEvaluated[i] == false) {
+        if (!conditionToBeEvaluated[i]) {
             continue;
         }
         sp<ConditionTracker>& condition = mAllConditionTrackers[i];
-        condition->evaluateCondition(event, matcherCache, mAllConditionTrackers, conditionCache,
-                                     changedCache);
+        const LogEvent& conditionEvent = conditionToTransformedLogEvents[i] == nullptr
+                                                 ? event
+                                                 : *conditionToTransformedLogEvents[i];
+        condition->evaluateCondition(conditionEvent, matcherCache, mAllConditionTrackers,
+                                     conditionCache, changedCache);
     }
 
     for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
-        if (changedCache[i] == false) {
+        if (!changedCache[i]) {
             continue;
         }
-        auto pair = mConditionToMetricMap.find(i);
-        if (pair != mConditionToMetricMap.end()) {
-            auto& metricList = pair->second;
-            for (auto metricIndex : metricList) {
-                // Metric cares about non sliced condition, and it's changed.
-                // Push the new condition to it directly.
-                if (!mAllMetricProducers[metricIndex]->isConditionSliced()) {
-                    mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i],
-                                                                         eventTimeNs);
-                    // Metric cares about sliced conditions, and it may have changed. Send
-                    // notification, and the metric can query the sliced conditions that are
-                    // interesting to it.
-                } else {
-                    mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(conditionCache[i],
-                                                                                 eventTimeNs);
-                }
+        auto it = mConditionToMetricMap.find(i);
+        if (it == mConditionToMetricMap.end()) {
+            continue;
+        }
+        auto& metricList = it->second;
+        for (auto metricIndex : metricList) {
+            // Metric cares about non sliced condition, and it's changed.
+            // Push the new condition to it directly.
+            if (!mAllMetricProducers[metricIndex]->isConditionSliced()) {
+                mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i],
+                                                                     eventTimeNs);
+                // Metric cares about sliced conditions, and it may have changed. Send
+                // notification, and the metric can query the sliced conditions that are
+                // interesting to it.
+            } else {
+                mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(conditionCache[i],
+                                                                             eventTimeNs);
             }
         }
     }
@@ -712,20 +721,23 @@
         if (matcherCache[i] == MatchingState::kMatched) {
             StatsdStats::getInstance().noteMatcherMatched(mConfigKey,
                                                           mAllAtomMatchingTrackers[i]->getId());
-            auto pair = mTrackerToMetricMap.find(i);
-            if (pair != mTrackerToMetricMap.end()) {
-                auto& metricList = pair->second;
-                for (const int metricIndex : metricList) {
-                    // pushed metrics are never scheduled pulls
-                    mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, event);
-                }
+            auto it = mTrackerToMetricMap.find(i);
+            if (it == mTrackerToMetricMap.end()) {
+                continue;
+            }
+            auto& metricList = it->second;
+            const LogEvent& metricEvent =
+                    matcherTransformations[i] == nullptr ? event : *matcherTransformations[i];
+            for (const int metricIndex : metricList) {
+                // pushed metrics are never scheduled pulls
+                mAllMetricProducers[metricIndex]->onMatchedLogEvent(i, metricEvent);
             }
         }
     }
 }
 
 void MetricsManager::onAnomalyAlarmFired(
-        const int64_t& timestampNs,
+        const int64_t timestampNs,
         unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
     for (const auto& itr : mAllAnomalyTrackers) {
         itr->informAlarmsFired(timestampNs, alarmSet);
@@ -733,7 +745,7 @@
 }
 
 void MetricsManager::onPeriodicAlarmFired(
-        const int64_t& timestampNs,
+        const int64_t timestampNs,
         unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet) {
     for (const auto& itr : mAllPeriodicAlarmTrackers) {
         itr->informAlarmsFired(timestampNs, alarmSet);
diff --git a/statsd/src/metrics/MetricsManager.h b/statsd/src/metrics/MetricsManager.h
index 4674a85..4721075 100644
--- a/statsd/src/metrics/MetricsManager.h
+++ b/statsd/src/metrics/MetricsManager.h
@@ -39,7 +39,7 @@
 // A MetricsManager is responsible for managing metrics from one single config source.
 class MetricsManager : public virtual RefBase, public virtual PullUidProvider {
 public:
-    MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, const int64_t timeBaseNs,
+    MetricsManager(const ConfigKey& configKey, const StatsdConfig& config, int64_t timeBaseNs,
                    const int64_t currentTimeNs, const sp<UidMap>& uidMap,
                    const sp<StatsPullerManager>& pullerManager,
                    const sp<AlarmMonitor>& anomalyAlarmMonitor,
@@ -47,8 +47,8 @@
 
     virtual ~MetricsManager();
 
-    bool updateConfig(const StatsdConfig& config, const int64_t timeBaseNs,
-                      const int64_t currentTimeNs, const sp<AlarmMonitor>& anomalyAlarmMonitor,
+    bool updateConfig(const StatsdConfig& config, int64_t timeBaseNs, const int64_t currentTimeNs,
+                      const sp<AlarmMonitor>& anomalyAlarmMonitor,
                       const sp<AlarmMonitor>& periodicAlarmMonitor);
 
     // Return whether the configuration is valid.
@@ -61,21 +61,20 @@
     virtual void onLogEvent(const LogEvent& event);
 
     void onAnomalyAlarmFired(
-        const int64_t& timestampNs,
-        unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
+            int64_t timestampNs,
+            unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
 
     void onPeriodicAlarmFired(
-        const int64_t& timestampNs,
-        unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
+            int64_t timestampNs,
+            unordered_set<sp<const InternalAlarm>, SpHash<InternalAlarm>>& alarmSet);
 
-    void notifyAppUpgrade(const int64_t& eventTimeNs, const string& apk, const int uid,
-                          const int64_t version);
+    void notifyAppUpgrade(int64_t eventTimeNs, const string& apk, int uid, int64_t version);
 
-    void notifyAppRemoved(const int64_t& eventTimeNs, const string& apk, const int uid);
+    void notifyAppRemoved(int64_t eventTimeNs, const string& apk, int uid);
 
-    void onUidMapReceived(const int64_t& eventTimeNs);
+    void onUidMapReceived(int64_t eventTimeNs);
 
-    void onStatsdInitCompleted(const int64_t& elapsedTimeNs);
+    void onStatsdInitCompleted(int64_t elapsedTimeNs);
 
     void init();
 
@@ -133,7 +132,7 @@
 
     virtual void dropData(const int64_t dropTimeNs);
 
-    virtual void onDumpReport(const int64_t dumpTimeNs, const int64_t wallClockNs,
+    virtual void onDumpReport(const int64_t dumpTimeNs, int64_t wallClockNs,
                               const bool include_current_partial_bucket, const bool erase_data,
                               const DumpLatency dumpLatency, std::set<string>* str_set,
                               android::util::ProtoOutputStream* protoOutput);
@@ -176,7 +175,7 @@
 
     void enforceRestrictedDataTtls(const int64_t wallClockNs);
 
-    bool validateRestrictedMetricsDelegate(const int32_t callingUid);
+    bool validateRestrictedMetricsDelegate(int32_t callingUid);
 
     virtual void flushRestrictedData();
 
diff --git a/statsd/src/metrics/NumericValueMetricProducer.cpp b/statsd/src/metrics/NumericValueMetricProducer.cpp
index bc17209..4024cce 100644
--- a/statsd/src/metrics/NumericValueMetricProducer.cpp
+++ b/statsd/src/metrics/NumericValueMetricProducer.cpp
@@ -34,7 +34,6 @@
 using android::util::FIELD_TYPE_MESSAGE;
 using android::util::FIELD_TYPE_STRING;
 using android::util::ProtoOutputStream;
-using std::map;
 using std::optional;
 using std::shared_ptr;
 using std::string;
@@ -267,15 +266,17 @@
         // before calculating the diff between sums of consecutive pulls.
         std::unordered_map<HashableDimensionKey, pair<LogEvent, vector<int>>> aggregateEvents;
         for (const auto& data : allData) {
-            if (mEventMatcherWizard->matchLogEvent(*data, mWhatMatcherIndex) !=
-                MatchingState::kMatched) {
+            const auto [matchResult, transformedEvent] =
+                    mEventMatcherWizard->matchLogEvent(*data, mWhatMatcherIndex);
+            if (matchResult != MatchingState::kMatched) {
                 continue;
             }
 
             // Get dimensions_in_what key and value indices.
             HashableDimensionKey dimensionsInWhat;
             vector<int> valueIndices(mFieldMatchers.size(), -1);
-            if (!filterValues(mDimensionsInWhat, mFieldMatchers, data->getValues(),
+            const LogEvent& eventRef = transformedEvent == nullptr ? *data : *transformedEvent;
+            if (!filterValues(mDimensionsInWhat, mFieldMatchers, eventRef.getValues(),
                               dimensionsInWhat, valueIndices)) {
                 StatsdStats::getInstance().noteBadValueType(mMetricId);
             }
@@ -285,9 +286,9 @@
             if (it == aggregateEvents.end()) {
                 aggregateEvents.emplace(std::piecewise_construct,
                                         std::forward_as_tuple(dimensionsInWhat),
-                                        std::forward_as_tuple(*data, valueIndices));
+                                        std::forward_as_tuple(eventRef, valueIndices));
             } else {
-                combineValueFields(it->second, *data, valueIndices);
+                combineValueFields(it->second, eventRef, valueIndices);
             }
         }
 
@@ -297,9 +298,10 @@
         }
     } else {
         for (const auto& data : allData) {
-            if (mEventMatcherWizard->matchLogEvent(*data, mWhatMatcherIndex) ==
-                MatchingState::kMatched) {
-                LogEvent localCopy = *data;
+            const auto [matchResult, transformedEvent] =
+                    mEventMatcherWizard->matchLogEvent(*data, mWhatMatcherIndex);
+            if (matchResult == MatchingState::kMatched) {
+                LogEvent localCopy = transformedEvent == nullptr ? *data : *transformedEvent;
                 localCopy.setElapsedTimestampNs(eventElapsedTimeNs);
                 onMatchedLogEventLocked(mWhatMatcherIndex, localCopy);
             }
diff --git a/statsd/src/metrics/NumericValueMetricProducer.h b/statsd/src/metrics/NumericValueMetricProducer.h
index 52eab1a..775fc1a 100644
--- a/statsd/src/metrics/NumericValueMetricProducer.h
+++ b/statsd/src/metrics/NumericValueMetricProducer.h
@@ -118,7 +118,7 @@
     bool hitFullBucketGuardRailLocked(const MetricDimensionKey& newKey);
 
     inline bool canSkipLogEventLocked(
-            const MetricDimensionKey& eventKey, const bool condition, const int64_t eventTimeNs,
+            const MetricDimensionKey& eventKey, const bool condition, int64_t eventTimeNs,
             const map<int, HashableDimensionKey>& statePrimaryKeys) const override {
         // For pushed metrics, can only skip if condition is false.
         // For pulled metrics, can only skip if metric is not diffed and condition is false or
diff --git a/statsd/src/metrics/RestrictedEventMetricProducer.h b/statsd/src/metrics/RestrictedEventMetricProducer.h
index 74b0b80..1f68387 100644
--- a/statsd/src/metrics/RestrictedEventMetricProducer.h
+++ b/statsd/src/metrics/RestrictedEventMetricProducer.h
@@ -15,7 +15,7 @@
     RestrictedEventMetricProducer(
             const ConfigKey& key, const EventMetric& eventMetric, const int conditionIndex,
             const vector<ConditionState>& initialConditionCache, const sp<ConditionWizard>& wizard,
-            const uint64_t protoHash, const int64_t startTimeNs,
+            const uint64_t protoHash, 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 = {},
@@ -24,7 +24,7 @@
 
     void onMetricRemove() override;
 
-    void enforceRestrictedDataTtl(sqlite3* db, const int64_t wallClockNs);
+    void enforceRestrictedDataTtl(sqlite3* db, int64_t wallClockNs);
 
     void flushRestrictedData() override;
 
diff --git a/statsd/src/metrics/ValueMetricProducer.cpp b/statsd/src/metrics/ValueMetricProducer.cpp
index c61e123..732cccc 100644
--- a/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/statsd/src/metrics/ValueMetricProducer.cpp
@@ -72,7 +72,7 @@
 
 template <typename AggregatedValue, typename DimExtras>
 ValueMetricProducer<AggregatedValue, DimExtras>::ValueMetricProducer(
-        const int64_t& metricId, const ConfigKey& key, const uint64_t protoHash,
+        const int64_t metricId, const ConfigKey& key, const uint64_t protoHash,
         const PullOptions& pullOptions, const BucketOptions& bucketOptions,
         const WhatOptions& whatOptions, const ConditionOptions& conditionOptions,
         const StateOptions& stateOptions, const ActivationOptions& activationOptions,
@@ -154,7 +154,7 @@
 
 template <typename AggregatedValue, typename DimExtras>
 void ValueMetricProducer<AggregatedValue, DimExtras>::onStatsdInitCompleted(
-        const int64_t& eventTimeNs) {
+        const int64_t eventTimeNs) {
     lock_guard<mutex> lock(mMutex);
 
     if (isPulled() && mCondition == ConditionState::kTrue && mIsActive) {
@@ -717,7 +717,7 @@
 // if mCondition and mIsActive are true!
 template <typename AggregatedValue, typename DimExtras>
 void ValueMetricProducer<AggregatedValue, DimExtras>::flushIfNeededLocked(
-        const int64_t& eventTimeNs) {
+        const int64_t eventTimeNs) {
     const int64_t currentBucketEndTimeNs = getCurrentBucketEndTimeNs();
     if (eventTimeNs < currentBucketEndTimeNs) {
         VLOG("eventTime is %lld, less than current bucket end time %lld", (long long)eventTimeNs,
@@ -742,7 +742,7 @@
 
 template <typename AggregatedValue, typename DimExtras>
 void ValueMetricProducer<AggregatedValue, DimExtras>::flushCurrentBucketLocked(
-        const int64_t& eventTimeNs, const int64_t& nextBucketStartTimeNs) {
+        const int64_t eventTimeNs, const int64_t nextBucketStartTimeNs) {
     if (mCondition == ConditionState::kUnknown) {
         StatsdStats::getInstance().noteBucketUnknownCondition(mMetricId);
         invalidateCurrentBucket(eventTimeNs, BucketDropReason::CONDITION_UNKNOWN);
diff --git a/statsd/src/metrics/ValueMetricProducer.h b/statsd/src/metrics/ValueMetricProducer.h
index 6bfc95d..92541029 100644
--- a/statsd/src/metrics/ValueMetricProducer.h
+++ b/statsd/src/metrics/ValueMetricProducer.h
@@ -127,13 +127,13 @@
     }
 
     // ValueMetric needs special logic if it's a pulled atom.
-    void onStatsdInitCompleted(const int64_t& eventTimeNs) override;
+    void onStatsdInitCompleted(int64_t eventTimeNs) override;
 
     void onStateChanged(int64_t eventTimeNs, int32_t atomId, const HashableDimensionKey& primaryKey,
                         const FieldValue& oldState, const FieldValue& newState) override;
 
 protected:
-    ValueMetricProducer(const int64_t& metricId, const ConfigKey& key, const uint64_t protoHash,
+    ValueMetricProducer(int64_t metricId, const ConfigKey& key, uint64_t protoHash,
                         const PullOptions& pullOptions, const BucketOptions& bucketOptions,
                         const WhatOptions& whatOptions, const ConditionOptions& conditionOptions,
                         const StateOptions& stateOptions,
@@ -178,7 +178,7 @@
     }
 
     // ValueMetricProducer internal interface to handle condition change.
-    void onConditionChangedLocked(const bool condition, const int64_t eventTimeNs) override;
+    void onConditionChangedLocked(const bool condition, int64_t eventTimeNs) override;
 
     // Only called when mIsActive, the event is NOT too late, and after pulling.
     virtual void onConditionChangedInternalLocked(const ConditionState oldCondition,
@@ -187,7 +187,7 @@
     }
 
     // Internal interface to handle sliced condition change.
-    void onSlicedConditionMayChangeLocked(bool overallCondition, const int64_t eventTime) override;
+    void onSlicedConditionMayChangeLocked(bool overallCondition, int64_t eventTime) override;
 
     void dumpStatesLocked(int out, bool verbose) const override;
 
@@ -195,12 +195,11 @@
 
     // For pulled metrics, this method should only be called if a pull has been done. Else we will
     // not have complete data for the bucket.
-    void flushIfNeededLocked(const int64_t& eventTime) override;
+    void flushIfNeededLocked(int64_t eventTime) override;
 
     // For pulled metrics, this method should only be called if a pulled has been done. Else we will
     // not have complete data for the bucket.
-    void flushCurrentBucketLocked(const int64_t& eventTimeNs,
-                                  const int64_t& nextBucketStartTimeNs) override;
+    void flushCurrentBucketLocked(int64_t eventTimeNs, int64_t nextBucketStartTimeNs) override;
 
     void dropDataLocked(const int64_t dropTimeNs) override;
 
@@ -216,7 +215,7 @@
     void skipCurrentBucket(const int64_t dropTimeNs, const BucketDropReason reason);
 
     optional<InvalidConfigReason> onConfigUpdatedLocked(
-            const StatsdConfig& config, const int configIndex, const int metricIndex,
+            const StatsdConfig& config, int configIndex, int metricIndex,
             const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
             const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
             const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
@@ -332,7 +331,7 @@
     virtual PastBucket<AggregatedValue> buildPartialBucket(int64_t bucketEndTime,
                                                            std::vector<Interval>& intervals) = 0;
 
-    virtual void closeCurrentBucket(const int64_t eventTimeNs, const int64_t nextBucketStartTimeNs);
+    virtual void closeCurrentBucket(const int64_t eventTimeNs, int64_t nextBucketStartTimeNs);
 
     virtual void initNextSlicedBucket(int64_t nextBucketStartTimeNs);
 
diff --git a/statsd/src/metrics/duration_helper/DurationTracker.h b/statsd/src/metrics/duration_helper/DurationTracker.h
index 5f2e643..f86197a 100644
--- a/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -73,8 +73,8 @@
 
 class DurationTracker {
 public:
-    DurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
-                    sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
+    DurationTracker(const ConfigKey& key, const int64_t id, const MetricDimensionKey& eventKey,
+                    const 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)
@@ -95,21 +95,21 @@
 
     virtual ~DurationTracker(){};
 
-    void onConfigUpdated(const sp<ConditionWizard>& wizard, const int conditionTrackerIndex) {
+    void onConfigUpdated(const sp<ConditionWizard>& wizard, int conditionTrackerIndex) {
         sp<ConditionWizard> tmpWizard = mWizard;
         mWizard = wizard;
         mConditionTrackerIndex = conditionTrackerIndex;
         mAnomalyTrackers.clear();
     };
 
-    virtual void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
+    virtual void noteStart(const HashableDimensionKey& key, bool condition, int64_t eventTime,
                            const ConditionKey& conditionKey, size_t dimensionHardLimit) = 0;
-    virtual void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
+    virtual void noteStop(const HashableDimensionKey& key, int64_t eventTime,
                           const bool stopAll) = 0;
     virtual void noteStopAll(const int64_t eventTime) = 0;
 
     virtual void onSlicedConditionMayChange(const int64_t timestamp) = 0;
-    virtual void onConditionChanged(bool condition, const int64_t timestamp) = 0;
+    virtual void onConditionChanged(bool condition, int64_t timestamp) = 0;
 
     virtual void onStateChanged(const int64_t timestamp, const int32_t atomId,
                                 const FieldValue& newState) = 0;
@@ -123,7 +123,7 @@
     // Should only be called during an app upgrade or from this tracker's flushIfNeeded. If from
     // an app upgrade, we assume that we're trying to form a partial bucket.
     virtual bool flushCurrentBucket(
-            const int64_t& eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
+            int64_t eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
             const int64_t globalConditionTrueNs,
             std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0;
 
@@ -138,7 +138,7 @@
     virtual int64_t getCurrentStateKeyFullBucketDuration() const = 0;
 
     // Replace old value with new value for the given state atom.
-    virtual void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState) = 0;
+    virtual void updateCurrentStateKey(int32_t atomId, const FieldValue& newState) = 0;
 
     virtual bool hasAccumulatedDuration() const = 0;
 
@@ -188,8 +188,8 @@
         }
     }
 
-    void addPastBucketToAnomalyTrackers(const MetricDimensionKey eventKey,
-                                        const int64_t& bucketValue, const int64_t& bucketNum) {
+    void addPastBucketToAnomalyTrackers(const MetricDimensionKey& eventKey, int64_t bucketValue,
+                                        int64_t bucketNum) {
         for (auto& anomalyTracker : mAnomalyTrackers) {
             if (anomalyTracker != nullptr) {
                 anomalyTracker->addPastBucket(eventKey, bucketValue, bucketNum);
@@ -197,8 +197,8 @@
         }
     }
 
-    void detectAndDeclareAnomaly(const int64_t& timestamp, const int64_t& currBucketNum,
-                                 const int64_t& currentBucketValue) {
+    void detectAndDeclareAnomaly(int64_t timestamp, int64_t currBucketNum,
+                                 int64_t currentBucketValue) {
         for (auto& anomalyTracker : mAnomalyTrackers) {
             if (anomalyTracker != nullptr) {
                 anomalyTracker->detectAndDeclareAnomaly(timestamp, currBucketNum, mTrackerId,
diff --git a/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index a263bc5..8a253e2 100644
--- a/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -24,12 +24,12 @@
 namespace os {
 namespace statsd {
 
-MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id,
+MaxDurationTracker::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 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<AnomalyTracker>>& anomalyTrackers)
     : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
                       currentBucketNum, startTimeNs, bucketSizeNs, conditionSliced, fullLink,
@@ -175,7 +175,7 @@
 }
 
 bool MaxDurationTracker::flushCurrentBucket(
-        const int64_t& eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
+        const int64_t eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
         const int64_t globalConditionTrueNs,
         std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) {
     VLOG("MaxDurationTracker flushing.....");
diff --git a/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index cff3fb6..2769232 100644
--- a/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -28,30 +28,29 @@
 // they stop or bucket expires.
 class MaxDurationTracker : public DurationTracker {
 public:
-    MaxDurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
-                       sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
+    MaxDurationTracker(const ConfigKey& key, const int64_t id, const MetricDimensionKey& eventKey,
+                       const 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;
 
-    void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
+    void noteStart(const HashableDimensionKey& key, bool condition, int64_t eventTime,
                    const ConditionKey& conditionKey, size_t dimensionHardLimit) override;
-    void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
-                  const bool stopAll) override;
+    void noteStop(const HashableDimensionKey& key, int64_t eventTime, const bool stopAll) override;
     void noteStopAll(const int64_t eventTime) override;
 
     bool flushIfNeeded(
             int64_t timestampNs, const optional<UploadThreshold>& uploadThreshold,
             std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
     bool flushCurrentBucket(
-            const int64_t& eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
+            int64_t eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
             const int64_t globalConditionTrueNs,
             std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>*) override;
 
     void onSlicedConditionMayChange(const int64_t timestamp) override;
-    void onConditionChanged(bool condition, const int64_t timestamp) override;
+    void onConditionChanged(bool condition, int64_t timestamp) override;
 
     void onStateChanged(const int64_t timestamp, const int32_t atomId,
                         const FieldValue& newState) override;
@@ -64,7 +63,7 @@
 
     int64_t getCurrentStateKeyFullBucketDuration() const override;
 
-    void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState);
+    void updateCurrentStateKey(int32_t atomId, const FieldValue& newState);
 
     bool hasAccumulatedDuration() const override;
 
diff --git a/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index c1c66f8..3fab0e8 100644
--- a/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -24,11 +24,14 @@
 
 using std::pair;
 
-OringDurationTracker::OringDurationTracker(
-        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<AnomalyTracker>>& anomalyTrackers)
+OringDurationTracker::OringDurationTracker(const ConfigKey& key, const int64_t id,
+                                           const MetricDimensionKey& eventKey,
+                                           const 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<AnomalyTracker>>& anomalyTrackers)
     : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
                       currentBucketNum, startTimeNs, bucketSizeNs, conditionSliced, fullLink,
                       anomalyTrackers),
@@ -138,7 +141,7 @@
 }
 
 bool OringDurationTracker::flushCurrentBucket(
-        const int64_t& eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
+        const int64_t eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
         const int64_t globalConditionTrueNs,
         std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) {
     VLOG("OringDurationTracker Flushing.............");
diff --git a/statsd/src/metrics/duration_helper/OringDurationTracker.h b/statsd/src/metrics/duration_helper/OringDurationTracker.h
index da9b7e3..a50862b 100644
--- a/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -26,29 +26,27 @@
 // Tracks the "Or'd" duration -- if 2 durations are overlapping, they won't be double counted.
 class OringDurationTracker : public DurationTracker {
 public:
-    OringDurationTracker(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<AnomalyTracker>>& anomalyTrackers);
+    OringDurationTracker(const ConfigKey& key, const int64_t id, const MetricDimensionKey& eventKey,
+                         const 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);
 
     OringDurationTracker(const OringDurationTracker& tracker) = default;
 
-    void noteStart(const HashableDimensionKey& key, bool condition, const int64_t eventTime,
+    void noteStart(const HashableDimensionKey& key, bool condition, int64_t eventTime,
                    const ConditionKey& conditionKey, size_t dimensionHardLimit) override;
-    void noteStop(const HashableDimensionKey& key, const int64_t eventTime,
-                  const bool stopAll) override;
+    void noteStop(const HashableDimensionKey& key, int64_t eventTime, const bool stopAll) override;
     void noteStopAll(const int64_t eventTime) override;
 
     void onSlicedConditionMayChange(const int64_t timestamp) override;
-    void onConditionChanged(bool condition, const int64_t timestamp) override;
+    void onConditionChanged(bool condition, int64_t timestamp) override;
 
     void onStateChanged(const int64_t timestamp, const int32_t atomId,
                         const FieldValue& newState) override;
 
     bool flushCurrentBucket(
-            const int64_t& eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
+            int64_t eventTimeNs, const optional<UploadThreshold>& uploadThreshold,
             const int64_t globalConditionTrueNs,
             std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
     bool flushIfNeeded(
@@ -63,7 +61,7 @@
 
     int64_t getCurrentStateKeyFullBucketDuration() const override;
 
-    void updateCurrentStateKey(const int32_t atomId, const FieldValue& newState);
+    void updateCurrentStateKey(int32_t atomId, const FieldValue& newState);
 
     bool hasAccumulatedDuration() const override;
 
diff --git a/statsd/src/metrics/parsing_utils/config_update_utils.cpp b/statsd/src/metrics/parsing_utils/config_update_utils.cpp
index 85495e4..18d7a32 100644
--- a/statsd/src/metrics/parsing_utils/config_update_utils.cpp
+++ b/statsd/src/metrics/parsing_utils/config_update_utils.cpp
@@ -203,8 +203,9 @@
     std::fill(cycleTracker.begin(), cycleTracker.end(), false);
     for (size_t matcherIndex = 0; matcherIndex < newAtomMatchingTrackers.size(); matcherIndex++) {
         auto& matcher = newAtomMatchingTrackers[matcherIndex];
-        invalidConfigReason = matcher->init(matcherIndex, matcherProtos, newAtomMatchingTrackers,
-                                            newAtomMatchingTrackerMap, cycleTracker);
+        const auto [invalidConfigReason, _] =
+                matcher->init(matcherIndex, matcherProtos, newAtomMatchingTrackers,
+                              newAtomMatchingTrackerMap, cycleTracker);
         if (invalidConfigReason.has_value()) {
             return invalidConfigReason;
         }
@@ -519,7 +520,7 @@
     if (invalidConfigReason.has_value()) {
         return invalidConfigReason;
     }
-    const sp<MetricProducer> oldMetricProducer = oldMetricProducers[oldMetricProducerIt->second];
+    const sp<MetricProducer>& oldMetricProducer = oldMetricProducers[oldMetricProducerIt->second];
     if (oldMetricProducer->getMetricType() != metricType ||
         oldMetricProducer->getProtoHash() != metricHash) {
         updateStatus = UPDATE_REPLACE;
diff --git a/statsd/src/metrics/parsing_utils/config_update_utils.h b/statsd/src/metrics/parsing_utils/config_update_utils.h
index 4ef8805..8b6c784 100644
--- a/statsd/src/metrics/parsing_utils/config_update_utils.h
+++ b/statsd/src/metrics/parsing_utils/config_update_utils.h
@@ -46,7 +46,7 @@
 // [cycleTracker]: intermediate param used during recursion.
 // Returns nullopt if successful and InvalidConfigReason if not.
 optional<InvalidConfigReason> determineMatcherUpdateStatus(
-        const StatsdConfig& config, const int matcherIdx,
+        const StatsdConfig& config, int matcherIdx,
         const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
         const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
         const std::unordered_map<int64_t, int>& newAtomMatchingTrackerMap,
@@ -86,7 +86,7 @@
 // [cycleTracker]: intermediate param used during recursion.
 // Returns nullopt if successful and InvalidConfigReason if not.
 optional<InvalidConfigReason> determineConditionUpdateStatus(
-        const StatsdConfig& config, const int conditionIdx,
+        const StatsdConfig& config, int conditionIdx,
         const std::unordered_map<int64_t, int>& oldConditionTrackerMap,
         const std::vector<sp<ConditionTracker>>& oldConditionTrackers,
         const std::unordered_map<int64_t, int>& newConditionTrackerMap,
@@ -164,7 +164,7 @@
 // [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
 // Returns nullopt if successful and InvalidConfigReason if not.
 optional<InvalidConfigReason> updateMetrics(
-        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const ConfigKey& key, const StatsdConfig& config, 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,
@@ -219,7 +219,7 @@
 // [newAnomalyTrackers]: contains the list of sp to the AnomalyTrackers created.
 // Returns nullopt if successful and InvalidConfigReason if not.
 optional<InvalidConfigReason> updateAlerts(
-        const StatsdConfig& config, const int64_t currentTimeNs,
+        const StatsdConfig& config, int64_t currentTimeNs,
         const std::unordered_map<int64_t, int>& metricProducerMap,
         const std::set<int64_t>& replacedMetrics,
         const std::unordered_map<int64_t, int>& oldAlertTrackerMap,
@@ -234,7 +234,7 @@
 optional<InvalidConfigReason> 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 sp<AlarmMonitor>& periodicAlarmMonitor, int64_t timeBaseNs,
         const int64_t currentTimeNs,
         const std::vector<sp<AtomMatchingTracker>>& oldAtomMatchingTrackers,
         const std::unordered_map<int64_t, int>& oldAtomMatchingTrackerMap,
diff --git a/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp b/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
index bae9918..673ff45 100644
--- a/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
+++ b/statsd/src/metrics/parsing_utils/metrics_manager_util.cpp
@@ -64,6 +64,71 @@
     return true;
 }
 
+// DFS for ensuring there is no
+// 1. value matching in the FieldValueMatcher tree with Position::ALL.
+// 2. string replacement in the FieldValueMatcher tree without a value matcher with Position::ANY.
+// Using vector to keep track of visited FieldValueMatchers since we expect number of
+// FieldValueMatchers to be low.
+optional<InvalidConfigReasonEnum> validateFvmPositionAllAndAny(
+        const FieldValueMatcher& fvm, bool inPositionAll, bool inPositionAny,
+        vector<FieldValueMatcher const*>& visited) {
+    visited.push_back(&fvm);
+    inPositionAll = inPositionAll || fvm.position() == Position::ALL;
+    inPositionAny = inPositionAny || fvm.position() == Position::ANY;
+    if (fvm.value_matcher_case() == FieldValueMatcher::kMatchesTuple) {
+        for (const FieldValueMatcher& childFvm : fvm.matches_tuple().field_value_matcher()) {
+            if (std::find(visited.cbegin(), visited.cend(), &childFvm) != visited.cend()) {
+                continue;
+            }
+            const optional<InvalidConfigReasonEnum> reasonEnum =
+                    validateFvmPositionAllAndAny(childFvm, inPositionAll, inPositionAny, visited);
+            if (reasonEnum != nullopt) {
+                return reasonEnum;
+            }
+        }
+        return nullopt;
+    }
+    if (inPositionAll && fvm.value_matcher_case() != FieldValueMatcher::VALUE_MATCHER_NOT_SET) {
+        // value_matcher is set to something other than matches_tuple with Position::ALL
+        return INVALID_CONFIG_REASON_MATCHER_VALUE_MATCHER_WITH_POSITION_ALL;
+    }
+    if (inPositionAny && fvm.value_matcher_case() == FieldValueMatcher::VALUE_MATCHER_NOT_SET &&
+        fvm.has_replace_string()) {
+        // value_matcher is not set and there is a string replacement with Position::ANY
+        return INVALID_CONFIG_REASON_MATCHER_STRING_REPLACE_WITH_NO_VALUE_MATCHER_WITH_POSITION_ANY;
+    }
+    return nullopt;
+}
+
+optional<InvalidConfigReason> validateSimpleAtomMatcher(int64_t matcherId,
+                                                        const SimpleAtomMatcher& simpleMatcher) {
+    for (const FieldValueMatcher& fvm : simpleMatcher.field_value_matcher()) {
+        if (fvm.value_matcher_case() == FieldValueMatcher::VALUE_MATCHER_NOT_SET &&
+            !fvm.has_replace_string()) {
+            return createInvalidConfigReasonWithMatcher(
+                    INVALID_CONFIG_REASON_MATCHER_NO_VALUE_MATCHER_NOR_STRING_REPLACER, matcherId);
+        } else if (fvm.has_replace_string() &&
+                   !(fvm.value_matcher_case() == FieldValueMatcher::VALUE_MATCHER_NOT_SET ||
+                     fvm.value_matcher_case() == FieldValueMatcher::kEqString ||
+                     fvm.value_matcher_case() == FieldValueMatcher::kEqAnyString ||
+                     fvm.value_matcher_case() == FieldValueMatcher::kNeqAnyString ||
+                     fvm.value_matcher_case() == FieldValueMatcher::kEqWildcardString ||
+                     fvm.value_matcher_case() == FieldValueMatcher::kEqAnyWildcardString ||
+                     fvm.value_matcher_case() == FieldValueMatcher::kNeqAnyWildcardString)) {
+            return createInvalidConfigReasonWithMatcher(
+                    INVALID_CONFIG_REASON_MATCHER_INVALID_VALUE_MATCHER_WITH_STRING_REPLACE,
+                    matcherId);
+        }
+        vector<FieldValueMatcher const*> visited;
+        const optional<InvalidConfigReasonEnum> reasonEnum = validateFvmPositionAllAndAny(
+                fvm, false /* inPositionAll */, false /* inPositionAny */, visited);
+        if (reasonEnum != nullopt) {
+            return createInvalidConfigReasonWithMatcher(*reasonEnum, matcherId);
+        }
+    }
+    return nullopt;
+}
+
 }  // namespace
 
 sp<AtomMatchingTracker> createAtomMatchingTracker(
@@ -79,6 +144,12 @@
     uint64_t protoHash = Hash64(serializedMatcher);
     switch (logMatcher.contents_case()) {
         case AtomMatcher::ContentsCase::kSimpleAtomMatcher: {
+            invalidConfigReason =
+                    validateSimpleAtomMatcher(logMatcher.id(), logMatcher.simple_atom_matcher());
+            if (invalidConfigReason != nullopt) {
+                ALOGE("Matcher \"%lld\" malformed", (long long)logMatcher.id());
+                return nullptr;
+            }
             sp<AtomMatchingTracker> simpleAtomMatcher = new SimpleAtomMatchingTracker(
                     logMatcher.id(), protoHash, logMatcher.simple_atom_matcher(), uidMap);
             return simpleAtomMatcher;
@@ -620,7 +691,7 @@
         }
     }
 
-    FieldMatcher internalDimensions = simplePredicate.dimensions();
+    const FieldMatcher& internalDimensions = simplePredicate.dimensions();
 
     int conditionIndex = -1;
     if (metric.has_condition()) {
@@ -860,7 +931,7 @@
         return nullopt;
     }
 
-    sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
+    const sp<AtomMatchingTracker>& atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
     int atomTagId = *(atomMatcher->getAtomIds().begin());
     int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
 
@@ -1089,7 +1160,7 @@
     const bool containsAnyPositionInDimensionsInWhat = HasPositionANY(metric.dimensions_in_what());
     const bool shouldUseNestedDimensions = ShouldUseNestedDimensions(metric.dimensions_in_what());
 
-    sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
+    const sp<AtomMatchingTracker>& atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
     const int atomTagId = *(atomMatcher->getAtomIds().begin());
     const auto [dimensionSoftLimit, dimensionHardLimit] =
             StatsdStats::getAtomDimensionKeySizeLimits(
@@ -1167,7 +1238,7 @@
         return nullopt;
     }
 
-    sp<AtomMatchingTracker> atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
+    const sp<AtomMatchingTracker>& atomMatcher = allAtomMatchingTrackers.at(trackerIndex);
     int atomTagId = *(atomMatcher->getAtomIds().begin());
     int pullTagId = pullerManager->PullerForMatcherExists(atomTagId) ? atomTagId : -1;
 
@@ -1194,7 +1265,7 @@
         if (invalidConfigReason.has_value()) {
             return nullopt;
         }
-        sp<AtomMatchingTracker> triggerAtomMatcher =
+        const sp<AtomMatchingTracker>& triggerAtomMatcher =
                 allAtomMatchingTrackers.at(triggerTrackerIndex);
         triggerAtomId = *(triggerAtomMatcher->getAtomIds().begin());
     }
@@ -1216,6 +1287,18 @@
         }
     }
 
+    if (pullTagId != -1 && metric.sampling_percentage() != 100) {
+        invalidConfigReason = InvalidConfigReason(
+                INVALID_CONFIG_REASON_GAUGE_METRIC_PULLED_WITH_SAMPLING, metric.id());
+        return nullopt;
+    }
+
+    if (metric.sampling_percentage() < 1 || metric.sampling_percentage() > 100) {
+        invalidConfigReason = InvalidConfigReason(
+                INVALID_CONFIG_REASON_METRIC_INCORRECT_SAMPLING_PERCENTAGE, metric.id());
+        return nullopt;
+    }
+
     unordered_map<int, shared_ptr<Activation>> eventActivationMap;
     unordered_map<int, vector<shared_ptr<Activation>>> eventDeactivationMap;
     invalidConfigReason = handleMetricActivation(
@@ -1330,8 +1413,9 @@
     vector<uint8_t> stackTracker2(allAtomMatchingTrackers.size(), false);
     for (size_t matcherIndex = 0; matcherIndex < allAtomMatchingTrackers.size(); matcherIndex++) {
         auto& matcher = allAtomMatchingTrackers[matcherIndex];
-        invalidConfigReason = matcher->init(matcherIndex, matcherConfigs, allAtomMatchingTrackers,
-                                            atomMatchingTrackerMap, stackTracker2);
+        const auto [invalidConfigReason, _] =
+                matcher->init(matcherIndex, matcherConfigs, allAtomMatchingTrackers,
+                              atomMatchingTrackerMap, stackTracker2);
         if (invalidConfigReason.has_value()) {
             return invalidConfigReason;
         }
@@ -1423,8 +1507,8 @@
         stateProtoHashes[stateId] = Hash64(serializedState);
 
         const StateMap& stateMap = state.map();
-        for (auto group : stateMap.group()) {
-            for (auto value : group.value()) {
+        for (const auto& group : stateMap.group()) {
+            for (const auto& value : group.value()) {
                 allStateGroupMaps[stateId][value] = group.group_id();
             }
         }
diff --git a/statsd/src/metrics/parsing_utils/metrics_manager_util.h b/statsd/src/metrics/parsing_utils/metrics_manager_util.h
index 67c09f0..41e8dc7 100644
--- a/statsd/src/metrics/parsing_utils/metrics_manager_util.h
+++ b/statsd/src/metrics/parsing_utils/metrics_manager_util.h
@@ -52,21 +52,20 @@
 // 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 ConfigKey& key, const Predicate& predicate, int index,
         const unordered_map<int64_t, int>& atomMatchingTrackerMap,
         optional<InvalidConfigReason>& invalidConfigReason);
 
 // Get the hash of a metric, combining the activation if the metric has one.
 optional<InvalidConfigReason> getMetricProtoHash(
-        const StatsdConfig& config, const google::protobuf::MessageLite& metric, const int64_t id,
+        const StatsdConfig& config, const google::protobuf::MessageLite& metric, 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
 optional<InvalidConfigReason> handleMetricWithAtomMatchingTrackers(
-        const int64_t matcherId, const int64_t metricId, const int metricIndex,
-        const bool enforceOneAtom,
+        const int64_t matcherId, int64_t metricId, 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);
@@ -74,7 +73,7 @@
 // 1. Validates condition existence, including those in links
 // 2. Gets condition index and updates condition to metric map
 optional<InvalidConfigReason> handleMetricWithConditions(
-        const int64_t condition, const int64_t metricId, const int metricIndex,
+        const int64_t condition, int64_t metricId, int metricIndex,
         const std::unordered_map<int64_t, int>& conditionTrackerMap,
         const ::google::protobuf::RepeatedPtrField<MetricConditionLink>& links,
         const std::vector<sp<ConditionTracker>>& allConditionTrackers, int& conditionIndex,
@@ -84,7 +83,7 @@
 // Fills the new event activation/deactivation maps, preserving the existing activations.
 // Returns nullopt if successful and InvalidConfigReason if not.
 optional<InvalidConfigReason> handleMetricActivationOnConfigUpdate(
-        const StatsdConfig& config, const int64_t metricId, const int metricIndex,
+        const StatsdConfig& config, int64_t metricId, 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,
@@ -98,8 +97,8 @@
 // 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 ConfigKey& key, const StatsdConfig& config, int64_t timeBaseNs,
+        const int64_t currentTimeNs, const CountMetric& metric, int metricIndex,
         const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
         const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
         std::vector<sp<ConditionTracker>>& allConditionTrackers,
@@ -118,8 +117,8 @@
 // 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 ConfigKey& key, const StatsdConfig& config, int64_t timeBaseNs,
+        const int64_t currentTimeNs, const DurationMetric& metric, int metricIndex,
         const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
         const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
         std::vector<sp<ConditionTracker>>& allConditionTrackers,
@@ -138,8 +137,8 @@
 // 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 ConfigKey& key, const StatsdConfig& config, int64_t timeBaseNs,
+        const EventMetric& metric, int metricIndex,
         const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
         const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
         std::vector<sp<ConditionTracker>>& allConditionTrackers,
@@ -156,9 +155,9 @@
 // Creates a NumericValueMetricProducer 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>> createNumericValueMetricProducerAndUpdateMetadata(
-        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const ConfigKey& key, const StatsdConfig& config, int64_t timeBaseNs,
         const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
-        const ValueMetric& metric, const int metricIndex,
+        const ValueMetric& metric, int metricIndex,
         const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
         const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
         std::vector<sp<ConditionTracker>>& allConditionTrackers,
@@ -178,9 +177,9 @@
 // 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 ConfigKey& key, const StatsdConfig& config, int64_t timeBaseNs,
         const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
-        const GaugeMetric& metric, const int metricIndex,
+        const GaugeMetric& metric, int metricIndex,
         const std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
         const std::unordered_map<int64_t, int>& atomMatchingTrackerMap,
         std::vector<sp<ConditionTracker>>& allConditionTrackers,
@@ -198,9 +197,9 @@
 // Creates a KllMetricProducer 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>> createKllMetricProducerAndUpdateMetadata(
-        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseNs,
+        const ConfigKey& key, const StatsdConfig& config, int64_t timeBaseNs,
         const int64_t currentTimeNs, const sp<StatsPullerManager>& pullerManager,
-        const KllMetric& metric, const int metricIndex,
+        const KllMetric& metric, int metricIndex,
         const vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
         const unordered_map<int64_t, int>& atomMatchingTrackerMap,
         vector<sp<ConditionTracker>>& allConditionTrackers,
@@ -220,7 +219,7 @@
 // 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 UpdateStatus& updateStatus, const int64_t currentTimeNs,
+        const UpdateStatus& updateStatus, int64_t currentTimeNs,
         const std::unordered_map<int64_t, int>& metricProducerMap,
         std::vector<sp<MetricProducer>>& allMetricProducers,
         optional<InvalidConfigReason>& invalidConfigReason);
@@ -339,7 +338,7 @@
 // [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
 // Returns nullopt if successful and InvalidConfigReason if not.
 optional<InvalidConfigReason> initMetrics(
-        const ConfigKey& key, const StatsdConfig& config, const int64_t timeBaseTimeNs,
+        const ConfigKey& key, const StatsdConfig& config, 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,
@@ -360,7 +359,7 @@
 // Is called both on initialize new configs and config updates since alarms do not have any state.
 optional<InvalidConfigReason> initAlarms(const StatsdConfig& config, const ConfigKey& key,
                                          const sp<AlarmMonitor>& periodicAlarmMonitor,
-                                         const int64_t timeBaseNs, const int64_t currentTimeNs,
+                                         const int64_t timeBaseNs, int64_t currentTimeNs,
                                          std::vector<sp<AlarmTracker>>& allAlarmTrackers);
 
 // Initialize MetricsManager from StatsdConfig.
@@ -368,7 +367,7 @@
 optional<InvalidConfigReason> 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 sp<AlarmMonitor>& periodicAlarmMonitor, int64_t timeBaseNs,
         const int64_t currentTimeNs,
         std::unordered_map<int, std::vector<int>>& allTagIdsToMatchersMap,
         std::vector<sp<AtomMatchingTracker>>& allAtomMatchingTrackers,
diff --git a/statsd/src/packages/PackageInfoListener.h b/statsd/src/packages/PackageInfoListener.h
index 485f9fd..d796774 100644
--- a/statsd/src/packages/PackageInfoListener.h
+++ b/statsd/src/packages/PackageInfoListener.h
@@ -29,15 +29,14 @@
 public:
     // Uid map will notify this listener that the app with apk name and uid has been upgraded to
     // the specified version.
-    virtual void notifyAppUpgrade(const int64_t& eventTimeNs, const std::string& apk,
-                                  const int uid, const int64_t version) = 0;
+    virtual void notifyAppUpgrade(int64_t eventTimeNs, const std::string& apk, const int uid,
+                                  int64_t version) = 0;
 
     // Notify interested listeners that the given apk and uid combination no longer exits.
-    virtual void notifyAppRemoved(const int64_t& eventTimeNs, const std::string& apk,
-                                  const int uid) = 0;
+    virtual void notifyAppRemoved(int64_t eventTimeNs, const std::string& apk, const int uid) = 0;
 
     // Notify the listener that the UidMap snapshot is available.
-    virtual void onUidMapReceived(const int64_t& eventTimeNs) = 0;
+    virtual void onUidMapReceived(int64_t eventTimeNs) = 0;
 };
 
 }  // namespace statsd
diff --git a/statsd/src/packages/UidMap.cpp b/statsd/src/packages/UidMap.cpp
index aa43e8d..3177225 100644
--- a/statsd/src/packages/UidMap.cpp
+++ b/statsd/src/packages/UidMap.cpp
@@ -25,7 +25,6 @@
 
 using namespace android;
 
-using android::base::StringPrintf;
 using android::util::FIELD_COUNT_REPEATED;
 using android::util::FIELD_TYPE_BOOL;
 using android::util::FIELD_TYPE_BYTES;
@@ -94,12 +93,12 @@
     return normalizedName;
 }
 
-std::set<string> UidMap::getAppNamesFromUid(const int32_t& uid, bool returnNormalized) const {
+std::set<string> UidMap::getAppNamesFromUid(const int32_t uid, bool returnNormalized) const {
     lock_guard<mutex> lock(mMutex);
     return getAppNamesFromUidLocked(uid,returnNormalized);
 }
 
-std::set<string> UidMap::getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const {
+std::set<string> UidMap::getAppNamesFromUidLocked(const int32_t uid, bool returnNormalized) const {
     std::set<string> names;
     for (const auto& kv : mMap) {
         if (kv.first.first == uid && !kv.second.deleted) {
@@ -119,7 +118,7 @@
     return it->second.versionCode;
 }
 
-void UidMap::updateMap(const int64_t& timestamp, const UidData& uidData) {
+void UidMap::updateMap(const int64_t timestamp, const UidData& uidData) {
     wp<PackageInfoListener> broadcast = NULL;
     {
         lock_guard<mutex> lock(mMutex);  // Exclusively lock for updates.
@@ -157,13 +156,13 @@
     // itself before we call it. It's then the listener's job to handle it (expect the callback to
     // be called after listener is removed, and the listener should properly ignore it).
     auto strongPtr = broadcast.promote();
-    if (strongPtr != NULL) {
+    if (strongPtr != nullptr) {
         strongPtr->onUidMapReceived(timestamp);
     }
 }
 
-void UidMap::updateApp(const int64_t& timestamp, const string& appName, const int32_t& uid,
-                       const int64_t& versionCode, const string& versionString,
+void UidMap::updateApp(const int64_t timestamp, const string& appName, const int32_t uid,
+                       const int64_t versionCode, const string& versionString,
                        const string& installer, const vector<uint8_t>& certificateHash) {
     wp<PackageInfoListener> broadcast = NULL;
 
@@ -202,7 +201,7 @@
     }
 
     auto strongPtr = broadcast.promote();
-    if (strongPtr != NULL) {
+    if (strongPtr != nullptr) {
         strongPtr->notifyAppUpgrade(timestamp, appName, uid, versionCode);
     }
 }
@@ -224,7 +223,7 @@
     }
 }
 
-void UidMap::removeApp(const int64_t& timestamp, const string& app, const int32_t& uid) {
+void UidMap::removeApp(const int64_t timestamp, const string& app, const int32_t uid) {
     wp<PackageInfoListener> broadcast = NULL;
     {
         lock_guard<mutex> lock(mMutex);
@@ -255,12 +254,12 @@
     }
 
     auto strongPtr = broadcast.promote();
-    if (strongPtr != NULL) {
+    if (strongPtr != nullptr) {
         strongPtr->notifyAppRemoved(timestamp, app, uid);
     }
 }
 
-void UidMap::setListener(wp<PackageInfoListener> listener) {
+void UidMap::setListener(const wp<PackageInfoListener>& listener) {
     lock_guard<mutex> lock(mMutex);  // Lock for updates
     mSubscriber = listener;
 }
@@ -409,7 +408,7 @@
     }
 }
 
-void UidMap::appendUidMap(const int64_t& timestamp, const ConfigKey& key,
+void UidMap::appendUidMap(const int64_t timestamp, const ConfigKey& key,
                           const bool includeVersionStrings, const bool includeInstaller,
                           const uint8_t truncatedCertificateHashSize, std::set<string>* str_set,
                           ProtoOutputStream* proto) {
diff --git a/statsd/src/packages/UidMap.h b/statsd/src/packages/UidMap.h
index d081803..45429cb 100644
--- a/statsd/src/packages/UidMap.h
+++ b/statsd/src/packages/UidMap.h
@@ -73,9 +73,9 @@
     const string versionString;
     const string prevVersionString;
 
-    ChangeRecord(const bool isDeletion, const int64_t timestampNs, const string& package,
-                 const int32_t uid, const int64_t version, const string versionString,
-                 const int64_t prevVersion, const string prevVersionString)
+    ChangeRecord(const bool isDeletion, int64_t timestampNs, const string& package,
+                 const int32_t uid, int64_t version, const string& versionString,
+                 const int64_t prevVersion, const string& prevVersionString)
         : deletion(isDeletion),
           timestampNs(timestampNs),
           package(package),
@@ -99,18 +99,18 @@
 
     static sp<UidMap> getInstance();
 
-    void updateMap(const int64_t& timestamp, const UidData& uidData);
+    void updateMap(const int64_t timestamp, const UidData& uidData);
 
-    void updateApp(const int64_t& timestamp, const string& appName, const int32_t& uid,
-                   const int64_t& versionCode, const string& versionString, const string& installer,
+    void updateApp(const int64_t timestamp, const string& appName, const int32_t uid,
+                   const int64_t versionCode, const string& versionString, const string& installer,
                    const vector<uint8_t>& certificateHash);
-    void removeApp(const int64_t& timestamp, const string& app, const int32_t& uid);
+    void removeApp(const int64_t timestamp, const string& app, const int32_t uid);
 
     // Returns true if the given uid contains the specified app (eg. com.google.android.gms).
     bool hasApp(int uid, const string& packageName) const;
 
     // Returns the app names from uid.
-    std::set<string> getAppNamesFromUid(const int32_t& uid, bool returnNormalized) const;
+    std::set<string> getAppNamesFromUid(int32_t uid, bool returnNormalized) const;
 
     int64_t getAppVersion(int uid, const string& packageName) const;
 
@@ -121,7 +121,7 @@
     // Command for indicating to the map that StatsLogProcessor should be notified if an app is
     // updated. This allows metric producers and managers to distinguish when the same uid or app
     // represents a different version of an app.
-    void setListener(wp<PackageInfoListener> listener);
+    void setListener(const wp<PackageInfoListener>& listener);
 
     // Informs uid map that a config is added/updated. Used for keeping mConfigKeys up to date.
     void OnConfigUpdated(const ConfigKey& key);
@@ -138,10 +138,9 @@
     // Gets all snapshots and changes that have occurred since the last output.
     // If every config key has received a change or snapshot record, then this
     // record is deleted.
-    void appendUidMap(const int64_t& timestamp, const ConfigKey& key,
-                      const bool includeVersionStrings, const bool includeInstaller,
-                      const uint8_t truncatedCertificateHashSize, std::set<string>* str_set,
-                      ProtoOutputStream* proto);
+    void appendUidMap(int64_t timestamp, const ConfigKey& key, const bool includeVersionStrings,
+                      const bool includeInstaller, const uint8_t truncatedCertificateHashSize,
+                      std::set<string>* str_set, ProtoOutputStream* proto);
 
     // Forces the output to be cleared. We still generate a snapshot based on the current state.
     // This results in extra data uploaded but helps us reconstruct the uid mapping on the server
@@ -165,7 +164,7 @@
                              ProtoOutputStream* proto) const;
 
 private:
-    std::set<string> getAppNamesFromUidLocked(const int32_t& uid, bool returnNormalized) const;
+    std::set<string> getAppNamesFromUidLocked(int32_t uid, bool returnNormalized) const;
     string normalizeAppName(const string& appName) const;
 
     void writeUidMapSnapshotLocked(const int64_t timestamp, const bool includeVersionStrings,
@@ -179,7 +178,7 @@
     mutable mutex mIsolatedMutex;
 
     struct PairHash {
-        size_t operator()(std::pair<int, string> p) const noexcept {
+        size_t operator()(const std::pair<int, string>& p) const noexcept {
             std::hash<std::string> hash_fn;
             return hash_fn(std::to_string(p.first) + p.second);
         }
diff --git a/statsd/src/shell/ShellSubscriber.h b/statsd/src/shell/ShellSubscriber.h
index 4ff8861..5e54fbf 100644
--- a/statsd/src/shell/ShellSubscriber.h
+++ b/statsd/src/shell/ShellSubscriber.h
@@ -56,7 +56,7 @@
  */
 class ShellSubscriber : public virtual RefBase {
 public:
-    ShellSubscriber(sp<UidMap> uidMap, sp<StatsPullerManager> pullerMgr,
+    ShellSubscriber(const sp<UidMap>& uidMap, const sp<StatsPullerManager>& pullerMgr,
                     const std::shared_ptr<LogEventFilter>& logEventFilter)
         : mUidMap(uidMap), mPullerMgr(pullerMgr), mLogEventFilter(logEventFilter){};
 
diff --git a/statsd/src/shell/ShellSubscriberClient.cpp b/statsd/src/shell/ShellSubscriberClient.cpp
index aaa4126..67f7eb1 100644
--- a/statsd/src/shell/ShellSubscriberClient.cpp
+++ b/statsd/src/shell/ShellSubscriberClient.cpp
@@ -24,7 +24,6 @@
 #include "stats_log_util.h"
 
 using android::base::unique_fd;
-using android::util::ProtoOutputStream;
 using Status = ::ndk::ScopedAStatus;
 
 namespace android {
@@ -177,23 +176,25 @@
 bool ShellSubscriberClient::writeEventToProtoIfMatched(const LogEvent& event,
                                                        const SimpleAtomMatcher& matcher,
                                                        const sp<UidMap>& uidMap) {
-    if (!matchesSimple(uidMap, matcher, event)) {
+    auto [matched, transformedEvent] = matchesSimple(mUidMap, matcher, event);
+    if (!matched) {
         return false;
     }
+    const LogEvent& eventRef = transformedEvent == nullptr ? event : *transformedEvent;
 
     // Cache atom event in mProtoOut.
     uint64_t atomToken = mProtoOut.start(util::FIELD_TYPE_MESSAGE | util::FIELD_COUNT_REPEATED |
                                          FIELD_ID_SHELL_DATA__ATOM);
-    event.ToProto(mProtoOut);
+    eventRef.ToProto(mProtoOut);
     mProtoOut.end(atomToken);
 
-    const int64_t timestampNs = truncateTimestampIfNecessary(event);
+    const int64_t timestampNs = truncateTimestampIfNecessary(eventRef);
     mProtoOut.write(util::FIELD_TYPE_INT64 | util::FIELD_COUNT_REPEATED |
                             FIELD_ID_SHELL_DATA__ELAPSED_TIMESTAMP_NANOS,
                     static_cast<long long>(timestampNs));
 
     // Update byte size of cached data.
-    mCacheSize += getSize(event.getValues()) + sizeof(timestampNs);
+    mCacheSize += getSize(eventRef.getValues()) + sizeof(timestampNs);
 
     return true;
 }
diff --git a/statsd/src/socket/StatsSocketListener.cpp b/statsd/src/socket/StatsSocketListener.cpp
index 3f36033..dd1013d 100644
--- a/statsd/src/socket/StatsSocketListener.cpp
+++ b/statsd/src/socket/StatsSocketListener.cpp
@@ -38,10 +38,10 @@
 namespace os {
 namespace statsd {
 
-StatsSocketListener::StatsSocketListener(std::shared_ptr<LogEventQueue> queue,
+StatsSocketListener::StatsSocketListener(const std::shared_ptr<LogEventQueue>& queue,
                                          const std::shared_ptr<LogEventFilter>& logEventFilter)
     : SocketListener(getLogSocket(), false /*start listen*/),
-      mQueue(std::move(queue)),
+      mQueue(queue),
       mLogEventFilter(logEventFilter) {
 }
 
@@ -148,6 +148,7 @@
 
     const int32_t atomId = logEvent->GetTagId();
     const bool isAtomSkipped = logEvent->isParsedHeaderOnly();
+    const int64_t atomTimestamp = logEvent->GetElapsedTimestampNs();
 
     if (atomId == util::STATS_SOCKET_LOSS_REPORTED) {
         if (isAtomSkipped) {
@@ -164,8 +165,10 @@
         }
     }
 
-    int64_t oldestTimestamp;
-    if (!queue->push(std::move(logEvent), &oldestTimestamp)) {
+    const auto [success, oldestTimestamp, queueSize] = queue->push(std::move(logEvent));
+    if (success) {
+        StatsdStats::getInstance().noteEventQueueSize(queueSize, atomTimestamp);
+    } else {
         StatsdStats::getInstance().noteEventQueueOverflow(oldestTimestamp, atomId, isAtomSkipped);
     }
 }
diff --git a/statsd/src/socket/StatsSocketListener.h b/statsd/src/socket/StatsSocketListener.h
index 903eff3..fcff2c2 100644
--- a/statsd/src/socket/StatsSocketListener.h
+++ b/statsd/src/socket/StatsSocketListener.h
@@ -38,7 +38,7 @@
 
 class StatsSocketListener : public SocketListener, public virtual RefBase {
 public:
-    explicit StatsSocketListener(std::shared_ptr<LogEventQueue> queue,
+    explicit StatsSocketListener(const std::shared_ptr<LogEventQueue>& queue,
                                  const std::shared_ptr<LogEventFilter>& logEventFilter);
 
     virtual ~StatsSocketListener() = default;
@@ -81,6 +81,7 @@
     FRIEND_TEST(SocketParseMessageTest, TestProcessMessageFilterCompleteSet);
     FRIEND_TEST(SocketParseMessageTest, TestProcessMessageFilterPartialSet);
     FRIEND_TEST(SocketParseMessageTest, TestProcessMessageFilterToggle);
+    FRIEND_TEST(LogEventQueue_test, TestQueueMaxSize);
 };
 
 }  // namespace statsd
diff --git a/statsd/src/state/StateManager.cpp b/statsd/src/state/StateManager.cpp
index 7841748..1a32495 100644
--- a/statsd/src/state/StateManager.cpp
+++ b/statsd/src/state/StateManager.cpp
@@ -53,7 +53,7 @@
     }
 }
 
-void StateManager::registerListener(const int32_t atomId, wp<StateListener> listener) {
+void StateManager::registerListener(const int32_t atomId, const wp<StateListener>& listener) {
     // Check if state tracker already exists.
     if (mStateTrackers.find(atomId) == mStateTrackers.end()) {
         mStateTrackers[atomId] = new StateTracker(atomId);
@@ -61,7 +61,7 @@
     mStateTrackers[atomId]->registerListener(listener);
 }
 
-void StateManager::unregisterListener(const int32_t atomId, wp<StateListener> listener) {
+void StateManager::unregisterListener(const int32_t atomId, const wp<StateListener>& listener) {
     std::unique_lock<std::mutex> lock(mMutex);
 
     // Hold the sp<> until the lock is released so that ~StateTracker() is
diff --git a/statsd/src/state/StateManager.h b/statsd/src/state/StateManager.h
index 68b5c90..2db206e 100644
--- a/statsd/src/state/StateManager.h
+++ b/statsd/src/state/StateManager.h
@@ -55,17 +55,17 @@
     // If the correct StateTracker does not exist, a new StateTracker is created.
     // Note: StateTrackers can be created for non-state atoms. They are essentially empty and
     // do not perform any actions.
-    void registerListener(const int32_t atomId, wp<StateListener> listener);
+    void registerListener(const int32_t atomId, const wp<StateListener>& listener);
 
     // Notifies the correct StateTracker to unregister a listener
     // and removes the tracker if it no longer has any listeners.
-    void unregisterListener(const int32_t atomId, wp<StateListener> listener);
+    void unregisterListener(const int32_t atomId, const wp<StateListener>& listener);
 
     // Returns true if the StateTracker exists and queries for the
     // original state value mapped to the given query key. The state value is
     // stored and output in a FieldValue class.
     // Returns false if the StateTracker doesn't exist.
-    bool getStateValue(const int32_t atomId, const HashableDimensionKey& queryKey,
+    bool getStateValue(int32_t atomId, const HashableDimensionKey& queryKey,
                        FieldValue* output) const;
 
     // Updates mAllowedLogSources with the latest uids for the packages that are allowed to log.
@@ -77,7 +77,7 @@
         return mStateTrackers.size();
     }
 
-    inline int getListenersCount(const int32_t atomId) const {
+    inline int getListenersCount(int32_t atomId) const {
         auto it = mStateTrackers.find(atomId);
         if (it != mStateTrackers.end()) {
             return it->second->getListenersCount();
diff --git a/statsd/src/state/StateTracker.cpp b/statsd/src/state/StateTracker.cpp
index 250a65c..d037e73 100644
--- a/statsd/src/state/StateTracker.cpp
+++ b/statsd/src/state/StateTracker.cpp
@@ -25,7 +25,7 @@
 namespace os {
 namespace statsd {
 
-StateTracker::StateTracker(const int32_t atomId) : mField(atomId, 0) {
+StateTracker::StateTracker(int32_t atomId) : mField(atomId, 0) {
 }
 
 void StateTracker::onLogEvent(const LogEvent& event) {
@@ -62,11 +62,11 @@
     updateStateForPrimaryKey(eventTimeNs, primaryKey, newState, nested, mStateMap[primaryKey]);
 }
 
-void StateTracker::registerListener(wp<StateListener> listener) {
+void StateTracker::registerListener(const wp<StateListener>& listener) {
     mListeners.insert(listener);
 }
 
-void StateTracker::unregisterListener(wp<StateListener> listener) {
+void StateTracker::unregisterListener(const wp<StateListener>& listener) {
     mListeners.erase(listener);
 }
 
@@ -165,7 +165,7 @@
 void StateTracker::notifyListeners(const int64_t eventTimeNs,
                                    const HashableDimensionKey& primaryKey,
                                    const FieldValue& oldState, const FieldValue& newState) {
-    for (auto l : mListeners) {
+    for (const auto& l : mListeners) {
         auto sl = l.promote();
         if (sl != nullptr) {
             sl->onStateChanged(eventTimeNs, mField.getTag(), primaryKey, oldState, newState);
diff --git a/statsd/src/state/StateTracker.h b/statsd/src/state/StateTracker.h
index 2bef70e..0fd504d 100644
--- a/statsd/src/state/StateTracker.h
+++ b/statsd/src/state/StateTracker.h
@@ -29,7 +29,7 @@
 
 class StateTracker : public virtual RefBase {
 public:
-    StateTracker(const int32_t atomId);
+    StateTracker(int32_t atomId);
 
     virtual ~StateTracker(){};
 
@@ -40,9 +40,9 @@
 
     // Adds new listeners to set of StateListeners. If a listener is already
     // registered, it is ignored.
-    void registerListener(wp<StateListener> listener);
+    void registerListener(const wp<StateListener>& listener);
 
-    void unregisterListener(wp<StateListener> listener);
+    void unregisterListener(const wp<StateListener>& listener);
 
     // The output is a FieldValue object that has mStateField as the field and
     // the original state value (found using the given query key) as the value.
diff --git a/statsd/src/stats_log.proto b/statsd/src/stats_log.proto
index b095546..7354f37 100644
--- a/statsd/src/stats_log.proto
+++ b/statsd/src/stats_log.proto
@@ -400,7 +400,9 @@
 
   repeated string strings = 9;
 
-  repeated DataCorruptedReason data_corrupted_reason = 10;
+  reserved 10;
+
+  repeated DataCorruptedReason data_corrupted_reason = 11;
 }
 
 message ConfigMetricsReportList {
@@ -682,6 +684,13 @@
     }
 
     optional SocketLossStats socket_loss_stats = 24;
+
+    message EventQueueStats {
+        optional int32 max_size_observed = 1;
+        optional int64 max_size_observed_elapsed_nanos = 2;
+    }
+
+    optional EventQueueStats event_queue_stats = 25;
 }
 
 message AlertTriggerDetails {
diff --git a/statsd/src/stats_log_util.cpp b/statsd/src/stats_log_util.cpp
index 1d9a43b..77a3d4f 100644
--- a/statsd/src/stats_log_util.cpp
+++ b/statsd/src/stats_log_util.cpp
@@ -617,7 +617,7 @@
     return success;
 }
 
-void mapIsolatedUidsToHostUidInLogEvent(const sp<UidMap> uidMap, LogEvent& event) {
+void mapIsolatedUidsToHostUidInLogEvent(const sp<UidMap>& uidMap, LogEvent& event) {
     uint8_t remainingUidCount = event.getNumUidFields();
     vector<FieldValue>* fieldValues = event.getMutableValues();
     auto it = fieldValues->begin();
diff --git a/statsd/src/stats_log_util.h b/statsd/src/stats_log_util.h
index 68155e4..3ca94be 100644
--- a/statsd/src/stats_log_util.h
+++ b/statsd/src/stats_log_util.h
@@ -79,7 +79,7 @@
 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 int64_t value,
+void writeNonZeroStatToStream(const uint64_t fieldId, int64_t value,
                               ProtoOutputStream* protoOutput);
 
 // Helper function to write PulledAtomStats to ProtoOutputStream
@@ -117,7 +117,7 @@
     return atomId >= StatsdStats::kPullAtomStartTag && atomId < StatsdStats::kVendorAtomStartTag;
 }
 
-void mapIsolatedUidsToHostUidInLogEvent(const sp<UidMap> uidMap, LogEvent& event);
+void mapIsolatedUidsToHostUidInLogEvent(const sp<UidMap>& uidMap, LogEvent& event);
 
 std::string toHexString(const string& bytes);
 
diff --git a/statsd/src/statsd_config.proto b/statsd/src/statsd_config.proto
index e8c1d99..a7eef69 100644
--- a/statsd/src/statsd_config.proto
+++ b/statsd/src/statsd_config.proto
@@ -56,6 +56,14 @@
   repeated FieldMatcher child = 3;
 }
 
+message StringReplacer {
+  // Regex for matching the string.
+  optional string regex = 1;
+
+  // String with which to replace the matched string.
+  optional string replacement = 2;
+}
+
 message FieldValueMatcher {
   optional int32 field = 1;
 
@@ -85,6 +93,11 @@
     StringListMatcher eq_any_wildcard_string = 18;
     StringListMatcher neq_any_wildcard_string = 19;
   }
+
+  // Can only be present if either:
+  // 1. value_matcher is not set.
+  // 2. value_matcher is set to one that is applicable to string fields.
+  optional StringReplacer replace_string = 20;
 }
 
 message MessageMatcher {
@@ -343,6 +356,8 @@
 
   optional int32 max_dimensions_per_bucket = 16;
 
+  optional int32 sampling_percentage = 17 [default = 100];
+
   reserved 100;
   reserved 101;
 }
diff --git a/statsd/src/storage/StorageManager.h b/statsd/src/storage/StorageManager.h
index 9a25bf0..0a459a8 100644
--- a/statsd/src/storage/StorageManager.h
+++ b/statsd/src/storage/StorageManager.h
@@ -42,7 +42,7 @@
 class StorageManager : public virtual RefBase {
 public:
     struct FileInfo {
-        FileInfo(std::string name, bool isHistory, int fileSize, long fileAge)
+        FileInfo(const std::string& name, bool isHistory, int fileSize, long fileAge)
             : mFileName(name),
               mIsHistory(isHistory),
               mFileSizeBytes(fileSize),
@@ -162,8 +162,7 @@
 
     static void sortFiles(vector<FileInfo>* fileNames);
 
-    static void enforceDbGuardrails(const char* path, const int64_t wallClockSec,
-                                    const int64_t maxBytes);
+    static void enforceDbGuardrails(const char* path, int64_t wallClockSec, int64_t maxBytes);
 
     static bool hasFile(const char* file);
 
diff --git a/statsd/src/utils/DbUtils.h b/statsd/src/utils/DbUtils.h
index 8a561e6..06d1292 100644
--- a/statsd/src/utils/DbUtils.h
+++ b/statsd/src/utils/DbUtils.h
@@ -39,15 +39,15 @@
 string reformatMetricId(const int64_t metricId);
 
 /* Creates a new data table for a specified metric if one does not yet exist. */
-bool createTableIfNeeded(const ConfigKey& key, const int64_t metricId, const LogEvent& event);
+bool createTableIfNeeded(const ConfigKey& key, int64_t metricId, const LogEvent& event);
 
 /* Checks whether the table schema for the given metric matches the event.
  * Returns true if the table has not yet been created.
  */
-bool isEventCompatible(const ConfigKey& key, const int64_t metricId, const LogEvent& event);
+bool isEventCompatible(const ConfigKey& key, int64_t metricId, const LogEvent& event);
 
 /* Deletes a data table for the specified metric. */
-bool deleteTable(const ConfigKey& key, const int64_t metricId);
+bool deleteTable(const ConfigKey& key, int64_t metricId);
 
 /* Deletes the SQLite db data file. */
 void deleteDb(const ConfigKey& key);
@@ -63,11 +63,10 @@
 /* Inserts new data into the specified metric data table.
  * A temp sqlite handle is created using the ConfigKey.
  */
-bool insert(const ConfigKey& key, const int64_t metricId, const vector<LogEvent>& events,
-            string& error);
+bool insert(const ConfigKey& key, int64_t metricId, const vector<LogEvent>& events, string& error);
 
 /* Inserts new data into the specified sqlite db handle. */
-bool insert(sqlite3* db, const int64_t metricId, const vector<LogEvent>& events, string& error);
+bool insert(sqlite3* db, int64_t metricId, const vector<LogEvent>& events, string& error);
 
 /* Executes a sql query on the specified SQLite db.
  * A temp sqlite handle is created using the ConfigKey.
@@ -75,7 +74,7 @@
 bool query(const ConfigKey& key, const string& zSql, vector<vector<string>>& rows,
            vector<int32_t>& columnTypes, vector<string>& columnNames, string& err);
 
-bool flushTtl(sqlite3* db, const int64_t metricId, const int64_t ttlWallClockNs);
+bool flushTtl(sqlite3* db, int64_t metricId, int64_t ttlWallClockNs);
 
 /* Checks for database corruption and deletes the db if it is corrupted. */
 void verifyIntegrityAndDeleteIfNecessary(const ConfigKey& key);
@@ -86,4 +85,4 @@
 }  // namespace dbutils
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/statsd/src/utils/MultiConditionTrigger.cpp b/statsd/src/utils/MultiConditionTrigger.cpp
index 5ef50ee..d9ad25b 100644
--- a/statsd/src/utils/MultiConditionTrigger.cpp
+++ b/statsd/src/utils/MultiConditionTrigger.cpp
@@ -19,19 +19,21 @@
 
 #include <thread>
 
-using namespace std;
-
 namespace android {
 namespace os {
 namespace statsd {
 
+using std::function;
+using std::set;
+using std::string;
+
 MultiConditionTrigger::MultiConditionTrigger(const set<string>& conditionNames,
                                              function<void()> trigger)
     : mRemainingConditionNames(conditionNames),
-      mTrigger(trigger),
+      mTrigger(std::move(trigger)),
       mCompleted(mRemainingConditionNames.empty()) {
     if (mCompleted) {
-        thread executorThread([this] { mTrigger(); });
+        std::thread executorThread([this] { mTrigger(); });
         executorThread.detach();
     }
 }
@@ -39,7 +41,7 @@
 void MultiConditionTrigger::markComplete(const string& conditionName) {
     bool doTrigger = false;
     {
-        lock_guard<mutex> lg(mMutex);
+        std::lock_guard<std::mutex> lg(mMutex);
         if (mCompleted) {
             return;
         }
diff --git a/statsd/src/utils/MultiConditionTrigger.h b/statsd/src/utils/MultiConditionTrigger.h
index 9a0314b..7a09119 100644
--- a/statsd/src/utils/MultiConditionTrigger.h
+++ b/statsd/src/utils/MultiConditionTrigger.h
@@ -40,7 +40,7 @@
 
     // Mark a specific condition as true. If this condition has called markComplete already or if
     // the event was not specified in the constructor, the function is a no-op.
-    void markComplete(const std::string& eventName);
+    void markComplete(const std::string& conditionName);
 
 private:
     mutable std::mutex mMutex;
diff --git a/statsd/src/utils/Regex.cpp b/statsd/src/utils/Regex.cpp
new file mode 100644
index 0000000..7c24dc8
--- /dev/null
+++ b/statsd/src/utils/Regex.cpp
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2024 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 "Regex.h"
+
+#include <log/log.h>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::string;
+using std::unique_ptr;
+
+Regex::Regex(regex_t impl) : mImpl(std::move(impl)) {
+}
+
+Regex::~Regex() {
+    regfree(&mImpl);
+}
+
+unique_ptr<Regex> Regex::create(const string& pattern) {
+    regex_t impl;
+    int status = regcomp(&impl, pattern.c_str(), REG_EXTENDED);
+
+    if (status != 0) {  // Invalid regex pattern.
+        // Calculate size of string needed to store error description.
+        size_t errBufSize = regerror(status, &impl, nullptr, 0);
+
+        // Get the error description.
+        char errBuf[errBufSize];
+        regerror(status, &impl, errBuf, errBufSize);
+
+        ALOGE("regex_error: %s, pattern: %s", errBuf, pattern.c_str());
+        regfree(&impl);
+        return nullptr;
+    } else if (impl.re_nsub > 0) {
+        ALOGE("regex_error: subexpressions are not allowed, pattern: %s", pattern.c_str());
+        regfree(&impl);
+        return nullptr;
+    } else {
+        return std::make_unique<Regex>(impl);
+    }
+}
+
+bool Regex::replace(string& str, const string& replacement) {
+    regmatch_t match;
+    int status = regexec(&mImpl, str.c_str(), 1 /* nmatch */, &match /* pmatch */, 0 /* flags */);
+
+    if (status != 0 || match.rm_so == -1) {  // No match.
+        return false;
+    }
+    str.replace(match.rm_so, match.rm_eo - match.rm_so, replacement);
+    return true;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/statsd/src/utils/Regex.h b/statsd/src/utils/Regex.h
new file mode 100644
index 0000000..f1931fa
--- /dev/null
+++ b/statsd/src/utils/Regex.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2024 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 <regex.h>
+
+#include <memory>
+#include <string>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class Regex {
+public:
+    Regex(regex_t impl);  // Do not use. It is public for std::make_unique. Use Regex::create.
+    ~Regex();
+    Regex& operator=(const Regex&) = delete;
+    Regex(const Regex&) = delete;
+
+    // Returns nullptr if pattern is not valid POSIX regex.
+    static std::unique_ptr<Regex> create(const std::string& pattern);
+
+    // Looks for a regex match in str and replaces the matched portion with replacement in-place.
+    // Returns true if there was a match, false otherwise.
+    bool replace(std::string& str, const std::string& replacement);
+
+private:
+    regex_t mImpl;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/statsd/src/utils/RestrictedPolicyManager.cpp b/statsd/src/utils/RestrictedPolicyManager.cpp
index 3ae75a4..0c18a13 100644
--- a/statsd/src/utils/RestrictedPolicyManager.cpp
+++ b/statsd/src/utils/RestrictedPolicyManager.cpp
@@ -30,7 +30,7 @@
 }
 
 int32_t RestrictedPolicyManager::getRestrictedCategoryTtl(
-        const StatsdRestrictionCategory categoryId) {
+        const StatsdRestrictionCategory categoryId) const {
     return mRestrictionCategoryTtlInDaysMap.find(categoryId) !=
                            mRestrictionCategoryTtlInDaysMap.end()
                    ? mRestrictionCategoryTtlInDaysMap.at(categoryId)
@@ -39,4 +39,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
\ No newline at end of file
+}  // namespace android
diff --git a/statsd/src/utils/RestrictedPolicyManager.h b/statsd/src/utils/RestrictedPolicyManager.h
index 34d1fa3..9795640 100644
--- a/statsd/src/utils/RestrictedPolicyManager.h
+++ b/statsd/src/utils/RestrictedPolicyManager.h
@@ -21,8 +21,6 @@
 
 #include "stats_annotations.h"
 
-using std::map;
-
 namespace android {
 namespace os {
 namespace statsd {
@@ -41,13 +39,14 @@
 
 // Single instance shared across the process.
 class RestrictedPolicyManager {
+    RestrictedPolicyManager() = default;
+
 public:
     static RestrictedPolicyManager& getInstance();
-    ~RestrictedPolicyManager(){};
 
     // Gets the TTL in days for a particular restricted category. Returns the default for unknown
     // categories.
-    int32_t getRestrictedCategoryTtl(const StatsdRestrictionCategory categoryId);
+    int32_t getRestrictedCategoryTtl(StatsdRestrictionCategory categoryId) const;
 
 private:
     std::map<StatsdRestrictionCategory, int32_t> mRestrictionCategoryTtlInDaysMap;
diff --git a/statsd/src/utils/ShardOffsetProvider.h b/statsd/src/utils/ShardOffsetProvider.h
index 7facd98..506e230 100644
--- a/statsd/src/utils/ShardOffsetProvider.h
+++ b/statsd/src/utils/ShardOffsetProvider.h
@@ -51,6 +51,7 @@
     FRIEND_TEST(CountMetricE2eTest, TestDimensionalSampling);
     FRIEND_TEST(DurationMetricE2eTest, TestDimensionalSampling);
     FRIEND_TEST(GaugeMetricE2ePushedTest, TestDimensionalSampling);
+    FRIEND_TEST(GaugeMetricE2ePushedTest, TestPushedGaugeMetricSamplingWithDimensionalSampling);
     FRIEND_TEST(GaugeMetricProducerTest, TestPullDimensionalSampling);
     FRIEND_TEST(KllMetricE2eTest, TestDimensionalSampling);
     FRIEND_TEST(NumericValueMetricProducerTest, TestDimensionalSampling);
diff --git a/statsd/tests/FieldValue_test.cpp b/statsd/tests/FieldValue_test.cpp
index 3baf9f7..cf1e5a5 100644
--- a/statsd/tests/FieldValue_test.cpp
+++ b/statsd/tests/FieldValue_test.cpp
@@ -158,6 +158,40 @@
     EXPECT_EQ("some value", output.getValues()[6].mValue.str_value);
 }
 
+TEST(AtomMatcherTest, TestFilter_FIRST) {
+    FieldMatcher matcher1;
+    matcher1.set_field(10);
+    FieldMatcher* child = matcher1.add_child();
+    child->set_field(1);
+    child->set_position(Position::FIRST);
+
+    child->add_child()->set_field(1);
+    child->add_child()->set_field(2);
+
+    child = matcher1.add_child();
+    child->set_field(2);
+
+    vector<Matcher> matchers;
+    translateFieldMatcher(matcher1, &matchers);
+
+    std::vector<int> attributionUids = {1111, 2222, 3333};
+    std::vector<string> attributionTags = {"location1", "location2", "location3"};
+
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeLogEvent(&event, 10 /*atomId*/, 1012345, attributionUids, attributionTags, "some value");
+    HashableDimensionKey output;
+
+    filterValues(matchers, event.getValues(), &output);
+
+    ASSERT_EQ((size_t)3, output.getValues().size());
+    EXPECT_EQ((int32_t)0x02010101, output.getValues()[0].mField.getField());
+    EXPECT_EQ((int32_t)1111, output.getValues()[0].mValue.int_value);
+    EXPECT_EQ((int32_t)0x02010102, output.getValues()[1].mField.getField());
+    EXPECT_EQ("location1", output.getValues()[1].mValue.str_value);
+    EXPECT_EQ((int32_t)0x00020000, output.getValues()[2].mField.getField());
+    EXPECT_EQ("some value", output.getValues()[2].mValue.str_value);
+};
+
 TEST(AtomMatcherTest, TestFilterRepeated_FIRST) {
     FieldMatcher matcher;
     matcher.set_field(123);
diff --git a/statsd/tests/LogEntryMatcher_test.cpp b/statsd/tests/LogEntryMatcher_test.cpp
index 5c098c2..75ec3b5 100644
--- a/statsd/tests/LogEntryMatcher_test.cpp
+++ b/statsd/tests/LogEntryMatcher_test.cpp
@@ -24,6 +24,7 @@
 #include "statsd_test_util.h"
 
 using namespace android::os::statsd;
+using std::shared_ptr;
 using std::unordered_map;
 using std::vector;
 
@@ -31,7 +32,7 @@
 const int32_t TAG_ID_2 = 28;  // hardcoded tag of atom with uid field
 const int FIELD_ID_1 = 1;
 const int FIELD_ID_2 = 2;
-const int FIELD_ID_3 = 2;
+const int FIELD_ID_3 = 3;
 
 const int ATTRIBUTION_UID_FIELD_ID = 1;
 const int ATTRIBUTION_TAG_FIELD_ID = 2;
@@ -151,11 +152,11 @@
     makeIntLogEvent(&event, TAG_ID, 0, 11);
 
     // Test
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // Wrong tag id.
     simpleMatcher->set_atom_id(TAG_ID + 1);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 }
 
 TEST(AtomMatcherTest, TestAttributionMatcher) {
@@ -186,43 +187,43 @@
     fieldMatcher->set_eq_string("some value");
 
     // Tag not matched.
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "location3");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "location1");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // Match last node.
     attributionMatcher->set_position(Position::LAST);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "location3");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // Match any node.
     attributionMatcher->set_position(Position::ANY);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "location1");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "location2");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "location3");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "location4");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // Attribution match but primitive field not match.
     attributionMatcher->set_position(Position::ANY);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "location2");
     fieldMatcher->set_eq_string("wrong value");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     fieldMatcher->set_eq_string("some value");
 
@@ -232,7 +233,7 @@
             ATTRIBUTION_UID_FIELD_ID);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "pkg0");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     UidData uidData;
     *uidData.add_app_info() = createApplicationInfo(/*uid*/ 1111, /*version*/ 1, "v1", "pkg0");
@@ -243,47 +244,47 @@
 
     uidMap->updateMap(1, uidData);
 
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
-            "pkg3");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+            "PkG3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
-            "pkg2");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+            "Pkg2");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "pkg1");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "pkg0");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     attributionMatcher->set_position(Position::FIRST);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "pkg0");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
-            "pkg3");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+            "PkG3");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
-            "pkg2");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+            "Pkg2");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "pkg1");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     attributionMatcher->set_position(Position::LAST);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "pkg0");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
-            "pkg3");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+            "PkG3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
-            "pkg2");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+            "Pkg2");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "pkg1");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // Uid + tag.
     attributionMatcher->set_position(Position::ANY);
@@ -293,96 +294,96 @@
             "pkg0");
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
             "location1");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "pkg1");
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
             "location1");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "pkg1");
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
             "location2");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
-            "pkg2");
+            "Pkg2");
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
             "location3");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
-            "pkg3");
+            "PkG3");
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
             "location3");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
-            "pkg3");
+            "PkG3");
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
             "location1");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     attributionMatcher->set_position(Position::FIRST);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "pkg0");
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
             "location1");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "pkg1");
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
             "location1");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "pkg1");
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
             "location2");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
-            "pkg2");
+            "Pkg2");
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
             "location3");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
-            "pkg3");
+            "PkG3");
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
             "location3");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
-            "pkg3");
+            "PkG3");
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
             "location1");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     attributionMatcher->set_position(Position::LAST);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "pkg0");
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
             "location1");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "pkg1");
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
             "location1");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
             "pkg1");
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
             "location2");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
-            "pkg2");
+            "Pkg2");
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
             "location3");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
-            "pkg3");
+            "PkG3");
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
             "location3");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string(
-            "pkg3");
+            "PkG3");
     attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)->set_eq_string(
             "location1");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 }
 
 TEST(AtomMatcherTest, TestUidFieldMatcher) {
@@ -407,7 +408,7 @@
     // Make event without is_uid annotation.
     LogEvent event1(/*uid=*/0, /*pid=*/0);
     makeIntLogEvent(&event1, TAG_ID, 0, 1111);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1).matched);
 
     // Make event with is_uid annotation.
     LogEvent event2(/*uid=*/0, /*pid=*/0);
@@ -416,12 +417,11 @@
 
     // Event has is_uid annotation, so mapping from uid to package name occurs.
     simpleMatcher->set_atom_id(TAG_ID_2);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
 
     // Event has is_uid annotation, but uid maps to different package name.
-    simpleMatcher->mutable_field_value_matcher(0)->set_eq_string(
-            "pkg2");  // package names are normalized
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2));
+    simpleMatcher->mutable_field_value_matcher(0)->set_eq_string("Pkg2");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
 }
 
 TEST(AtomMatcherTest, TestRepeatedUidFieldMatcher) {
@@ -450,35 +450,35 @@
 
     fieldValueMatcher->set_position(Position::FIRST);
     fieldValueMatcher->set_eq_string("pkg0");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1).matched);
 
     fieldValueMatcher->set_position(Position::LAST);
     fieldValueMatcher->set_eq_string("pkg1");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1).matched);
 
     fieldValueMatcher->set_position(Position::ANY);
-    fieldValueMatcher->set_eq_string("pkg2");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+    fieldValueMatcher->set_eq_string("Pkg2");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1).matched);
 
     // is_uid annotation, mapping from uid to package name.
     LogEvent event2(/*uid=*/0, /*pid=*/0);
     makeRepeatedUidLogEvent(&event2, TAG_ID, intArray);
 
     fieldValueMatcher->set_position(Position::FIRST);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
     fieldValueMatcher->set_eq_string("pkg0");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
 
     fieldValueMatcher->set_position(Position::LAST);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
     fieldValueMatcher->set_eq_string("pkg1");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
 
     fieldValueMatcher->set_position(Position::ANY);
     fieldValueMatcher->set_eq_string("pkg");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2));
-    fieldValueMatcher->set_eq_string("pkg2");  // package names are normalized
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
+    fieldValueMatcher->set_eq_string("Pkg2");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
 }
 
 TEST(AtomMatcherTest, TestNeqAnyStringMatcher_SingleString) {
@@ -498,17 +498,17 @@
     // First string matched.
     LogEvent event1(/*uid=*/0, /*pid=*/0);
     makeStringLogEvent(&event1, TAG_ID, 0, "some value");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1).matched);
 
     // Second string matched.
     LogEvent event2(/*uid=*/0, /*pid=*/0);
     makeStringLogEvent(&event2, TAG_ID, 0, "another value");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
 
     // No strings matched.
     LogEvent event3(/*uid=*/0, /*pid=*/0);
     makeStringLogEvent(&event3, TAG_ID, 0, "foo");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3).matched);
 }
 
 TEST(AtomMatcherTest, TestNeqAnyStringMatcher_AttributionUids) {
@@ -544,33 +544,33 @@
     auto neqStringList = attributionMatcher->mutable_matches_tuple()
                                  ->mutable_field_value_matcher(0)
                                  ->mutable_neq_any_string();
-    neqStringList->add_str_value("pkg2");
-    neqStringList->add_str_value("pkg3");
+    neqStringList->add_str_value("Pkg2");
+    neqStringList->add_str_value("PkG3");
 
     auto fieldMatcher = simpleMatcher->add_field_value_matcher();
     fieldMatcher->set_field(FIELD_ID_2);
     fieldMatcher->set_eq_string("some value");
 
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     neqStringList->Clear();
     neqStringList->add_str_value("pkg1");
-    neqStringList->add_str_value("pkg3");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    neqStringList->add_str_value("PkG3");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     attributionMatcher->set_position(Position::ANY);
     neqStringList->Clear();
     neqStringList->add_str_value("maps.com");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     neqStringList->Clear();
     neqStringList->add_str_value("PkG3");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     attributionMatcher->set_position(Position::LAST);
     neqStringList->Clear();
     neqStringList->add_str_value("AID_STATSD");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 }
 
 TEST(AtomMatcherTest, TestEqAnyStringMatcher) {
@@ -613,29 +613,29 @@
     fieldMatcher->set_field(FIELD_ID_2);
     fieldMatcher->set_eq_string("some value");
 
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     attributionMatcher->set_position(Position::ANY);
     eqStringList->Clear();
     eqStringList->add_str_value("AID_STATSD");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     eqStringList->Clear();
     eqStringList->add_str_value("pkg1");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     auto normalStringField = fieldMatcher->mutable_eq_any_string();
     normalStringField->add_str_value("some value123");
     normalStringField->add_str_value("some value");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     normalStringField->Clear();
     normalStringField->add_str_value("AID_STATSD");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     eqStringList->Clear();
     eqStringList->add_str_value("maps.com");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 }
 
 TEST(AtomMatcherTest, TestBoolMatcher) {
@@ -656,19 +656,19 @@
     // Test
     keyValue1->set_eq_bool(true);
     keyValue2->set_eq_bool(false);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     keyValue1->set_eq_bool(false);
     keyValue2->set_eq_bool(false);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     keyValue1->set_eq_bool(false);
     keyValue2->set_eq_bool(true);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     keyValue1->set_eq_bool(true);
     keyValue2->set_eq_bool(true);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 }
 
 TEST(AtomMatcherTest, TestStringMatcher) {
@@ -686,7 +686,7 @@
     makeStringLogEvent(&event, TAG_ID, 0, "some value");
 
     // Test
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 }
 
 TEST(AtomMatcherTest, TestIntMatcher_EmptyRepeatedField) {
@@ -706,16 +706,16 @@
     // Match first int.
     fieldValueMatcher->set_position(Position::FIRST);
     fieldValueMatcher->set_eq_int(9);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // Match last int.
     fieldValueMatcher->set_position(Position::LAST);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // Match any int.
     fieldValueMatcher->set_position(Position::ANY);
     fieldValueMatcher->set_eq_int(13);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 }
 
 TEST(AtomMatcherTest, TestIntMatcher_RepeatedIntField) {
@@ -736,28 +736,28 @@
     fieldValueMatcher->set_field(FIELD_ID_1);
     fieldValueMatcher->set_position(Position::FIRST);
     fieldValueMatcher->set_eq_int(9);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     fieldValueMatcher->set_eq_int(21);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // Match last int.
     fieldValueMatcher->set_position(Position::LAST);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     fieldValueMatcher->set_eq_int(9);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // Match any int.
     fieldValueMatcher->set_position(Position::ANY);
     fieldValueMatcher->set_eq_int(13);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     fieldValueMatcher->set_eq_int(21);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     fieldValueMatcher->set_eq_int(9);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 }
 
 TEST(AtomMatcherTest, TestLtIntMatcher_RepeatedIntField) {
@@ -778,34 +778,34 @@
     fieldValueMatcher->set_field(FIELD_ID_1);
     fieldValueMatcher->set_position(Position::FIRST);
     fieldValueMatcher->set_lt_int(9);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     fieldValueMatcher->set_lt_int(21);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     fieldValueMatcher->set_lt_int(23);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // Match last int.
     fieldValueMatcher->set_position(Position::LAST);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     fieldValueMatcher->set_lt_int(9);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     fieldValueMatcher->set_lt_int(8);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // Match any int.
     fieldValueMatcher->set_position(Position::ANY);
     fieldValueMatcher->set_lt_int(21);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     fieldValueMatcher->set_lt_int(8);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     fieldValueMatcher->set_lt_int(23);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 }
 
 TEST(AtomMatcherTest, TestStringMatcher_RepeatedStringField) {
@@ -826,31 +826,31 @@
     fieldValueMatcher->set_field(FIELD_ID_1);
     fieldValueMatcher->set_position(Position::FIRST);
     fieldValueMatcher->set_eq_string("str2");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     fieldValueMatcher->set_eq_string("str1");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // Match last int.
     fieldValueMatcher->set_position(Position::LAST);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     fieldValueMatcher->set_eq_string("str3");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // Match any int.
     fieldValueMatcher->set_position(Position::ANY);
     fieldValueMatcher->set_eq_string("str4");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     fieldValueMatcher->set_eq_string("str1");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     fieldValueMatcher->set_eq_string("str2");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     fieldValueMatcher->set_eq_string("str3");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 }
 
 TEST(AtomMatcherTest, TestEqAnyStringMatcher_RepeatedStringField) {
@@ -871,43 +871,43 @@
     StringListMatcher* eqStringList = fieldValueMatcher->mutable_eq_any_string();
 
     fieldValueMatcher->set_position(Position::FIRST);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     fieldValueMatcher->set_position(Position::LAST);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     fieldValueMatcher->set_position(Position::ANY);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     eqStringList->add_str_value("str4");
     fieldValueMatcher->set_position(Position::FIRST);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     fieldValueMatcher->set_position(Position::LAST);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     fieldValueMatcher->set_position(Position::ANY);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     eqStringList->add_str_value("str2");
     fieldValueMatcher->set_position(Position::FIRST);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     fieldValueMatcher->set_position(Position::LAST);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     fieldValueMatcher->set_position(Position::ANY);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     eqStringList->add_str_value("str3");
     fieldValueMatcher->set_position(Position::FIRST);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     fieldValueMatcher->set_position(Position::LAST);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     fieldValueMatcher->set_position(Position::ANY);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     eqStringList->add_str_value("str1");
     fieldValueMatcher->set_position(Position::FIRST);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     fieldValueMatcher->set_position(Position::LAST);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     fieldValueMatcher->set_position(Position::ANY);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 }
 
 TEST(AtomMatcherTest, TestNeqAnyStringMatcher_RepeatedStringField) {
@@ -928,43 +928,43 @@
     StringListMatcher* neqStringList = fieldValueMatcher->mutable_neq_any_string();
 
     fieldValueMatcher->set_position(Position::FIRST);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     fieldValueMatcher->set_position(Position::LAST);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     fieldValueMatcher->set_position(Position::ANY);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     neqStringList->add_str_value("str4");
     fieldValueMatcher->set_position(Position::FIRST);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     fieldValueMatcher->set_position(Position::LAST);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     fieldValueMatcher->set_position(Position::ANY);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     neqStringList->add_str_value("str2");
     fieldValueMatcher->set_position(Position::FIRST);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     fieldValueMatcher->set_position(Position::LAST);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     fieldValueMatcher->set_position(Position::ANY);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     neqStringList->add_str_value("str3");
     fieldValueMatcher->set_position(Position::FIRST);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     fieldValueMatcher->set_position(Position::LAST);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     fieldValueMatcher->set_position(Position::ANY);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     neqStringList->add_str_value("str1");
     fieldValueMatcher->set_position(Position::FIRST);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     fieldValueMatcher->set_position(Position::LAST);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     fieldValueMatcher->set_position(Position::ANY);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 }
 
 TEST(AtomMatcherTest, TestMultiFieldsMatcher) {
@@ -985,15 +985,15 @@
     // Test
     keyValue1->set_eq_int(2);
     keyValue2->set_eq_int(3);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     keyValue1->set_eq_int(2);
     keyValue2->set_eq_int(4);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     keyValue1->set_eq_int(4);
     keyValue2->set_eq_int(3);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 }
 
 TEST(AtomMatcherTest, TestIntComparisonMatcher) {
@@ -1014,43 +1014,43 @@
 
     // eq_int
     keyValue->set_eq_int(10);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     keyValue->set_eq_int(11);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     keyValue->set_eq_int(12);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // lt_int
     keyValue->set_lt_int(10);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     keyValue->set_lt_int(11);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     keyValue->set_lt_int(12);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // lte_int
     keyValue->set_lte_int(10);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     keyValue->set_lte_int(11);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     keyValue->set_lte_int(12);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // gt_int
     keyValue->set_gt_int(10);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     keyValue->set_gt_int(11);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     keyValue->set_gt_int(12);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // gte_int
     keyValue->set_gte_int(10);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     keyValue->set_gte_int(11);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
     keyValue->set_gte_int(12);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 }
 
 TEST(AtomMatcherTest, TestFloatComparisonMatcher) {
@@ -1066,20 +1066,20 @@
     LogEvent event1(/*uid=*/0, /*pid=*/0);
     makeFloatLogEvent(&event1, TAG_ID, 0, 10.1f);
     keyValue->set_lt_float(10.0);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1).matched);
 
     LogEvent event2(/*uid=*/0, /*pid=*/0);
     makeFloatLogEvent(&event2, TAG_ID, 0, 9.9f);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
 
     LogEvent event3(/*uid=*/0, /*pid=*/0);
     makeFloatLogEvent(&event3, TAG_ID, 0, 10.1f);
     keyValue->set_gt_float(10.0);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3).matched);
 
     LogEvent event4(/*uid=*/0, /*pid=*/0);
     makeFloatLogEvent(&event4, TAG_ID, 0, 9.9f);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4).matched);
 }
 
 // Helper for the composite matchers.
@@ -1226,17 +1226,17 @@
     // Event without is_uid annotation.
     LogEvent event1(/*uid=*/0, /*pid=*/0);
     makeIntLogEvent(&event1, TAG_ID, 0, 1111);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1).matched);
 
     // Event where mapping from uid to package name occurs.
     LogEvent event2(/*uid=*/0, /*pid=*/0);
     makeIntWithBoolAnnotationLogEvent(&event2, TAG_ID, 1111, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
 
     // Event where uid maps to package names that don't fit wildcard pattern.
     LogEvent event3(/*uid=*/0, /*pid=*/0);
     makeIntWithBoolAnnotationLogEvent(&event3, TAG_ID, 3333, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event3));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event3).matched);
 
     // Update matcher to match one AID
     simpleMatcher->mutable_field_value_matcher(0)->set_eq_wildcard_string(
@@ -1245,12 +1245,12 @@
     // Event where mapping from uid to aid doesn't fit wildcard pattern.
     LogEvent event4(/*uid=*/0, /*pid=*/0);
     makeIntWithBoolAnnotationLogEvent(&event4, TAG_ID, 1005, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4).matched);
 
     // Event where mapping from uid to aid does fit wildcard pattern.
     LogEvent event5(/*uid=*/0, /*pid=*/0);
     makeIntWithBoolAnnotationLogEvent(&event5, TAG_ID, 1000, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event5));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event5).matched);
 
     // Update matcher to match multiple AIDs
     simpleMatcher->mutable_field_value_matcher(0)->set_eq_wildcard_string("AID_SDCARD_*");
@@ -1258,16 +1258,16 @@
     // Event where mapping from uid to aid doesn't fit wildcard pattern.
     LogEvent event6(/*uid=*/0, /*pid=*/0);
     makeIntWithBoolAnnotationLogEvent(&event6, TAG_ID, 1036, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event6));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event6).matched);
 
     // Event where mapping from uid to aid does fit wildcard pattern.
     LogEvent event7(/*uid=*/0, /*pid=*/0);
     makeIntWithBoolAnnotationLogEvent(&event7, TAG_ID, 1034, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event7));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event7).matched);
 
     LogEvent event8(/*uid=*/0, /*pid=*/0);
     makeIntWithBoolAnnotationLogEvent(&event8, TAG_ID, 1035, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event8));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event8).matched);
 }
 
 TEST(AtomMatcherTest, TestWildcardStringMatcher) {
@@ -1284,55 +1284,57 @@
 
     LogEvent event1(/*uid=*/0, /*pid=*/0);
     makeStringLogEvent(&event1, TAG_ID, 0, "test.string:test_0");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event1));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event1).matched);
 
     LogEvent event2(/*uid=*/0, /*pid=*/0);
     makeStringLogEvent(&event2, TAG_ID, 0, "test.string:test_19");
-    EXPECT_FALSE(
-            matchesSimple(uidMap, *simpleMatcher, event2));  // extra character at end of string
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event2)
+                         .matched);  // extra character at end of string
 
     LogEvent event3(/*uid=*/0, /*pid=*/0);
     makeStringLogEvent(&event3, TAG_ID, 0, "extra.test.string:test_1");
     EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher,
-                               event3));  // extra characters at beginning of string
+                               event3)
+                         .matched);  // extra characters at beginning of string
 
     LogEvent event4(/*uid=*/0, /*pid=*/0);
     makeStringLogEvent(&event4, TAG_ID, 0, "test.string:test_");
     EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher,
-                               event4));  // missing character from 0-9 at end of string
+                               event4)
+                         .matched);  // missing character from 0-9 at end of string
 
     LogEvent event5(/*uid=*/0, /*pid=*/0);
     makeStringLogEvent(&event5, TAG_ID, 0, "est.string:test_1");
-    EXPECT_FALSE(
-            matchesSimple(uidMap, *simpleMatcher, event5));  // missing 't' at beginning of string
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event5)
+                         .matched);  // missing 't' at beginning of string
 
     LogEvent event6(/*uid=*/0, /*pid=*/0);
     makeStringLogEvent(&event6, TAG_ID, 0, "test.string:test_1extra");
-    EXPECT_FALSE(
-            matchesSimple(uidMap, *simpleMatcher, event6));  // extra characters at end of string
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event6)
+                         .matched);  // extra characters at end of string
 
     // Matches any string that contains "test.string:test_" + any extra characters before or after
     fieldValueMatcher->set_eq_wildcard_string("*test.string:test_*");
 
     LogEvent event7(/*uid=*/0, /*pid=*/0);
     makeStringLogEvent(&event7, TAG_ID, 0, "test.string:test_");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event7));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event7).matched);
 
     LogEvent event8(/*uid=*/0, /*pid=*/0);
     makeStringLogEvent(&event8, TAG_ID, 0, "extra.test.string:test_");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event8));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event8).matched);
 
     LogEvent event9(/*uid=*/0, /*pid=*/0);
     makeStringLogEvent(&event9, TAG_ID, 0, "test.string:test_extra");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event9));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event9).matched);
 
     LogEvent event10(/*uid=*/0, /*pid=*/0);
     makeStringLogEvent(&event10, TAG_ID, 0, "est.string:test_");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event10));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event10).matched);
 
     LogEvent event11(/*uid=*/0, /*pid=*/0);
     makeStringLogEvent(&event11, TAG_ID, 0, "test.string:test");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event11));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event11).matched);
 }
 
 TEST(AtomMatcherTest, TestEqAnyWildcardStringMatcher) {
@@ -1352,17 +1354,17 @@
     // First wildcard pattern matched.
     LogEvent event1(/*uid=*/0, /*pid=*/0);
     makeStringLogEvent(&event1, TAG_ID, 0, "first_string_1");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event1));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event1).matched);
 
     // Second wildcard pattern matched.
     LogEvent event2(/*uid=*/0, /*pid=*/0);
     makeStringLogEvent(&event2, TAG_ID, 0, "second_string_1");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
 
     // No wildcard patterns matched.
     LogEvent event3(/*uid=*/0, /*pid=*/0);
     makeStringLogEvent(&event3, TAG_ID, 0, "third_string_1");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event3));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event3).matched);
 }
 
 TEST(AtomMatcherTest, TestNeqAnyWildcardStringMatcher) {
@@ -1389,27 +1391,27 @@
 
     // First tag is not matched. neq string list {"tag"}
     neqWildcardStrList->add_str_value("tag");
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // First tag is matched. neq string list {"tag", "location_*"}
     neqWildcardStrList->add_str_value("location_*");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // Match last tag.
     attributionMatcher->set_position(Position::LAST);
 
     // Last tag is not matched. neq string list {"tag", "location_*"}
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // Last tag is matched. neq string list {"tag", "location_*", "location*"}
     neqWildcardStrList->add_str_value("location*");
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // Match any tag.
     attributionMatcher->set_position(Position::ANY);
 
     // All tags are matched. neq string list {"tag", "location_*", "location*"}
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // Set up another log event.
     std::vector<string> attributionTags2 = {"location_1", "location", "string"};
@@ -1417,7 +1419,7 @@
     makeAttributionLogEvent(&event2, TAG_ID, 0, attributionUids, attributionTags2, "some value");
 
     // Tag "string" is not matched. neq string list {"tag", "location_*", "location*"}
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
 }
 
 TEST(AtomMatcherTest, TestEqAnyIntMatcher) {
@@ -1437,17 +1439,17 @@
     // First int matched.
     LogEvent event1(/*uid=*/0, /*pid=*/0);
     makeIntLogEvent(&event1, TAG_ID, 0, 3);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event1));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event1).matched);
 
     // Second int matched.
     LogEvent event2(/*uid=*/0, /*pid=*/0);
     makeIntLogEvent(&event2, TAG_ID, 0, 5);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2).matched);
 
     // No ints matched.
     LogEvent event3(/*uid=*/0, /*pid=*/0);
     makeIntLogEvent(&event3, TAG_ID, 0, 4);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event3));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event3).matched);
 }
 
 TEST(AtomMatcherTest, TestNeqAnyIntMatcher) {
@@ -1473,31 +1475,593 @@
 
     // First uid is not matched. neq int list {4444}
     neqIntList->add_int_value(4444);
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // First uid is matched. neq int list {4444, 1111}
     neqIntList->add_int_value(1111);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // Match last uid.
     attributionMatcher->set_position(Position::LAST);
 
     // Last uid is not matched. neq int list {4444, 1111}
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // Last uid is matched. neq int list {4444, 1111, 3333}
     neqIntList->add_int_value(3333);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // Match any uid.
     attributionMatcher->set_position(Position::ANY);
 
     // Uid 2222 is not matched. neq int list {4444, 1111, 3333}
-    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event).matched);
 
     // All uids are matched. neq int list {4444, 1111, 3333, 2222}
     neqIntList->add_int_value(2222);
-    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event).matched);
+}
+
+TEST(AtomMatcherTest, TestStringReplaceRoot) {
+    sp<UidMap> uidMap = new UidMap();
+
+    // Set up the log event.
+    std::vector<int> attributionUids = {1111, 2222, 3333};
+    std::vector<string> attributionTags = {"location1", "location2", "location3"};
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value123");
+
+    // Set up the matcher. Replace second field.
+    AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+    FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(FIELD_ID_2);
+    StringReplacer* stringReplacer = fvm->mutable_replace_string();
+    stringReplacer->set_regex(R"([0-9]+$)");  // match trailing digits, example "42" in "foo42".
+    stringReplacer->set_replacement("");
+
+    const auto [hasMatched, transformedEvent] =
+            matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+    EXPECT_TRUE(hasMatched);
+    ASSERT_NE(transformedEvent, nullptr);
+
+    const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+    ASSERT_EQ(fieldValues.size(), 7);
+    EXPECT_EQ(fieldValues[0].mValue.int_value, 1111);
+    EXPECT_EQ(fieldValues[1].mValue.str_value, "location1");
+    EXPECT_EQ(fieldValues[2].mValue.int_value, 2222);
+    EXPECT_EQ(fieldValues[3].mValue.str_value, "location2");
+    EXPECT_EQ(fieldValues[4].mValue.int_value, 3333);
+    EXPECT_EQ(fieldValues[5].mValue.str_value, "location3");
+    EXPECT_EQ(fieldValues[6].mValue.str_value, "some value");
+}
+
+TEST(AtomMatcherTest, TestStringReplaceAttributionTagFirst) {
+    sp<UidMap> uidMap = new UidMap();
+
+    // Set up the log event.
+    std::vector<int> attributionUids = {1111, 2222, 3333};
+    std::vector<string> attributionTags = {"location1", "location2", "location3"};
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value123");
+
+    // Set up the matcher. Replace first attribution tag.
+    AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+    FieldValueMatcher* attributionFvm =
+            matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    attributionFvm->set_field(FIELD_ID_1);
+    attributionFvm->set_position(Position::FIRST);
+    FieldValueMatcher* attributionTagFvm =
+            attributionFvm->mutable_matches_tuple()->add_field_value_matcher();
+    attributionTagFvm->set_field(ATTRIBUTION_TAG_FIELD_ID);
+    StringReplacer* stringReplacer = attributionTagFvm->mutable_replace_string();
+    stringReplacer->set_regex(R"([0-9]+$)");  // match trailing digits, example "42" in "foo42".
+    stringReplacer->set_replacement("");
+
+    const auto [hasMatched, transformedEvent] =
+            matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+    EXPECT_TRUE(hasMatched);
+    ASSERT_NE(transformedEvent, nullptr);
+    const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+    ASSERT_EQ(fieldValues.size(), 7);
+    EXPECT_EQ(fieldValues[0].mValue.int_value, 1111);
+    EXPECT_EQ(fieldValues[1].mValue.str_value, "location");
+    EXPECT_EQ(fieldValues[2].mValue.int_value, 2222);
+    EXPECT_EQ(fieldValues[3].mValue.str_value, "location2");
+    EXPECT_EQ(fieldValues[4].mValue.int_value, 3333);
+    EXPECT_EQ(fieldValues[5].mValue.str_value, "location3");
+    EXPECT_EQ(fieldValues[6].mValue.str_value, "some value123");
+}
+
+TEST(AtomMatcherTest, TestStringReplaceAttributionTagLast) {
+    sp<UidMap> uidMap = new UidMap();
+
+    // Set up the log event.
+    std::vector<int> attributionUids = {1111, 2222, 3333};
+    std::vector<string> attributionTags = {"location1", "location2", "location3"};
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value123");
+
+    // Set up the matcher. Replace last attribution tag.
+    AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+    FieldValueMatcher* attributionFvm =
+            matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    attributionFvm->set_field(FIELD_ID_1);
+    attributionFvm->set_position(Position::LAST);
+    FieldValueMatcher* attributionTagFvm =
+            attributionFvm->mutable_matches_tuple()->add_field_value_matcher();
+    attributionTagFvm->set_field(ATTRIBUTION_TAG_FIELD_ID);
+    StringReplacer* stringReplacer = attributionTagFvm->mutable_replace_string();
+    stringReplacer->set_regex(R"([0-9]+$)");  // match trailing digits, example "42" in "foo42".
+    stringReplacer->set_replacement("");
+
+    const auto [hasMatched, transformedEvent] =
+            matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+    EXPECT_TRUE(hasMatched);
+    ASSERT_NE(transformedEvent, nullptr);
+
+    const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+    ASSERT_EQ(fieldValues.size(), 7);
+    EXPECT_EQ(fieldValues[0].mValue.int_value, 1111);
+    EXPECT_EQ(fieldValues[1].mValue.str_value, "location1");
+    EXPECT_EQ(fieldValues[2].mValue.int_value, 2222);
+    EXPECT_EQ(fieldValues[3].mValue.str_value, "location2");
+    EXPECT_EQ(fieldValues[4].mValue.int_value, 3333);
+    EXPECT_EQ(fieldValues[5].mValue.str_value, "location");
+    EXPECT_EQ(fieldValues[6].mValue.str_value, "some value123");
+}
+
+TEST(AtomMatcherTest, TestStringReplaceAttributionTagAll) {
+    sp<UidMap> uidMap = new UidMap();
+
+    // Set up the log event.
+    std::vector<int> attributionUids = {1111, 2222, 3333};
+    std::vector<string> attributionTags = {"location1", "location2", "location3"};
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value123");
+
+    // Set up the matcher. Replace all attribution tags.
+    AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+    FieldValueMatcher* attributionFvm =
+            matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    attributionFvm->set_field(FIELD_ID_1);
+    attributionFvm->set_position(Position::ALL);
+    FieldValueMatcher* attributionTagFvm =
+            attributionFvm->mutable_matches_tuple()->add_field_value_matcher();
+    attributionTagFvm->set_field(ATTRIBUTION_TAG_FIELD_ID);
+    StringReplacer* stringReplacer = attributionTagFvm->mutable_replace_string();
+    stringReplacer->set_regex(R"([0-9]+$)");  // match trailing digits, example "42" in "foo42".
+    stringReplacer->set_replacement("");
+
+    const auto [hasMatched, transformedEvent] =
+            matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+    EXPECT_TRUE(hasMatched);
+    ASSERT_NE(transformedEvent, nullptr);
+
+    const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+    ASSERT_EQ(fieldValues.size(), 7);
+    EXPECT_EQ(fieldValues[0].mValue.int_value, 1111);
+    EXPECT_EQ(fieldValues[1].mValue.str_value, "location");
+    EXPECT_EQ(fieldValues[2].mValue.int_value, 2222);
+    EXPECT_EQ(fieldValues[3].mValue.str_value, "location");
+    EXPECT_EQ(fieldValues[4].mValue.int_value, 3333);
+    EXPECT_EQ(fieldValues[5].mValue.str_value, "location");
+    EXPECT_EQ(fieldValues[6].mValue.str_value, "some value123");
+}
+
+TEST(AtomMatcherTest, TestStringReplaceNestedAllWithMultipleNestedStringFields) {
+    sp<UidMap> uidMap = new UidMap();
+
+    // Set up the log event.
+    std::vector<int> attributionUids = {1111, 2222, 3333};
+    std::vector<string> attributionTags = {"location1", "location2", "location3"};
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value123");
+
+    // Manually change uid fields to string fields, as there is no direct way to create a
+    // LogEvent with multiple nested string fields.
+    (*event.getMutableValues())[0].mValue = android::os::statsd::Value("abc1");
+    (*event.getMutableValues())[2].mValue = android::os::statsd::Value("xyz2");
+    (*event.getMutableValues())[4].mValue = android::os::statsd::Value("abc3");
+
+    // Set up the matcher. Replace all attribution tags.
+    AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+    FieldValueMatcher* attributionFvm =
+            matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    attributionFvm->set_field(FIELD_ID_1);
+    attributionFvm->set_position(Position::ALL);
+    FieldValueMatcher* attributionTagFvm =
+            attributionFvm->mutable_matches_tuple()->add_field_value_matcher();
+    attributionTagFvm->set_field(ATTRIBUTION_TAG_FIELD_ID);
+    StringReplacer* stringReplacer = attributionTagFvm->mutable_replace_string();
+    stringReplacer->set_regex(R"([0-9]+$)");  // match trailing digits, example "42" in "foo42".
+    stringReplacer->set_replacement("");
+
+    const auto [hasMatched, transformedEvent] =
+            matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+    EXPECT_TRUE(hasMatched);
+    ASSERT_NE(transformedEvent, nullptr);
+
+    const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+    ASSERT_EQ(fieldValues.size(), 7);
+    EXPECT_EQ(fieldValues[0].mValue.str_value, "abc1");
+    EXPECT_EQ(fieldValues[1].mValue.str_value, "location");
+    EXPECT_EQ(fieldValues[2].mValue.str_value, "xyz2");
+    EXPECT_EQ(fieldValues[3].mValue.str_value, "location");
+    EXPECT_EQ(fieldValues[4].mValue.str_value, "abc3");
+    EXPECT_EQ(fieldValues[5].mValue.str_value, "location");
+    EXPECT_EQ(fieldValues[6].mValue.str_value, "some value123");
+}
+
+TEST(AtomMatcherTest, TestStringReplaceRootOnMatchedField) {
+    sp<UidMap> uidMap = new UidMap();
+
+    // Set up the matcher. Replace second field and match on replaced field.
+    AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+    FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(FIELD_ID_2);
+    fvm->set_eq_string("bar");
+    StringReplacer* stringReplacer = fvm->mutable_replace_string();
+    stringReplacer->set_regex(R"([0-9]+$)");  // match trailing digits, example "42" in "foo42".
+    stringReplacer->set_replacement("");
+
+    // Set up the log event.
+    std::vector<int> attributionUids = {1111, 2222, 3333};
+    std::vector<string> attributionTags = {"location1", "location2", "location3"};
+    {
+        LogEvent event(/*uid=*/0, /*pid=*/0);
+        makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags,
+                                "some value123");
+
+        EXPECT_FALSE(matchesSimple(uidMap, matcher.simple_atom_matcher(), event).matched);
+    }
+
+    {
+        LogEvent event(/*uid=*/0, /*pid=*/0);
+        makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "bar123");
+        const auto [hasMatched, transformedEvent] =
+                matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+        EXPECT_TRUE(hasMatched);
+        ASSERT_NE(transformedEvent, nullptr);
+        const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+        ASSERT_EQ(fieldValues.size(), 7);
+        EXPECT_EQ(fieldValues[0].mValue.int_value, 1111);
+        EXPECT_EQ(fieldValues[1].mValue.str_value, "location1");
+        EXPECT_EQ(fieldValues[2].mValue.int_value, 2222);
+        EXPECT_EQ(fieldValues[3].mValue.str_value, "location2");
+        EXPECT_EQ(fieldValues[4].mValue.int_value, 3333);
+        EXPECT_EQ(fieldValues[5].mValue.str_value, "location3");
+        EXPECT_EQ(fieldValues[6].mValue.str_value, "bar");
+    }
+}
+
+TEST(AtomMatcherTest, TestStringReplaceAttributionTagFirstOnMatchedField) {
+    sp<UidMap> uidMap = new UidMap();
+
+    // Set up the matcher. Replace first attribution tag and match on that tag.
+    AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+    FieldValueMatcher* attributionFvm =
+            matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    attributionFvm->set_field(FIELD_ID_1);
+    attributionFvm->set_position(Position::FIRST);
+    FieldValueMatcher* attributionTagFvm =
+            attributionFvm->mutable_matches_tuple()->add_field_value_matcher();
+    attributionTagFvm->set_field(ATTRIBUTION_TAG_FIELD_ID);
+    attributionTagFvm->set_eq_string("bar");
+    StringReplacer* stringReplacer = attributionTagFvm->mutable_replace_string();
+    stringReplacer->set_regex(R"([0-9]+$)");  // match trailing digits, example "42" in "foo42".
+    stringReplacer->set_replacement("");
+
+    // Set up the log event.
+    std::vector<int> attributionUids = {1111, 2222, 3333};
+    std::vector<string> attributionTags = {"location1", "location2", "location3"};
+    {
+        LogEvent event(/*uid=*/0, /*pid=*/0);
+        makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags,
+                                "some value123");
+
+        EXPECT_FALSE(matchesSimple(uidMap, matcher.simple_atom_matcher(), event).matched);
+    }
+
+    {
+        LogEvent event(/*uid=*/0, /*pid=*/0);
+        attributionTags = {"bar1", "bar2", "bar3"};
+        makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "bar123");
+        const auto [hasMatched, transformedEvent] =
+                matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+        EXPECT_TRUE(hasMatched);
+        ASSERT_NE(transformedEvent, nullptr);
+        const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+        ASSERT_EQ(fieldValues.size(), 7);
+        EXPECT_EQ(fieldValues[0].mValue.int_value, 1111);
+        EXPECT_EQ(fieldValues[1].mValue.str_value, "bar");
+        EXPECT_EQ(fieldValues[2].mValue.int_value, 2222);
+        EXPECT_EQ(fieldValues[3].mValue.str_value, "bar2");
+        EXPECT_EQ(fieldValues[4].mValue.int_value, 3333);
+        EXPECT_EQ(fieldValues[5].mValue.str_value, "bar3");
+        EXPECT_EQ(fieldValues[6].mValue.str_value, "bar123");
+    }
+}
+
+TEST(AtomMatcherTest, TestStringReplaceAttributionTagLastOnMatchedField) {
+    sp<UidMap> uidMap = new UidMap();
+
+    // Set up the matcher. Replace last attribution tag and match on that tag.
+    AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+    FieldValueMatcher* attributionFvm =
+            matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    attributionFvm->set_field(FIELD_ID_1);
+    attributionFvm->set_position(Position::LAST);
+    FieldValueMatcher* attributionTagFvm =
+            attributionFvm->mutable_matches_tuple()->add_field_value_matcher();
+    attributionTagFvm->set_field(ATTRIBUTION_TAG_FIELD_ID);
+    attributionTagFvm->set_eq_string("bar");
+    StringReplacer* stringReplacer = attributionTagFvm->mutable_replace_string();
+    stringReplacer->set_regex(R"([0-9]+$)");  // match trailing digits, example "42" in "foo42".
+    stringReplacer->set_replacement("");
+
+    // Set up the log event.
+    std::vector<int> attributionUids = {1111, 2222, 3333};
+    std::vector<string> attributionTags = {"location1", "location2", "location3"};
+    {
+        LogEvent event(/*uid=*/0, /*pid=*/0);
+        makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags,
+                                "some value123");
+
+        EXPECT_FALSE(matchesSimple(uidMap, matcher.simple_atom_matcher(), event).matched);
+    }
+
+    {
+        LogEvent event(/*uid=*/0, /*pid=*/0);
+        attributionTags = {"bar1", "bar2", "bar3"};
+        makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "bar123");
+        const auto [hasMatched, transformedEvent] =
+                matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+        EXPECT_TRUE(hasMatched);
+        ASSERT_NE(transformedEvent, nullptr);
+        const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+        ASSERT_EQ(fieldValues.size(), 7);
+        EXPECT_EQ(fieldValues[0].mValue.int_value, 1111);
+        EXPECT_EQ(fieldValues[1].mValue.str_value, "bar1");
+        EXPECT_EQ(fieldValues[2].mValue.int_value, 2222);
+        EXPECT_EQ(fieldValues[3].mValue.str_value, "bar2");
+        EXPECT_EQ(fieldValues[4].mValue.int_value, 3333);
+        EXPECT_EQ(fieldValues[5].mValue.str_value, "bar");
+        EXPECT_EQ(fieldValues[6].mValue.str_value, "bar123");
+    }
+}
+
+TEST(AtomMatcherTest, TestStringReplaceAttributionTagAnyOnMatchedField) {
+    sp<UidMap> uidMap = new UidMap();
+
+    // Set up the matcher. Replace all attribution tags but match on any tag.
+    AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+    FieldValueMatcher* attributionFvm =
+            matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    attributionFvm->set_field(FIELD_ID_1);
+    attributionFvm->set_position(Position::ANY);
+    FieldValueMatcher* attributionTagFvm =
+            attributionFvm->mutable_matches_tuple()->add_field_value_matcher();
+    attributionTagFvm->set_field(ATTRIBUTION_TAG_FIELD_ID);
+    attributionTagFvm->set_eq_string("bar");
+    StringReplacer* stringReplacer = attributionTagFvm->mutable_replace_string();
+    stringReplacer->set_regex(R"([0-9]+$)");  // match trailing digits, example "42" in "foo42".
+    stringReplacer->set_replacement("");
+
+    // Set up the log event.
+    std::vector<int> attributionUids = {1111, 2222, 3333};
+    std::vector<string> attributionTags = {"location1", "location2", "location3"};
+    {
+        LogEvent event(/*uid=*/0, /*pid=*/0);
+        makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags,
+                                "some value123");
+
+        EXPECT_FALSE(matchesSimple(uidMap, matcher.simple_atom_matcher(), event).matched);
+    }
+
+    {
+        LogEvent event(/*uid=*/0, /*pid=*/0);
+        attributionTags = {"foo1", "bar2", "foo3"};
+        makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "bar123");
+        const auto [hasMatched, transformedEvent] =
+                matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+        EXPECT_TRUE(hasMatched);
+        ASSERT_NE(transformedEvent, nullptr);
+        const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+        ASSERT_EQ(fieldValues.size(), 7);
+        EXPECT_EQ(fieldValues[0].mValue.int_value, 1111);
+        EXPECT_EQ(fieldValues[1].mValue.str_value, "foo");
+        EXPECT_EQ(fieldValues[2].mValue.int_value, 2222);
+        EXPECT_EQ(fieldValues[3].mValue.str_value, "bar");
+        EXPECT_EQ(fieldValues[4].mValue.int_value, 3333);
+        EXPECT_EQ(fieldValues[5].mValue.str_value, "foo");
+        EXPECT_EQ(fieldValues[6].mValue.str_value, "bar123");
+    }
+}
+
+TEST(AtomMatcherTest, TestStringReplaceAttributionTagAnyAndRootOnMatchedFields) {
+    sp<UidMap> uidMap = new UidMap();
+
+    // Set up the matcher. Replace all attribution tags but match on any tag.
+    AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+    FieldValueMatcher* attributionFvm =
+            matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    attributionFvm->set_field(FIELD_ID_1);
+    attributionFvm->set_position(Position::ANY);
+    FieldValueMatcher* attributionTagFvm =
+            attributionFvm->mutable_matches_tuple()->add_field_value_matcher();
+    attributionTagFvm->set_field(ATTRIBUTION_TAG_FIELD_ID);
+    attributionTagFvm->set_eq_string("bar");
+    StringReplacer* stringReplacer = attributionTagFvm->mutable_replace_string();
+    stringReplacer->set_regex(R"([0-9]+$)");  // match trailing digits, example "42" in "foo42".
+    stringReplacer->set_replacement("");
+    FieldValueMatcher* rootFvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    rootFvm->set_field(FIELD_ID_2);
+    rootFvm->set_eq_string("blah");
+    stringReplacer = rootFvm->mutable_replace_string();
+    stringReplacer->set_regex(R"([0-9]+$)");  // match trailing digits, example "42" in "foo42".
+    stringReplacer->set_replacement("");
+
+    {
+        LogEvent event(/*uid=*/0, /*pid=*/0);
+        makeAttributionLogEvent(&event, TAG_ID, 0, {1111, 2222, 3333} /* uids */,
+                                {"location1", "location2", "location3"} /* tags */,
+                                "some value123" /* name */);
+
+        EXPECT_FALSE(matchesSimple(uidMap, matcher.simple_atom_matcher(), event).matched);
+    }
+
+    {
+        LogEvent event(/*uid=*/0, /*pid=*/0);
+        makeAttributionLogEvent(&event, TAG_ID, 0, {1111, 2222, 3333} /* uids */,
+                                {"foo1", "bar2", "foo3"} /* tags */, "bar123" /* name */);
+        EXPECT_FALSE(matchesSimple(uidMap, matcher.simple_atom_matcher(), event).matched);
+    }
+
+    {
+        LogEvent event(/*uid=*/0, /*pid=*/0);
+        makeAttributionLogEvent(&event, TAG_ID, 0, {1111, 2222, 3333} /* uids */,
+                                {"foo1", "bar2", "foo3"} /* tags */, "blah123" /* name */);
+        const auto [hasMatched, transformedEvent] =
+                matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+        EXPECT_TRUE(hasMatched);
+        ASSERT_NE(transformedEvent, nullptr);
+        const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+        ASSERT_EQ(fieldValues.size(), 7);
+        EXPECT_EQ(fieldValues[0].mValue.int_value, 1111);
+        EXPECT_EQ(fieldValues[1].mValue.str_value, "foo");
+        EXPECT_EQ(fieldValues[2].mValue.int_value, 2222);
+        EXPECT_EQ(fieldValues[3].mValue.str_value, "bar");
+        EXPECT_EQ(fieldValues[4].mValue.int_value, 3333);
+        EXPECT_EQ(fieldValues[5].mValue.str_value, "foo");
+        EXPECT_EQ(fieldValues[6].mValue.str_value, "blah");
+    }
+}
+
+TEST(AtomMatcherTest, TestStringReplaceAttributionTagAnyWithAttributionUidValueMatcher) {
+    sp<UidMap> uidMap = new UidMap();
+
+    // Set up the matcher. Replace all attribution tags but match on any uid and tag.
+    AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+    FieldValueMatcher* attributionFvm =
+            matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    attributionFvm->set_field(FIELD_ID_1);
+    attributionFvm->set_position(Position::ANY);
+    FieldValueMatcher* attributionUidFvm =
+            attributionFvm->mutable_matches_tuple()->add_field_value_matcher();
+    attributionUidFvm->set_field(ATTRIBUTION_UID_FIELD_ID);
+    attributionUidFvm->set_eq_int(2222);
+    FieldValueMatcher* attributionTagFvm =
+            attributionFvm->mutable_matches_tuple()->add_field_value_matcher();
+    attributionTagFvm->set_field(ATTRIBUTION_TAG_FIELD_ID);
+    attributionTagFvm->set_eq_string("bar");
+    StringReplacer* stringReplacer = attributionTagFvm->mutable_replace_string();
+    stringReplacer->set_regex(R"([0-9]+$)");  // match trailing digits, example "42" in "foo42".
+    stringReplacer->set_replacement("");
+
+    {
+        LogEvent event(/*uid=*/0, /*pid=*/0);
+        makeAttributionLogEvent(&event, TAG_ID, 0, {1111, 2222, 3333} /* uids */,
+                                {"location1", "location2", "location3"} /* tags */,
+                                "some value123" /* name */);
+
+        EXPECT_FALSE(matchesSimple(uidMap, matcher.simple_atom_matcher(), event).matched);
+    }
+
+    {
+        LogEvent event(/*uid=*/0, /*pid=*/0);
+        makeAttributionLogEvent(&event, TAG_ID, 0, {1111, 3223, 3333} /* uids */,
+                                {"foo1", "bar2", "foo3"} /* tags */, "bar123" /* name */);
+        EXPECT_FALSE(matchesSimple(uidMap, matcher.simple_atom_matcher(), event).matched);
+    }
+
+    {
+        LogEvent event(/*uid=*/0, /*pid=*/0);
+        makeAttributionLogEvent(&event, TAG_ID, 0, {1111, 2222, 3333} /* uids */,
+                                {"foo1", "bar2", "foo3"} /* tags */, "bar123" /* name */);
+        const auto [hasMatched, transformedEvent] =
+                matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+        EXPECT_TRUE(hasMatched);
+        ASSERT_NE(transformedEvent, nullptr);
+        const vector<FieldValue>& fieldValues = transformedEvent->getValues();
+        ASSERT_EQ(fieldValues.size(), 7);
+        EXPECT_EQ(fieldValues[0].mValue.int_value, 1111);
+        EXPECT_EQ(fieldValues[1].mValue.str_value, "foo");
+        EXPECT_EQ(fieldValues[2].mValue.int_value, 2222);
+        EXPECT_EQ(fieldValues[3].mValue.str_value, "bar");
+        EXPECT_EQ(fieldValues[4].mValue.int_value, 3333);
+        EXPECT_EQ(fieldValues[5].mValue.str_value, "foo");
+        EXPECT_EQ(fieldValues[6].mValue.str_value, "bar123");
+    }
+}
+
+TEST(AtomMatcherTest, TestStringReplaceBadRegex) {
+    sp<UidMap> uidMap = new UidMap();
+
+    // Set up the log event.
+    std::vector<int> attributionUids = {1111, 2222, 3333};
+    std::vector<string> attributionTags = {"location1", "location2", "location3"};
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value123");
+
+    // Set up the matcher. Replace second field.
+    AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+    FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(FIELD_ID_2);
+    StringReplacer* stringReplacer = fvm->mutable_replace_string();
+    stringReplacer->set_regex(
+            R"(*[0-9]+$)");  // bad regex: asterisk not preceded by any expression.
+    stringReplacer->set_replacement("");
+
+    const auto [hasMatched, transformedEvent] =
+            matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+    EXPECT_TRUE(hasMatched);
+    ASSERT_EQ(transformedEvent, nullptr);
+}
+
+TEST(AtomMatcherTest, TestStringReplaceRegexWithSubgroup) {
+    sp<UidMap> uidMap = new UidMap();
+
+    // Set up the log event.
+    std::vector<int> attributionUids = {1111, 2222, 3333};
+    std::vector<string> attributionTags = {"location1", "location2", "location3"};
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value123");
+
+    // Set up the matcher. Replace second field.
+    AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+    FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(FIELD_ID_2);
+    StringReplacer* stringReplacer = fvm->mutable_replace_string();
+    stringReplacer->set_regex(R"(([a-z]+)[0-9]+$)");  // "([a-z]+)" is a subgroup.
+    stringReplacer->set_replacement("");
+
+    const auto [hasMatched, transformedEvent] =
+            matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+    EXPECT_TRUE(hasMatched);
+    ASSERT_EQ(transformedEvent, nullptr);
+}
+
+TEST(AtomMatcherTest, TestStringReplaceNoop) {
+    sp<UidMap> uidMap = new UidMap();
+
+    // Set up the log event.
+    std::vector<int> attributionUids = {1111, 2222, 3333};
+    std::vector<string> attributionTags = {"location1", "location2", "location3"};
+    LogEvent event(/*uid=*/0, /*pid=*/0);
+    makeAttributionLogEvent(&event, TAG_ID, 0, attributionUids, attributionTags, "some value123");
+
+    // Set up the matcher. Replace second field.
+    AtomMatcher matcher = CreateSimpleAtomMatcher("matcher", TAG_ID);
+    FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(FIELD_ID_2);
+    StringReplacer* stringReplacer = fvm->mutable_replace_string();
+    stringReplacer->set_regex(R"(this_pattern_should_not_match)");
+    stringReplacer->set_replacement("");
+
+    const auto [hasMatched, transformedEvent] =
+            matchesSimple(uidMap, matcher.simple_atom_matcher(), event);
+    EXPECT_TRUE(hasMatched);
+    ASSERT_EQ(transformedEvent, nullptr);
 }
 
 #else
diff --git a/statsd/tests/SocketListener_test.cpp b/statsd/tests/SocketListener_test.cpp
index 90e5d3e..3636508 100644
--- a/statsd/tests/SocketListener_test.cpp
+++ b/statsd/tests/SocketListener_test.cpp
@@ -87,8 +87,11 @@
                          SocketParseMessageTest::ToString);
 
 TEST_P(SocketParseMessageTest, TestProcessMessage) {
+    StatsdStats::getInstance().reset();
+
     generateAtomLogging(mEventQueue, mLogEventFilter, kEventCount, kAtomId);
 
+    int64_t lastEventTs = 0;
     // check content of the queue
     EXPECT_EQ(kEventCount, mEventQueue->mQueue.size());
     for (int i = 0; i < kEventCount; i++) {
@@ -96,7 +99,11 @@
         EXPECT_TRUE(logEvent->isValid());
         EXPECT_EQ(kAtomId + i, logEvent->GetTagId());
         EXPECT_EQ(logEvent->isParsedHeaderOnly(), GetParam());
+        lastEventTs = logEvent->GetElapsedTimestampNs();
     }
+
+    EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObserved, kEventCount);
+    EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObservedElapsedNanos, lastEventTs);
 }
 
 TEST_P(SocketParseMessageTest, TestProcessMessageEmptySetExplicitSet) {
diff --git a/statsd/tests/anomaly/AnomalyTracker_test.cpp b/statsd/tests/anomaly/AnomalyTracker_test.cpp
index 9d7ccea..827f8f5 100644
--- a/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -74,8 +74,7 @@
 }
 
 // Returns true if keys in trueList are detected as anomalies and keys in falseList are not.
-bool detectAnomaliesPass(AnomalyTracker& tracker,
-                         const int64_t& bucketNum,
+bool detectAnomaliesPass(AnomalyTracker& tracker, int64_t bucketNum,
                          const std::shared_ptr<DimToValMap>& currentBucket,
                          const std::set<const MetricDimensionKey>& trueList,
                          const std::set<const MetricDimensionKey>& falseList) {
@@ -93,10 +92,8 @@
 }
 
 // Calls tracker.detectAndDeclareAnomaly on each key in the bucket.
-void detectAndDeclareAnomalies(AnomalyTracker& tracker,
-                               const int64_t& bucketNum,
-                               const std::shared_ptr<DimToValMap>& bucket,
-                               const int64_t& eventTimestamp) {
+void detectAndDeclareAnomalies(AnomalyTracker& tracker, int64_t bucketNum,
+                               const std::shared_ptr<DimToValMap>& bucket, int64_t eventTimestamp) {
     for (const auto& kv : *bucket) {
         tracker.detectAndDeclareAnomaly(eventTimestamp, bucketNum, 0 /*metric_id*/, kv.first,
                                         kv.second);
@@ -107,9 +104,8 @@
 // timestamp (in ns) + refractoryPeriodSec.
 // If a timestamp value is negative, instead asserts that the refractory period is inapplicable
 // (either non-existant or already past).
-void checkRefractoryTimes(AnomalyTracker& tracker,
-                          const int64_t& currTimestampNs,
-                          const int32_t& refractoryPeriodSec,
+void checkRefractoryTimes(AnomalyTracker& tracker, int64_t currTimestampNs,
+                          int32_t refractoryPeriodSec,
                           const std::unordered_map<MetricDimensionKey, int64_t>& timestamps) {
     for (const auto& kv : timestamps) {
         if (kv.second < 0) {
diff --git a/statsd/tests/e2e/DurationMetric_e2e_test.cpp b/statsd/tests/e2e/DurationMetric_e2e_test.cpp
index 6908f57..a6d50d9 100644
--- a/statsd/tests/e2e/DurationMetric_e2e_test.cpp
+++ b/statsd/tests/e2e/DurationMetric_e2e_test.cpp
@@ -466,7 +466,6 @@
 
 TEST(DurationMetricE2eTest, TestWithSlicedCondition) {
     StatsdConfig config;
-    auto screenOnMatcher = CreateScreenTurnedOnAtomMatcher();
     *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
     *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
     *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
diff --git a/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp b/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
index 4f7589f..b852d7f 100644
--- a/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
+++ b/statsd/tests/e2e/GaugeMetric_e2e_push_test.cpp
@@ -498,6 +498,178 @@
                              {gaugeEventTimeNs3, gaugeEventTimeNs6});
 }
 
+TEST_F(GaugeMetricE2ePushedTest, TestPushedGaugeMetricSampling) {
+    // Initiating StatsdStats at the start of this test, so it doesn't call rand() during the test.
+    StatsdStats::getInstance();
+    // Set srand seed to make rand deterministic for testing.
+    srand(0);
+
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+
+    AtomMatcher appCrashMatcher =
+            CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
+    *config.add_atom_matcher() = appCrashMatcher;
+
+    GaugeMetric sampledGaugeMetric =
+            createGaugeMetric("GaugeSampledAppCrashesPerUid", appCrashMatcher.id(),
+                              GaugeMetric::FIRST_N_SAMPLES, nullopt, nullopt);
+    *sampledGaugeMetric.mutable_dimensions_in_what() =
+            CreateDimensions(util::APP_CRASH_OCCURRED, {1 /* uid */});
+    sampledGaugeMetric.set_sampling_percentage(50);
+    *config.add_gauge_metric() = sampledGaugeMetric;
+
+    const int64_t configAddedTimeNs = 10 * NS_PER_SEC;  // 0:10
+    const int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000LL * 1000LL;
+
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+            configAddedTimeNs, configAddedTimeNs, config, cfgKey, nullptr, 0, new UidMap());
+
+    std::vector<std::unique_ptr<LogEvent>> events;
+    for (int i = 0; i < 10; i++) {
+        events.push_back(
+                CreateAppCrashOccurredEvent(configAddedTimeNs + (10 * i * NS_PER_SEC), 1000 + i));
+    }
+
+    // Send log events to StatsLogProcessor.
+    for (auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, configAddedTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    backfillAggregatedAtoms(&reports);
+
+    ASSERT_EQ(1, reports.reports_size());
+    ASSERT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_TRUE(reports.reports(0).metrics(0).has_gauge_metrics());
+    StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+    ASSERT_EQ(5, gaugeMetrics.data_size());
+
+    GaugeMetricData data = gaugeMetrics.data(0);
+    ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1000);
+    ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
+                             configAddedTimeNs + bucketSizeNs, {configAddedTimeNs});
+
+    data = gaugeMetrics.data(1);
+    ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1002);
+    ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
+                             configAddedTimeNs + bucketSizeNs,
+                             {configAddedTimeNs + (10 * 2 * NS_PER_SEC)});
+
+    data = gaugeMetrics.data(2);
+    ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1003);
+    ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
+                             configAddedTimeNs + bucketSizeNs,
+                             {configAddedTimeNs + (10 * 3 * NS_PER_SEC)});
+
+    data = gaugeMetrics.data(3);
+    ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1007);
+    ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
+                             configAddedTimeNs + bucketSizeNs,
+                             {configAddedTimeNs + (10 * 7 * NS_PER_SEC)});
+
+    data = gaugeMetrics.data(4);
+    ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1009);
+    ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
+                             configAddedTimeNs + bucketSizeNs,
+                             {configAddedTimeNs + (10 * 9 * NS_PER_SEC)});
+}
+
+TEST_F(GaugeMetricE2ePushedTest, TestPushedGaugeMetricSamplingWithDimensionalSampling) {
+    ShardOffsetProvider::getInstance().setShardOffset(5);
+    // Initiating StatsdStats at the start of this test, so it doesn't call rand() during the test.
+    StatsdStats::getInstance();
+    // Set srand seed to make rand deterministic for testing.
+    srand(0);
+
+    StatsdConfig config;
+    config.add_allowed_log_source("AID_ROOT");  // LogEvent defaults to UID of root.
+
+    AtomMatcher appCrashMatcher =
+            CreateSimpleAtomMatcher("APP_CRASH_OCCURRED", util::APP_CRASH_OCCURRED);
+    *config.add_atom_matcher() = appCrashMatcher;
+
+    GaugeMetric sampledGaugeMetric =
+            createGaugeMetric("GaugeSampledAppCrashesPerUid", appCrashMatcher.id(),
+                              GaugeMetric::FIRST_N_SAMPLES, nullopt, nullopt);
+    *sampledGaugeMetric.mutable_dimensions_in_what() =
+            CreateDimensions(util::APP_CRASH_OCCURRED, {1 /* uid */});
+    *sampledGaugeMetric.mutable_dimensional_sampling_info()->mutable_sampled_what_field() =
+            CreateDimensions(util::APP_CRASH_OCCURRED, {1 /*uid*/});
+    sampledGaugeMetric.mutable_dimensional_sampling_info()->set_shard_count(2);
+    sampledGaugeMetric.set_sampling_percentage(50);
+    *config.add_gauge_metric() = sampledGaugeMetric;
+
+    const int64_t configAddedTimeNs = 10 * NS_PER_SEC;  // 0:10
+    const int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000LL * 1000LL;
+
+    int uid = 12345;
+    int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+            configAddedTimeNs, configAddedTimeNs, config, cfgKey, nullptr, 0, new UidMap());
+
+    std::vector<std::unique_ptr<LogEvent>> events;
+    for (int i = 0; i < 30; i++) {
+        // Generate events with three different app uids: 1001, 1002, 1003.
+        events.push_back(CreateAppCrashOccurredEvent(configAddedTimeNs + (10 * i * NS_PER_SEC),
+                                                     1001 + (i % 3)));
+    }
+
+    // Send log events to StatsLogProcessor.
+    for (auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, configAddedTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    EXPECT_TRUE(buffer.size() > 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    backfillAggregatedAtoms(&reports);
+
+    ASSERT_EQ(1, reports.reports_size());
+    ASSERT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_TRUE(reports.reports(0).metrics(0).has_gauge_metrics());
+    StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+    ASSERT_EQ(2, gaugeMetrics.data_size());
+
+    // Only Uid 1 and 3 are logged. (odd hash value) + (offset of 5) % (shard count of 2) = 0
+    GaugeMetricData data = gaugeMetrics.data(0);
+    ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1001);
+    ValidateGaugeBucketTimes(
+            data.bucket_info(0), configAddedTimeNs, configAddedTimeNs + bucketSizeNs,
+            {10 * NS_PER_SEC, 40 * NS_PER_SEC, 220 * NS_PER_SEC, 280 * NS_PER_SEC});
+
+    data = gaugeMetrics.data(1);
+    ValidateUidDimension(data.dimensions_in_what(), util::APP_CRASH_OCCURRED, 1003);
+    ValidateGaugeBucketTimes(data.bucket_info(0), configAddedTimeNs,
+                             configAddedTimeNs + bucketSizeNs,
+                             {60 * NS_PER_SEC, 120 * NS_PER_SEC, 150 * NS_PER_SEC, 180 * NS_PER_SEC,
+                              210 * NS_PER_SEC, 300 * NS_PER_SEC});
+}
+
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/statsd/tests/e2e/StringReplace_e2e_test.cpp b/statsd/tests/e2e/StringReplace_e2e_test.cpp
new file mode 100644
index 0000000..d92499d
--- /dev/null
+++ b/statsd/tests/e2e/StringReplace_e2e_test.cpp
@@ -0,0 +1,695 @@
+/*
+ * Copyright (C) 2024, 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 <gtest/gtest.h>
+#include <gtest_matchers.h>
+
+#include "src/StatsLogProcessor.h"
+#include "tests/statsd_test_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+namespace {
+
+const int64_t metricId = 123456;
+const int TEST_ATOM_REPORTED_STRING_FIELD_ID = 5;
+const int SUBSYSTEM_SLEEP_STATE_SUBSYSTEM_NAME_FIELD_ID = 1;
+const int SUBSYSTEM_SLEEP_STATE_SUBNAME_FIELD_ID = 2;
+const int SUBSYSTEM_SLEEP_STATE_TIME_MILLIS_FIELD_ID = 4;
+const int SCHEDULED_JOB_STATE_CHANGED_JOB_NAME_FIELD_ID = 2;
+const int ACTIVITY_FOREGROUND_STATE_CHANGED_UID_FIELD_ID = 1;
+const int ACTIVITY_FOREGROUND_STATE_CHANGED_PKG_NAME_FIELD_ID = 2;
+const int WAKELOCK_STATE_CHANGED_TAG_FIELD_ID = 3;
+const int ATTRIBUTION_CHAIN_FIELD_ID = 1;
+const int ATTRIBUTION_TAG_FIELD_ID = 2;
+
+std::unique_ptr<LogEvent> CreateTestAtomReportedEventStringDim(uint64_t timestampNs,
+                                                               const string& stringField) {
+    return CreateTestAtomReportedEventWithPrimitives(
+            timestampNs, 0 /* intField */, 0l /* longField */, 0.0f /* floatField */, stringField,
+            false /* boolField */, TestAtomReported::OFF /* enumField */);
+}
+
+StatsdConfig CreateStatsdConfig() {
+    StatsdConfig config;
+    config.add_default_pull_packages("AID_ROOT");  // Fake puller is registered with root.
+    config.set_hash_strings_in_metric_report(false);
+
+    return config;
+}
+
+}  // namespace
+
+TEST(StringReplaceE2eTest, TestPushedDimension) {
+    StatsdConfig config = CreateStatsdConfig();
+
+    *config.add_atom_matcher() =
+            CreateSimpleAtomMatcher("TestAtomMatcher", util::TEST_ATOM_REPORTED);
+    FieldValueMatcher* fvm = config.mutable_atom_matcher(0)
+                                     ->mutable_simple_atom_matcher()
+                                     ->add_field_value_matcher();
+    fvm->set_field(TEST_ATOM_REPORTED_STRING_FIELD_ID);
+    StringReplacer* stringReplacer = fvm->mutable_replace_string();
+    stringReplacer->set_regex(R"([0-9]+$)");  // match trailing digits, example "42" in "foo42".
+    stringReplacer->set_replacement("");
+
+    CountMetric* countMetric = config.add_count_metric();
+    *countMetric = createCountMetric("TestCountMetric", config.atom_matcher(0).id() /* what */,
+                                     nullopt /* condition */, {} /* states */);
+    countMetric->mutable_dimensions_in_what()->set_field(util::TEST_ATOM_REPORTED);
+    countMetric->mutable_dimensions_in_what()->add_child()->set_field(
+            TEST_ATOM_REPORTED_STRING_FIELD_ID);
+
+    // Initialize StatsLogProcessor.
+    const uint64_t bucketStartTimeNs = 10000000000;  // 0:10
+    const uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+    const int uid = 12345;
+    const int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+            bucketStartTimeNs, bucketStartTimeNs, config, cfgKey, nullptr, 0, new UidMap());
+
+    std::vector<std::unique_ptr<LogEvent>> events;
+    events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 20 * NS_PER_SEC,
+                                                          "dimA" /* stringField */));  // 0:30
+    events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 40 * NS_PER_SEC,
+                                                          "dimA123" /* stringField */));  // 0:50
+    events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 60 * NS_PER_SEC,
+                                                          "dimA123B" /* stringField */));  // 1:10
+    events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 80 * NS_PER_SEC,
+                                                          "dimC0" /* stringField */));  // 1:20
+    events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 90 * NS_PER_SEC,
+                                                          "dimC00000" /* stringField */));  // 1:30
+    events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 100 * NS_PER_SEC,
+                                                          "dimC" /* stringField */));  // 1:40
+
+    // Send log events to StatsLogProcessor.
+    for (auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+
+    // Check dump report.
+    vector<uint8_t> buffer;
+    ConfigMetricsReportList reports;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    ASSERT_GT(buffer.size(), 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+
+    ASSERT_EQ(1, reports.reports_size());
+    ASSERT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
+    StatsLogReport::CountMetricDataWrapper countMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+    ASSERT_EQ(3, countMetrics.data_size());
+
+    CountMetricData data = countMetrics.data(0);
+    DimensionsValue dimValue = data.dimensions_in_what();
+    EXPECT_EQ(dimValue.field(), util::TEST_ATOM_REPORTED);
+    ASSERT_EQ(dimValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimValue.value_tuple().dimensions_value(0).field(),
+              TEST_ATOM_REPORTED_STRING_FIELD_ID);
+    EXPECT_EQ(dimValue.value_tuple().dimensions_value(0).value_str(), "dimA");
+    ASSERT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(2, data.bucket_info(0).count());
+
+    data = countMetrics.data(1);
+    dimValue = data.dimensions_in_what();
+    EXPECT_EQ(dimValue.field(), util::TEST_ATOM_REPORTED);
+    ASSERT_EQ(dimValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimValue.value_tuple().dimensions_value(0).field(),
+              TEST_ATOM_REPORTED_STRING_FIELD_ID);
+    EXPECT_EQ(dimValue.value_tuple().dimensions_value(0).value_str(), "dimA123B");
+    ASSERT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(1, data.bucket_info(0).count());
+
+    data = countMetrics.data(2);
+    dimValue = data.dimensions_in_what();
+    EXPECT_EQ(dimValue.field(), util::TEST_ATOM_REPORTED);
+    ASSERT_EQ(dimValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimValue.value_tuple().dimensions_value(0).field(),
+              TEST_ATOM_REPORTED_STRING_FIELD_ID);
+    EXPECT_EQ(dimValue.value_tuple().dimensions_value(0).value_str(), "dimC");
+    ASSERT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(3, data.bucket_info(0).count());
+}
+
+TEST(StringReplaceE2eTest, TestPushedWhat) {
+    StatsdConfig config = CreateStatsdConfig();
+
+    *config.add_atom_matcher() =
+            CreateSimpleAtomMatcher("TestAtomMatcher", util::TEST_ATOM_REPORTED);
+
+    FieldValueMatcher* fvm = config.mutable_atom_matcher(0)
+                                     ->mutable_simple_atom_matcher()
+                                     ->add_field_value_matcher();
+    fvm->set_field(TEST_ATOM_REPORTED_STRING_FIELD_ID);
+    StringReplacer* stringReplacer = fvm->mutable_replace_string();
+    stringReplacer->set_regex(R"([0-9]+$)");  // match trailing digits, example "42" in "foo42".
+    stringReplacer->set_replacement("");
+
+    *config.add_gauge_metric() = createGaugeMetric(
+            "TestAtomGaugeMetric", config.atom_matcher(0).id() /* what */,
+            GaugeMetric::FIRST_N_SAMPLES, nullopt /* condition */, nullopt /* triggerEvent */);
+
+    // Initialize StatsLogProcessor.
+    const uint64_t bucketStartTimeNs = 10000000000;  // 0:10
+    const uint64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000LL;
+    const int uid = 12345;
+    const int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+            bucketStartTimeNs, bucketStartTimeNs, config, cfgKey, nullptr, 0, new UidMap());
+
+    std::vector<std::unique_ptr<LogEvent>> events;
+    events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 20 * NS_PER_SEC,
+                                                          "dimA" /* stringField */));  // 0:30
+    events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 40 * NS_PER_SEC,
+                                                          "dimA123" /* stringField */));  // 0:50
+    events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 60 * NS_PER_SEC,
+                                                          "dimA123B" /* stringField */));  // 1:10
+    events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 80 * NS_PER_SEC,
+                                                          "dimC0" /* stringField */));  // 1:20
+    events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 90 * NS_PER_SEC,
+                                                          "dimC00000" /* stringField */));  // 1:30
+    events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 100 * NS_PER_SEC,
+                                                          "dimC" /* stringField */));  // 1:40
+
+    // Send log events to StatsLogProcessor.
+    for (auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+
+    // Check dump report.
+    vector<uint8_t> buffer;
+    ConfigMetricsReportList reports;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    ASSERT_GT(buffer.size(), 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    backfillAggregatedAtoms(&reports);
+
+    ASSERT_EQ(1, reports.reports_size());
+    ASSERT_EQ(1, reports.reports(0).metrics_size());
+    EXPECT_TRUE(reports.reports(0).metrics(0).has_gauge_metrics());
+    StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+    ASSERT_EQ(gaugeMetrics.data_size(), 1);
+
+    auto data = gaugeMetrics.data(0);
+    ASSERT_EQ(1, data.bucket_info_size());
+
+    ASSERT_EQ(6, data.bucket_info(0).atom_size());
+    EXPECT_EQ(data.bucket_info(0).atom(0).test_atom_reported().string_field(), "dimA");
+    EXPECT_EQ(data.bucket_info(0).atom(1).test_atom_reported().string_field(), "dimA");
+    EXPECT_EQ(data.bucket_info(0).atom(2).test_atom_reported().string_field(), "dimA123B");
+    EXPECT_EQ(data.bucket_info(0).atom(3).test_atom_reported().string_field(), "dimC");
+    EXPECT_EQ(data.bucket_info(0).atom(4).test_atom_reported().string_field(), "dimC");
+    EXPECT_EQ(data.bucket_info(0).atom(5).test_atom_reported().string_field(), "dimC");
+}
+
+TEST(StringReplaceE2eTest, TestPulledDimension) {
+    StatsdConfig config = CreateStatsdConfig();
+
+    *config.add_atom_matcher() =
+            CreateSimpleAtomMatcher("SubsystemMatcher", util::SUBSYSTEM_SLEEP_STATE);
+    FieldValueMatcher* fvm = config.mutable_atom_matcher(0)
+                                     ->mutable_simple_atom_matcher()
+                                     ->add_field_value_matcher();
+    fvm->set_field(SUBSYSTEM_SLEEP_STATE_SUBSYSTEM_NAME_FIELD_ID);
+    StringReplacer* stringReplacer = fvm->mutable_replace_string();
+    stringReplacer->set_regex(R"([0-9]+$)");  // match trailing digits, example "42" in "foo42".
+    stringReplacer->set_replacement("");
+
+    *config.add_gauge_metric() = createGaugeMetric(
+            "SubsystemGaugeMetric", config.atom_matcher(0).id() /* what */,
+            GaugeMetric::RANDOM_ONE_SAMPLE, nullopt /* condition */, nullopt /* triggerEvent */);
+    *config.mutable_gauge_metric(0)->mutable_dimensions_in_what() =
+            CreateDimensions(util::SUBSYSTEM_SLEEP_STATE, {1 /* subsystem name */});
+
+    int64_t baseTimeNs = getElapsedRealtimeNs();
+    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
+                                             util::SUBSYSTEM_SLEEP_STATE);
+    processor->mPullerManager->ForceClearPullerCache();
+
+    // Pulling alarm arrives on time and reset the sequential pulling alarm.
+    processor->informPullAlarmFired(baseTimeNs + 2 * bucketSizeNs + 1);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 3 * bucketSizeNs + 10, false, true,
+                            ADB_DUMP, FAST, &buffer);
+    EXPECT_GT(buffer.size(), 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    backfillAggregatedAtoms(&reports);
+    ASSERT_EQ(1, reports.reports_size());
+    ASSERT_EQ(1, reports.reports(0).metrics_size());
+    StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+    ASSERT_EQ(gaugeMetrics.data_size(), 1);
+
+    auto data = gaugeMetrics.data(0);
+    EXPECT_EQ(util::SUBSYSTEM_SLEEP_STATE, data.dimensions_in_what().field());
+    ASSERT_EQ(1, data.dimensions_in_what().value_tuple().dimensions_value_size());
+    EXPECT_EQ(SUBSYSTEM_SLEEP_STATE_SUBSYSTEM_NAME_FIELD_ID,
+              data.dimensions_in_what().value_tuple().dimensions_value(0).field());
+
+    // Trailing numbers are trimmed from the dimension: subsystem_name_# --> subsystem_name_
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_str(),
+              "subsystem_name_");
+}
+
+TEST(StringReplaceE2eTest, TestPulledWhat) {
+    StatsdConfig config = CreateStatsdConfig();
+
+    *config.add_atom_matcher() =
+            CreateSimpleAtomMatcher("SubsystemMatcher", util::SUBSYSTEM_SLEEP_STATE);
+    FieldValueMatcher* fvm = config.mutable_atom_matcher(0)
+                                     ->mutable_simple_atom_matcher()
+                                     ->add_field_value_matcher();
+    fvm->set_field(SUBSYSTEM_SLEEP_STATE_SUBNAME_FIELD_ID);
+    StringReplacer* stringReplacer = fvm->mutable_replace_string();
+    stringReplacer->set_regex(R"(foo)");
+    stringReplacer->set_replacement("bar");
+
+    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+
+    *config.add_predicate() = CreateScreenIsOffPredicate();
+
+    *config.add_gauge_metric() =
+            createGaugeMetric("SubsystemGaugeMetric", config.atom_matcher(0).id() /* what */,
+                              GaugeMetric::RANDOM_ONE_SAMPLE,
+                              config.predicate(0).id() /* condition */, nullopt /* triggerEvent */);
+
+    int64_t baseTimeNs = getElapsedRealtimeNs();
+    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.gauge_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
+                                             util::SUBSYSTEM_SLEEP_STATE);
+    processor->mPullerManager->ForceClearPullerCache();
+
+    auto screenOffEvent =
+            CreateScreenStateChangedEvent(configAddedTimeNs + 55, android::view::DISPLAY_STATE_OFF);
+    processor->OnLogEvent(screenOffEvent.get());
+
+    // Pulling alarm arrives on time and reset the sequential pulling alarm.
+    processor->informPullAlarmFired(baseTimeNs + 2 * bucketSizeNs + 1);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 3 * bucketSizeNs + 10, false, true,
+                            ADB_DUMP, FAST, &buffer);
+    EXPECT_GT(buffer.size(), 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    backfillAggregatedAtoms(&reports);
+    ASSERT_EQ(1, reports.reports_size());
+    ASSERT_EQ(1, reports.reports(0).metrics_size());
+    StatsLogReport::GaugeMetricDataWrapper gaugeMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).gauge_metrics(), &gaugeMetrics);
+    ASSERT_EQ(gaugeMetrics.data_size(), 1);
+
+    auto data = gaugeMetrics.data(0);
+    ASSERT_EQ(2, data.bucket_info_size());
+
+    ASSERT_EQ(1, data.bucket_info(0).atom_size());
+    EXPECT_EQ(data.bucket_info(0).atom(0).subsystem_sleep_state().subname(),
+              "subsystem_subname bar");
+
+    ASSERT_EQ(1, data.bucket_info(1).atom_size());
+    EXPECT_EQ(data.bucket_info(1).atom(0).subsystem_sleep_state().subname(),
+              "subsystem_subname bar");
+}
+
+TEST(StringReplaceE2eTest, TestCondition) {
+    StatsdConfig config = CreateStatsdConfig();
+
+    AtomMatcher matcher = CreateStartScheduledJobAtomMatcher();
+    FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(SCHEDULED_JOB_STATE_CHANGED_JOB_NAME_FIELD_ID);
+    fvm->set_eq_string("foo");
+    StringReplacer* stringReplacer = fvm->mutable_replace_string();
+    stringReplacer->set_regex(R"(com.google.)");
+    stringReplacer->set_replacement("");
+    *config.add_atom_matcher() = matcher;
+    matcher = CreateFinishScheduledJobAtomMatcher();
+    fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(SCHEDULED_JOB_STATE_CHANGED_JOB_NAME_FIELD_ID);
+    fvm->set_eq_string("foo");
+    stringReplacer = fvm->mutable_replace_string();
+    stringReplacer->set_regex(R"(com.google.)");
+    stringReplacer->set_replacement("");
+    *config.add_atom_matcher() = matcher;
+
+    Predicate predicate = CreateScheduledJobPredicate();
+    *config.add_predicate() = predicate;
+
+    matcher = CreateSimpleAtomMatcher("TestAtomMatcher", util::TEST_ATOM_REPORTED);
+    *config.add_atom_matcher() = matcher;
+
+    CountMetric* countMetric = config.add_count_metric();
+    *countMetric = createCountMetric("TestCountMetric", matcher.id() /* what */,
+                                     predicate.id() /* condition */, {} /* states */);
+
+    // Initialize StatsLogProcessor.
+    const uint64_t bucketStartTimeNs = 10000000000;  // 0:10
+    const uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(countMetric->bucket()) * 1000000LL;
+    const int uid = 12345;
+    const int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+            bucketStartTimeNs, bucketStartTimeNs, config, cfgKey, nullptr, 0, new UidMap());
+
+    std::vector<std::unique_ptr<LogEvent>> events;
+    events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 20 * NS_PER_SEC,
+                                                  {1001} /* uids */, {"App1"},
+                                                  "com.google.job1"));  // 0:30
+    events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 30 * NS_PER_SEC,
+                                                          "str" /* stringField */));  // 0:40
+    events.push_back(CreateStartScheduledJobEvent(bucketStartTimeNs + 40 * NS_PER_SEC,
+                                                  {1002} /* uids */, {"App1"},
+                                                  "com.google.foo"));  // 0:50
+    events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 50 * NS_PER_SEC,
+                                                          "str" /* stringField */));  // 1:00
+    events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 60 * NS_PER_SEC,
+                                                          "str" /* stringField */));  // 1:10
+    events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 70 * NS_PER_SEC,
+                                                   {1001} /* uids */, {"App1"},
+                                                   "com.google.job1"));  // 1:20
+    events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 80 * NS_PER_SEC,
+                                                          "str" /* stringField */));  // 1:30
+    events.push_back(CreateFinishScheduledJobEvent(bucketStartTimeNs + 90 * NS_PER_SEC,
+                                                   {1002} /* uids */, {"App1"},
+                                                   "com.google.foo"));  // 1:40
+    events.push_back(CreateTestAtomReportedEventStringDim(bucketStartTimeNs + 100 * NS_PER_SEC,
+                                                          "str" /* stringField */));  // 1:50
+
+    // Send log events to StatsLogProcessor.
+    for (auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+
+    // Check dump report.
+    vector<uint8_t> buffer;
+    ConfigMetricsReportList reports;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+    ASSERT_GT(buffer.size(), 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+
+    ASSERT_EQ(1, reports.reports_size());
+    ASSERT_EQ(1, reports.reports(0).metrics_size());
+    ASSERT_TRUE(reports.reports(0).metrics(0).has_count_metrics());
+    StatsLogReport::CountMetricDataWrapper countMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+    ASSERT_EQ(1, countMetrics.data_size());
+
+    CountMetricData data = countMetrics.data(0);
+    ASSERT_EQ(1, data.bucket_info_size());
+    EXPECT_EQ(3, data.bucket_info(0).count());
+}
+
+TEST(StringReplaceE2eTest, TestDurationMetric) {
+    StatsdConfig config = CreateStatsdConfig();
+
+    AtomMatcher matcher = CreateAcquireWakelockAtomMatcher();
+    FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(ATTRIBUTION_CHAIN_FIELD_ID);
+    fvm->set_position(Position::FIRST);
+    fvm->mutable_matches_tuple()->add_field_value_matcher()->set_field(ATTRIBUTION_TAG_FIELD_ID);
+    StringReplacer* stringReplacer =
+            fvm->mutable_matches_tuple()->mutable_field_value_matcher(0)->mutable_replace_string();
+    stringReplacer->set_regex(R"([0-9]+)");
+    stringReplacer->set_replacement("#");
+    *config.add_atom_matcher() = matcher;
+
+    matcher = CreateReleaseWakelockAtomMatcher();
+    fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(ATTRIBUTION_CHAIN_FIELD_ID);
+    fvm->set_position(Position::FIRST);
+    fvm->mutable_matches_tuple()->add_field_value_matcher()->set_field(ATTRIBUTION_TAG_FIELD_ID);
+    stringReplacer =
+            fvm->mutable_matches_tuple()->mutable_field_value_matcher(0)->mutable_replace_string();
+    stringReplacer->set_regex(R"([0-9]+)");
+    stringReplacer->set_replacement("#");
+    *config.add_atom_matcher() = matcher;
+
+    matcher = CreateMoveToBackgroundAtomMatcher();
+    fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(ACTIVITY_FOREGROUND_STATE_CHANGED_PKG_NAME_FIELD_ID);
+    stringReplacer = fvm->mutable_replace_string();
+    stringReplacer->set_regex(R"([0-9]+)");
+    stringReplacer->set_replacement("#");
+    *config.add_atom_matcher() = matcher;
+
+    matcher = CreateMoveToForegroundAtomMatcher();
+    fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(ACTIVITY_FOREGROUND_STATE_CHANGED_PKG_NAME_FIELD_ID);
+    stringReplacer = fvm->mutable_replace_string();
+    stringReplacer->set_regex(R"([0-9]+)");
+    stringReplacer->set_replacement("#");
+    *config.add_atom_matcher() = matcher;
+
+    Predicate holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+    // The predicate is dimensioning by first attribution node by uid and tag.
+    *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() =
+            CreateAttributionUidAndTagDimensions(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,
+                             {ACTIVITY_FOREGROUND_STATE_CHANGED_UID_FIELD_ID,
+                              ACTIVITY_FOREGROUND_STATE_CHANGED_PKG_NAME_FIELD_ID});
+    *config.add_predicate() = isInBackgroundPredicate;
+
+    DurationMetric durationMetric =
+            createDurationMetric("WakelockDuration", holdingWakelockPredicate.id() /* what */,
+                                 isInBackgroundPredicate.id() /* condition */, {} /* states */);
+    *durationMetric.mutable_dimensions_in_what() =
+            CreateAttributionUidAndTagDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    durationMetric.set_bucket(FIVE_MINUTES);
+    *config.add_duration_metric() = durationMetric;
+
+    // Links between wakelock state atom and condition of app is in background.
+    MetricConditionLink* link = durationMetric.add_links();
+    link->set_condition(isInBackgroundPredicate.id());
+    *link->mutable_fields_in_what() =
+            CreateAttributionUidAndTagDimensions(util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    *link->mutable_fields_in_condition() =
+            CreateDimensions(util::ACTIVITY_FOREGROUND_STATE_CHANGED,
+                             {ACTIVITY_FOREGROUND_STATE_CHANGED_UID_FIELD_ID,
+                              ACTIVITY_FOREGROUND_STATE_CHANGED_PKG_NAME_FIELD_ID});
+
+    // Initialize StatsLogProcessor.
+    const uint64_t bucketStartTimeNs = 10000000000;  // 0:10
+    const uint64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(durationMetric.bucket()) * 1000000LL;
+    const int uid = 12345;
+    const int64_t cfgId = 98765;
+    ConfigKey cfgKey(uid, cfgId);
+
+    sp<StatsLogProcessor> processor = CreateStatsLogProcessor(
+            bucketStartTimeNs, bucketStartTimeNs, config, cfgKey, nullptr, 0, new UidMap());
+
+    int appUid = 123;
+    std::vector<int> attributionUids1 = {appUid};
+    std::vector<string> attributionTags1 = {"App1"};
+    std::vector<string> attributionTags2 = {"App2"};
+
+    std::vector<std::unique_ptr<LogEvent>> events;
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + 10 * NS_PER_SEC,
+                                                attributionUids1, attributionTags1,
+                                                "wl1"));  // 0:10
+    events.push_back(CreateActivityForegroundStateChangedEvent(
+            bucketStartTimeNs + 22 * NS_PER_SEC, appUid, "App1", "class_name",
+            ActivityForegroundStateChanged::BACKGROUND));  // 0:22
+    events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + 60 * NS_PER_SEC,
+                                                attributionUids1, attributionTags1,
+                                                "wl1"));  // 1:00
+    events.push_back(CreateActivityForegroundStateChangedEvent(
+            bucketStartTimeNs + (3 * 60 + 15) * NS_PER_SEC, appUid, "App1", "class_name",
+            ActivityForegroundStateChanged::FOREGROUND));  // 3:15
+    events.push_back(CreateAcquireWakelockEvent(bucketStartTimeNs + (3 * 60 + 20) * NS_PER_SEC,
+                                                attributionUids1, attributionTags2,
+                                                "wl2"));  // 3:20
+    events.push_back(CreateActivityForegroundStateChangedEvent(
+            bucketStartTimeNs + (3 * 60 + 30) * NS_PER_SEC, appUid, "App1", "class_name",
+            ActivityForegroundStateChanged::BACKGROUND));  // 3:30
+    events.push_back(CreateActivityForegroundStateChangedEvent(
+            bucketStartTimeNs + (3 * 60 + 40) * NS_PER_SEC, appUid, "App2", "class_name",
+            ActivityForegroundStateChanged::FOREGROUND));  // 3:40
+    events.push_back(CreateReleaseWakelockEvent(bucketStartTimeNs + (3 * 60 + 50) * NS_PER_SEC,
+                                                attributionUids1, attributionTags2,
+                                                "wl2"));  // 3:50
+
+    // Send log events to StatsLogProcessor.
+    for (auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+
+    vector<uint8_t> buffer;
+    ConfigMetricsReportList reports;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + bucketSizeNs + 1, false, true, ADB_DUMP,
+                            FAST, &buffer);
+
+    ASSERT_GT(buffer.size(), 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+
+    ASSERT_EQ(1, reports.reports_size());
+    ASSERT_EQ(1, reports.reports(0).metrics_size());
+    ASSERT_TRUE(reports.reports(0).metrics(0).has_duration_metrics());
+    StatsLogReport::DurationMetricDataWrapper durationMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(),
+                                    &durationMetrics);
+    ASSERT_EQ(1, durationMetrics.data_size());
+
+    DurationMetricData data = durationMetrics.data(0);
+    // Validate dimension value.
+    ValidateAttributionUidAndTagDimension(data.dimensions_in_what(), util::WAKELOCK_STATE_CHANGED,
+                                          appUid, "App#");
+    // Validate bucket info.
+    ASSERT_EQ(1, data.bucket_info_size());
+
+    auto bucketInfo = data.bucket_info(0);
+    EXPECT_EQ(bucketStartTimeNs, bucketInfo.start_bucket_elapsed_nanos());
+    EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, bucketInfo.end_bucket_elapsed_nanos());
+    EXPECT_EQ(48 * NS_PER_SEC, bucketInfo.duration_nanos());
+}
+
+TEST(StringReplaceE2eTest, TestMultipleMatchersForAtom) {
+    StatsdConfig config = CreateStatsdConfig();
+
+    {
+        AtomMatcher matcher = CreateSimpleAtomMatcher("Matcher1", util::SUBSYSTEM_SLEEP_STATE);
+        FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+        fvm->set_field(SUBSYSTEM_SLEEP_STATE_SUBSYSTEM_NAME_FIELD_ID);
+        fvm->set_eq_string("subsystem_name_1");
+        fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+        fvm->set_field(SUBSYSTEM_SLEEP_STATE_SUBNAME_FIELD_ID);
+        fvm->set_eq_string("subsystem_subname bar");
+        StringReplacer* stringReplacer = fvm->mutable_replace_string();
+        stringReplacer->set_regex(R"(foo)");
+        stringReplacer->set_replacement("bar");
+        *config.add_atom_matcher() = matcher;
+
+        *config.add_value_metric() =
+                createValueMetric("Value1", matcher, SUBSYSTEM_SLEEP_STATE_TIME_MILLIS_FIELD_ID,
+                                  nullopt /* condition */, {} /* states */);
+    }
+    {
+        AtomMatcher matcher = CreateSimpleAtomMatcher("Matcher2", util::SUBSYSTEM_SLEEP_STATE);
+        FieldValueMatcher* fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+        fvm->set_field(SUBSYSTEM_SLEEP_STATE_SUBSYSTEM_NAME_FIELD_ID);
+        fvm->set_eq_string("subsystem_name_2");
+        fvm = matcher.mutable_simple_atom_matcher()->add_field_value_matcher();
+        fvm->set_field(SUBSYSTEM_SLEEP_STATE_SUBNAME_FIELD_ID);
+        fvm->set_eq_string("subsystem_subname foo");
+        *config.add_atom_matcher() = matcher;
+
+        *config.add_value_metric() =
+                createValueMetric("Value2", matcher, SUBSYSTEM_SLEEP_STATE_TIME_MILLIS_FIELD_ID,
+                                  nullopt /* condition */, {} /* states */);
+    }
+
+    int64_t baseTimeNs = getElapsedRealtimeNs();
+    int64_t configAddedTimeNs = 10 * 60 * NS_PER_SEC + baseTimeNs;
+    int64_t bucketSizeNs = TimeUnitToBucketSizeInMillis(config.value_metric(0).bucket()) * 1000000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(baseTimeNs, configAddedTimeNs, config, cfgKey,
+                                             SharedRefBase::make<FakeSubsystemSleepCallback>(),
+                                             util::SUBSYSTEM_SLEEP_STATE);
+    processor->mPullerManager->ForceClearPullerCache();
+
+    // Pulling alarm arrives on time and reset the sequential pulling alarm.
+    processor->informPullAlarmFired(baseTimeNs + 2 * bucketSizeNs + 1);
+
+    ConfigMetricsReportList reports;
+    vector<uint8_t> buffer;
+    processor->onDumpReport(cfgKey, configAddedTimeNs + 3 * bucketSizeNs + 10, false, true,
+                            ADB_DUMP, FAST, &buffer);
+    EXPECT_GT(buffer.size(), 0);
+    EXPECT_TRUE(reports.ParseFromArray(&buffer[0], buffer.size()));
+    backfillDimensionPath(&reports);
+    backfillStringInReport(&reports);
+    backfillStartEndTimestamp(&reports);
+    backfillAggregatedAtoms(&reports);
+    ASSERT_EQ(1, reports.reports_size());
+    ASSERT_EQ(2, reports.reports(0).metrics_size());
+
+    {
+        StatsLogReport::ValueMetricDataWrapper valueMetrics;
+        sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).value_metrics(),
+                                        &valueMetrics);
+        ASSERT_EQ(valueMetrics.data_size(), 1);
+
+        ValueMetricData data = valueMetrics.data(0);
+        ASSERT_EQ(data.bucket_info_size(), 1);
+        ASSERT_EQ(data.bucket_info(0).values_size(), 1);
+    }
+    {
+        StatsLogReport::ValueMetricDataWrapper valueMetrics;
+        sortMetricDataByDimensionsValue(reports.reports(0).metrics(1).value_metrics(),
+                                        &valueMetrics);
+        ASSERT_EQ(valueMetrics.data_size(), 1);
+
+        ValueMetricData data = valueMetrics.data(0);
+        ASSERT_EQ(data.bucket_info_size(), 1);
+        ASSERT_EQ(data.bucket_info(0).values_size(), 1);
+    }
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/statsd/tests/guardrail/StatsdStats_test.cpp b/statsd/tests/guardrail/StatsdStats_test.cpp
index 6e61b62..1d56caa 100644
--- a/statsd/tests/guardrail/StatsdStats_test.cpp
+++ b/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -750,6 +750,16 @@
     EXPECT_FALSE(nonPlatformPushedAtomStats.has_skip_count());
 }
 
+TEST(StatsdStatsTest, TestQueueStats) {
+    StatsdStats stats;
+
+    stats.noteEventQueueSize(100, 1000);
+    StatsdStatsReport report = getStatsdStatsReport(stats, /* reset stats */ true);
+
+    ASSERT_EQ(100, report.event_queue_stats().max_size_observed());
+    ASSERT_EQ(1000, report.event_queue_stats().max_size_observed_elapsed_nanos());
+}
+
 TEST(StatsdStatsTest, TestAtomLoggedAndDroppedStats) {
     StatsdStats stats;
 
diff --git a/statsd/tests/indexed_priority_queue_test.cpp b/statsd/tests/indexed_priority_queue_test.cpp
index 3a65456..1d365e8 100644
--- a/statsd/tests/indexed_priority_queue_test.cpp
+++ b/statsd/tests/indexed_priority_queue_test.cpp
@@ -30,7 +30,7 @@
     const std::string b;
 
     struct Smaller {
-        bool operator()(const sp<const AATest> a, const sp<const AATest> b) const {
+        bool operator()(const sp<const AATest>& a, const sp<const AATest>& b) const {
             return (a->val < b->val);
         }
     };
diff --git a/statsd/tests/log_event/LogEventQueue_test.cpp b/statsd/tests/log_event/LogEventQueue_test.cpp
index a15f95b..acc9c5d 100644
--- a/statsd/tests/log_event/LogEventQueue_test.cpp
+++ b/statsd/tests/log_event/LogEventQueue_test.cpp
@@ -20,6 +20,7 @@
 
 #include <thread>
 
+#include "socket/StatsSocketListener.h"
 #include "stats_event.h"
 #include "tests/statsd_test_util.h"
 
@@ -34,37 +35,43 @@
 
 namespace {
 
-std::unique_ptr<LogEvent> makeLogEvent(uint64_t timestampNs) {
+AStatsEvent* makeStatsEvent(uint64_t timestampNs) {
     AStatsEvent* statsEvent = AStatsEvent_obtain();
     AStatsEvent_setAtomId(statsEvent, 10);
     AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
+    AStatsEvent_build(statsEvent);
+    return statsEvent;
+}
 
+std::unique_ptr<LogEvent> makeLogEvent(uint64_t timestampNs) {
+    AStatsEvent* statsEvent = makeStatsEvent(timestampNs);
     std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
     parseStatsEventToLogEvent(statsEvent, logEvent.get());
+    EXPECT_EQ(logEvent->GetElapsedTimestampNs(), timestampNs);
     return logEvent;
 }
 
-} // anonymous namespace
+}  // anonymous namespace
 
 #ifdef __ANDROID__
 TEST(LogEventQueue_test, TestGoodConsumer) {
     LogEventQueue queue(50);
-    int64_t timeBaseNs = 100;
-    std::thread writer([&queue, timeBaseNs] {
+    int64_t eventTimeNs = 100;
+    std::thread writer([&queue, eventTimeNs] {
+        LogEventQueue::Result result;
         for (int i = 0; i < 100; i++) {
-            int64_t oldestEventNs;
-            bool success = queue.push(makeLogEvent(timeBaseNs + i * 1000), &oldestEventNs);
-            EXPECT_TRUE(success);
+            result = queue.push(makeLogEvent(eventTimeNs + i * 1000));
+            EXPECT_TRUE(result.success);
             std::this_thread::sleep_for(std::chrono::milliseconds(1));
         }
     });
 
-    std::thread reader([&queue, timeBaseNs] {
+    std::thread reader([&queue, eventTimeNs] {
         for (int i = 0; i < 100; i++) {
             auto event = queue.waitPop();
             EXPECT_TRUE(event != nullptr);
             // All events are in right order.
-            EXPECT_EQ(timeBaseNs + i * 1000, event->GetElapsedTimestampNs());
+            EXPECT_EQ(eventTimeNs + i * 1000, event->GetElapsedTimestampNs());
         }
     });
 
@@ -74,13 +81,15 @@
 
 TEST(LogEventQueue_test, TestSlowConsumer) {
     LogEventQueue queue(50);
-    int64_t timeBaseNs = 100;
-    std::thread writer([&queue, timeBaseNs] {
+    int64_t eventTimeNs = 100;
+    std::thread writer([&queue, eventTimeNs] {
         int failure_count = 0;
-        int64_t oldestEventNs;
+        LogEventQueue::Result result;
         for (int i = 0; i < 100; i++) {
-            bool success = queue.push(makeLogEvent(timeBaseNs + i * 1000), &oldestEventNs);
-            if (!success) failure_count++;
+            result = queue.push(makeLogEvent(eventTimeNs + i * 1000));
+            if (!result.success) {
+                failure_count++;
+            }
             std::this_thread::sleep_for(std::chrono::milliseconds(1));
         }
 
@@ -89,16 +98,16 @@
         // There will be at least 45 events lost due to overflow.
         EXPECT_TRUE(failure_count >= 45);
         // The oldest event must be at least the 6th event.
-        EXPECT_TRUE(oldestEventNs <= (100 + 5 * 1000));
+        EXPECT_TRUE(result.oldestTimestampNs <= (100 + 5 * 1000));
     });
 
-    std::thread reader([&queue, timeBaseNs] {
+    std::thread reader([&queue, eventTimeNs] {
         // The consumer quickly processed 5 events, then it got stuck (not reading anymore).
         for (int i = 0; i < 5; i++) {
             auto event = queue.waitPop();
             EXPECT_TRUE(event != nullptr);
             // All events are in right order.
-            EXPECT_EQ(timeBaseNs + i * 1000, event->GetElapsedTimestampNs());
+            EXPECT_EQ(eventTimeNs + i * 1000, event->GetElapsedTimestampNs());
         }
     });
 
@@ -106,6 +115,87 @@
     writer.join();
 }
 
+TEST(LogEventQueue_test, TestQueueMaxSize) {
+    StatsdStats::getInstance().reset();
+
+    std::shared_ptr<LogEventQueue> queue(std::make_shared<LogEventQueue>(50));
+    std::shared_ptr<LogEventFilter> filter(std::make_shared<LogEventFilter>());
+    filter->setFilteringEnabled(false);
+
+    int64_t eventTimeNs = 100;
+    int64_t oldestEventNs = 0;
+    int32_t newSize = 0;
+    for (int i = 0; i < 30; i++, eventTimeNs++) {
+        auto statsEvent = makeStatsEvent(eventTimeNs);
+        size_t bufferSize;
+        const uint8_t* buffer = AStatsEvent_getBuffer(statsEvent, &bufferSize);
+        StatsSocketListener::processMessage(buffer, bufferSize, 0, 0, queue, filter);
+        AStatsEvent_release(statsEvent);
+        EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObserved, i + 1);
+        EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObservedElapsedNanos, eventTimeNs);
+    }
+
+    const int32_t lastMaxSizeObserved = StatsdStats::getInstance().mEventQueueMaxSizeObserved;
+    const int64_t lastMaxSizeElapsedNanos =
+            StatsdStats::getInstance().mEventQueueMaxSizeObservedElapsedNanos;
+
+    // consumer reads the entire queue
+    int64_t nextEventTs = 100;
+    for (int i = 0; i < 30; i++, nextEventTs++) {
+        auto event = queue->waitPop();
+        EXPECT_TRUE(event != nullptr);
+        // All events are in right order.
+        EXPECT_EQ(nextEventTs, event->GetElapsedTimestampNs());
+    }
+
+    // the expectation after queue drained entirely the max count & ts do not update for
+    // smaller values
+    {
+        auto statsEvent = makeStatsEvent(eventTimeNs);
+        size_t bufferSize;
+        const uint8_t* buffer = AStatsEvent_getBuffer(statsEvent, &bufferSize);
+        StatsSocketListener::processMessage(buffer, bufferSize, 0, 0, queue, filter);
+        AStatsEvent_release(statsEvent);
+        EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObserved, lastMaxSizeObserved);
+        EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObservedElapsedNanos,
+                  lastMaxSizeElapsedNanos);
+        eventTimeNs++;
+    }
+
+    for (int i = 0; i < 1; i++, nextEventTs++) {
+        auto event = queue->waitPop();
+        EXPECT_TRUE(event != nullptr);
+        // All events are in right order.
+        EXPECT_EQ(nextEventTs, event->GetElapsedTimestampNs());
+    }
+
+    // the expectation after queue drained entirely the max count & ts do update for
+    // bigger values
+    // fill up to the the previous max values observed - stats are not changed
+    for (int i = 0; i < lastMaxSizeObserved; i++, eventTimeNs++) {
+        auto statsEvent = makeStatsEvent(eventTimeNs);
+        size_t bufferSize;
+        const uint8_t* buffer = AStatsEvent_getBuffer(statsEvent, &bufferSize);
+        StatsSocketListener::processMessage(buffer, bufferSize, 0, 0, queue, filter);
+        AStatsEvent_release(statsEvent);
+        EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObserved, lastMaxSizeObserved);
+        EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObservedElapsedNanos,
+                  lastMaxSizeElapsedNanos);
+    }
+
+    // add extra elements to update the stats
+    for (int i = 0; i < 10; i++, eventTimeNs++) {
+        auto statsEvent = makeStatsEvent(eventTimeNs);
+        size_t bufferSize;
+        const uint8_t* buffer = AStatsEvent_getBuffer(statsEvent, &bufferSize);
+        StatsSocketListener::processMessage(buffer, bufferSize, 0, 0, queue, filter);
+        AStatsEvent_release(statsEvent);
+        EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObserved,
+                  lastMaxSizeObserved + i + 1);
+        EXPECT_EQ(StatsdStats::getInstance().mEventQueueMaxSizeObservedElapsedNanos, eventTimeNs);
+    }
+}
+
 #else
 GTEST_LOG_(INFO) << "This test does nothing.\n";
 #endif
diff --git a/statsd/tests/metrics/metrics_test_helper.h b/statsd/tests/metrics/metrics_test_helper.h
index 39232c1..d750149 100644
--- a/statsd/tests/metrics/metrics_test_helper.h
+++ b/statsd/tests/metrics/metrics_test_helper.h
@@ -34,18 +34,18 @@
 class MockStatsPullerManager : public StatsPullerManager {
 public:
     MOCK_METHOD5(RegisterReceiver,
-                 void(int tagId, const ConfigKey& key, wp<PullDataReceiver> receiver,
+                 void(int tagId, const ConfigKey& key, const wp<PullDataReceiver>& receiver,
                       int64_t nextPulltimeNs, int64_t intervalNs));
     MOCK_METHOD3(UnRegisterReceiver,
-                 void(int tagId, const ConfigKey& key, wp<PullDataReceiver> receiver));
-    MOCK_METHOD4(Pull, bool(const int pullCode, const ConfigKey& key, const int64_t eventTimeNs,
+                 void(int tagId, const ConfigKey& key, const wp<PullDataReceiver>& receiver));
+    MOCK_METHOD4(Pull, bool(const int pullCode, const ConfigKey& key, 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));
+                 void(const ConfigKey& configKey, const wp<PullUidProvider>& provider));
     MOCK_METHOD2(UnregisterPullUidProvider,
-                 void(const ConfigKey& configKey, wp<PullUidProvider> provider));
+                 void(const ConfigKey& configKey, const wp<PullUidProvider>& provider));
 };
 
 HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value);
@@ -55,7 +55,7 @@
 MetricDimensionKey getMockedStateDimensionKey(int tagId, int key, int64_t value);
 
 // Utils to build FieldMatcher proto for simple one-depth atoms.
-void buildSimpleAtomFieldMatcher(const int tagId, const int atomFieldNum, FieldMatcher* matcher);
+void buildSimpleAtomFieldMatcher(const int tagId, int atomFieldNum, FieldMatcher* matcher);
 void buildSimpleAtomFieldMatcher(const int tagId, FieldMatcher* matcher);
 
 }  // namespace statsd
diff --git a/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp b/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp
index 1da910b..da678b4 100644
--- a/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp
+++ b/statsd/tests/metrics/parsing_utils/metrics_manager_util_test.cpp
@@ -628,7 +628,7 @@
                                   StringToId("Event")));
 }
 
-TEST_F(MetricsManagerUtilTest, TestEventMetricInvalidSamplingPercentage2) {
+TEST_F(MetricsManagerUtilTest, TestEventMetricInvalidSamplingPercentageZero) {
     StatsdConfig config;
     EventMetric* metric = config.add_event_metric();
     *metric = createEventMetric(/*name=*/"Event", /*what=*/StringToId("ScreenTurnedOn"),
@@ -641,7 +641,7 @@
                                   StringToId("Event")));
 }
 
-TEST_F(MetricsManagerUtilTest, TestEventMetricValidSamplingPercentage2) {
+TEST_F(MetricsManagerUtilTest, TestEventMetricValidSamplingPercentage) {
     StatsdConfig config;
     EventMetric* metric = config.add_event_metric();
     *metric = createEventMetric(/*name=*/"Event", /*what=*/StringToId("ScreenTurnedOn"),
@@ -652,6 +652,61 @@
     EXPECT_EQ(initConfig(config), nullopt);
 }
 
+TEST_F(MetricsManagerUtilTest, TestGaugeMetricInvalidSamplingPercentage) {
+    StatsdConfig config;
+    GaugeMetric* metric = config.add_gauge_metric();
+    *metric = createGaugeMetric(/*name=*/"Gauge", /*what=*/StringToId("ScreenTurnedOn"),
+                                GaugeMetric::FIRST_N_SAMPLES,
+                                /*condition=*/nullopt, /*triggerEvent=*/nullopt);
+    metric->set_sampling_percentage(101);
+    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+
+    EXPECT_EQ(initConfig(config),
+              InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_INCORRECT_SAMPLING_PERCENTAGE,
+                                  StringToId("Gauge")));
+}
+
+TEST_F(MetricsManagerUtilTest, TestGaugeMetricInvalidSamplingPercentageZero) {
+    StatsdConfig config;
+    GaugeMetric* metric = config.add_gauge_metric();
+    *metric = createGaugeMetric(/*name=*/"Gauge", /*what=*/StringToId("ScreenTurnedOn"),
+                                GaugeMetric::FIRST_N_SAMPLES,
+                                /*condition=*/nullopt, /*triggerEvent=*/nullopt);
+    metric->set_sampling_percentage(0);
+    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+
+    EXPECT_EQ(initConfig(config),
+              InvalidConfigReason(INVALID_CONFIG_REASON_METRIC_INCORRECT_SAMPLING_PERCENTAGE,
+                                  StringToId("Gauge")));
+}
+
+TEST_F(MetricsManagerUtilTest, TestGaugeMetricValidSamplingPercentage) {
+    StatsdConfig config;
+    GaugeMetric* metric = config.add_gauge_metric();
+    *metric = createGaugeMetric(/*name=*/"Gauge", /*what=*/StringToId("ScreenTurnedOn"),
+                                GaugeMetric::FIRST_N_SAMPLES,
+                                /*condition=*/nullopt, /*triggerEvent=*/nullopt);
+    metric->set_sampling_percentage(50);
+    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+
+    EXPECT_EQ(initConfig(config), nullopt);
+}
+
+TEST_F(MetricsManagerUtilTest, TestPulledGaugeMetricWithSamplingPercentage) {
+    StatsdConfig config;
+    GaugeMetric* metric = config.add_gauge_metric();
+    *metric = createGaugeMetric(/*name=*/"Gauge", /*what=*/StringToId("SubsystemSleep"),
+                                GaugeMetric::FIRST_N_SAMPLES,
+                                /*condition=*/nullopt, /*triggerEvent=*/nullopt);
+    metric->set_sampling_percentage(50);
+    *config.add_atom_matcher() =
+            CreateSimpleAtomMatcher("SubsystemSleep", util::SUBSYSTEM_SLEEP_STATE);
+
+    EXPECT_EQ(initConfig(config),
+              InvalidConfigReason(INVALID_CONFIG_REASON_GAUGE_METRIC_PULLED_WITH_SAMPLING,
+                                  StringToId("Gauge")));
+}
+
 TEST_F(MetricsManagerUtilTest, TestNumericValueMetricMissingIdOrWhat) {
     StatsdConfig config;
     int64_t metricId = 1;
@@ -1930,6 +1985,313 @@
     EXPECT_EQ(kllProducer->mDimensionHardLimit, actualLimit);
 }
 
+TEST_F(MetricsManagerUtilTest, TestMissingValueMatcherAndStringReplacer) {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* matcher = config.add_atom_matcher();
+    matcher->set_id(111);
+    matcher->mutable_simple_atom_matcher()->set_atom_id(SCREEN_STATE_ATOM_ID);
+    matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+
+    optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+    ASSERT_NE(actualInvalidConfigReason, nullopt);
+    EXPECT_EQ(actualInvalidConfigReason->reason,
+              INVALID_CONFIG_REASON_MATCHER_NO_VALUE_MATCHER_NOR_STRING_REPLACER);
+    EXPECT_THAT(actualInvalidConfigReason->matcherIds, ElementsAre(111));
+}
+
+TEST_F(MetricsManagerUtilTest, TestMatcherWithValueMatcherOnly) {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* matcher = config.add_atom_matcher();
+    matcher->set_id(111);
+    matcher->mutable_simple_atom_matcher()->set_atom_id(SCREEN_STATE_ATOM_ID);
+    FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(2 /*int_field*/);
+    fvm->set_eq_int(1);
+
+    optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+    ASSERT_EQ(actualInvalidConfigReason, nullopt);
+}
+
+TEST_F(MetricsManagerUtilTest, TestMatcherWithStringReplacerOnly) {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* matcher = config.add_atom_matcher();
+    matcher->set_id(111);
+    matcher->mutable_simple_atom_matcher()->set_atom_id(SCREEN_STATE_ATOM_ID);
+    FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(5 /*string_field*/);
+    fvm->mutable_replace_string()->set_regex(R"([0-9]+$)");
+    fvm->mutable_replace_string()->set_replacement("#");
+
+    optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+    ASSERT_EQ(actualInvalidConfigReason, nullopt);
+}
+
+TEST_F(MetricsManagerUtilTest, TestValueMatcherWithPositionAll) {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* matcher = config.add_atom_matcher();
+    matcher->set_id(111);
+    matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+    FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(9 /*repeated_int_field*/);
+    fvm->set_position(Position::ALL);
+    fvm->set_eq_int(1);
+
+    optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+    ASSERT_NE(actualInvalidConfigReason, nullopt);
+    EXPECT_EQ(actualInvalidConfigReason->reason,
+              INVALID_CONFIG_REASON_MATCHER_VALUE_MATCHER_WITH_POSITION_ALL);
+    EXPECT_THAT(actualInvalidConfigReason->matcherIds, ElementsAre(111));
+}
+
+TEST_F(MetricsManagerUtilTest, TestValueMatcherAndStringReplaceWithPositionAll) {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* matcher = config.add_atom_matcher();
+    matcher->set_id(111);
+    matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+    FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(12 /*repeated_string_field*/);
+    fvm->set_position(Position::ALL);
+    fvm->set_eq_string("foo");
+    fvm->mutable_replace_string()->set_regex(R"([0-9]+$)");
+    fvm->mutable_replace_string()->set_replacement("");
+
+    optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+    ASSERT_NE(actualInvalidConfigReason, nullopt);
+    EXPECT_EQ(actualInvalidConfigReason->reason,
+              INVALID_CONFIG_REASON_MATCHER_VALUE_MATCHER_WITH_POSITION_ALL);
+    EXPECT_THAT(actualInvalidConfigReason->matcherIds, ElementsAre(111));
+}
+
+TEST_F(MetricsManagerUtilTest, TestValueMatcherWithPositionAllNested) {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    // Match on attribution_node[ALL].uid = 1
+    AtomMatcher* matcher = config.add_atom_matcher();
+    matcher->set_id(111);
+    matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+    FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(1 /*attribution_node*/);
+    fvm->set_position(Position::ALL);
+    fvm->mutable_matches_tuple()->add_field_value_matcher()->set_field(1 /* uid */);
+    fvm->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_int(1);
+
+    optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+    ASSERT_NE(actualInvalidConfigReason, nullopt);
+    EXPECT_EQ(actualInvalidConfigReason->reason,
+              INVALID_CONFIG_REASON_MATCHER_VALUE_MATCHER_WITH_POSITION_ALL);
+    EXPECT_THAT(actualInvalidConfigReason->matcherIds, ElementsAre(111));
+}
+
+TEST_F(MetricsManagerUtilTest, TestValueMatcherAndStringReplaceWithPositionAllNested) {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    // Match on attribution_node[ALL].uid = 1
+    AtomMatcher* matcher = config.add_atom_matcher();
+    matcher->set_id(111);
+    matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+    FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(1 /*attribution_node*/);
+    fvm->set_position(Position::ALL);
+    fvm->mutable_matches_tuple()->add_field_value_matcher()->set_field(2 /* tag */);
+    fvm->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string("foo");
+    fvm->mutable_matches_tuple()
+            ->mutable_field_value_matcher(0)
+            ->mutable_replace_string()
+            ->set_regex(R"([0-9]+$)");
+    fvm->mutable_matches_tuple()
+            ->mutable_field_value_matcher(0)
+            ->mutable_replace_string()
+            ->set_replacement("");
+
+    optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+    ASSERT_NE(actualInvalidConfigReason, nullopt);
+    EXPECT_EQ(actualInvalidConfigReason->reason,
+              INVALID_CONFIG_REASON_MATCHER_VALUE_MATCHER_WITH_POSITION_ALL);
+    EXPECT_THAT(actualInvalidConfigReason->matcherIds, ElementsAre(111));
+}
+
+TEST_F(MetricsManagerUtilTest, TestStringReplaceWithNoValueMatcherWithPositionAny) {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* matcher = config.add_atom_matcher();
+    matcher->set_id(111);
+    matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+    FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(12 /*repeated_string_field*/);
+    fvm->set_position(Position::ANY);
+    fvm->mutable_replace_string()->set_regex(R"([0-9]+$)");
+    fvm->mutable_replace_string()->set_replacement("");
+
+    optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+    ASSERT_NE(actualInvalidConfigReason, nullopt);
+    EXPECT_EQ(actualInvalidConfigReason->reason,
+              INVALID_CONFIG_REASON_MATCHER_STRING_REPLACE_WITH_NO_VALUE_MATCHER_WITH_POSITION_ANY);
+    EXPECT_THAT(actualInvalidConfigReason->matcherIds, ElementsAre(111));
+}
+
+TEST_F(MetricsManagerUtilTest, TestStringReplaceWithNoValueMatcherWithPositionAnyNested) {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    // Match on attribution_node[ALL].uid = 1
+    AtomMatcher* matcher = config.add_atom_matcher();
+    matcher->set_id(111);
+    matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+    FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(1 /*attribution_node*/);
+    fvm->set_position(Position::ANY);
+    fvm->mutable_matches_tuple()->add_field_value_matcher()->set_field(2 /* tag */);
+    fvm->mutable_matches_tuple()
+            ->mutable_field_value_matcher(0)
+            ->mutable_replace_string()
+            ->set_regex(R"([0-9]+$)");
+    fvm->mutable_matches_tuple()
+            ->mutable_field_value_matcher(0)
+            ->mutable_replace_string()
+            ->set_replacement("");
+
+    optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+    ASSERT_NE(actualInvalidConfigReason, nullopt);
+    EXPECT_EQ(actualInvalidConfigReason->reason,
+              INVALID_CONFIG_REASON_MATCHER_STRING_REPLACE_WITH_NO_VALUE_MATCHER_WITH_POSITION_ANY);
+    EXPECT_THAT(actualInvalidConfigReason->matcherIds, ElementsAre(111));
+}
+
+TEST_F(MetricsManagerUtilTest, TestStringReplaceWithValueMatcherWithPositionAny) {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* matcher = config.add_atom_matcher();
+    matcher->set_id(111);
+    matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+    FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(12 /*repeated_string_field*/);
+    fvm->set_position(Position::ANY);
+    fvm->set_eq_string("bar");
+    fvm->mutable_replace_string()->set_regex(R"([0-9]+$)");
+    fvm->mutable_replace_string()->set_replacement("");
+
+    optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+    ASSERT_EQ(actualInvalidConfigReason, nullopt);
+}
+
+TEST_F(MetricsManagerUtilTest, TestStringReplaceWithValueMatcherWithPositionAnyNested) {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    // Match on attribution_node[ALL].uid = 1
+    AtomMatcher* matcher = config.add_atom_matcher();
+    matcher->set_id(111);
+    matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+    FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(1 /*attribution_node*/);
+    fvm->set_position(Position::ANY);
+    fvm->mutable_matches_tuple()->add_field_value_matcher()->set_field(2 /* tag */);
+    fvm->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string("bar");
+    fvm->mutable_matches_tuple()
+            ->mutable_field_value_matcher(0)
+            ->mutable_replace_string()
+            ->set_regex(R"([0-9]+$)");
+    fvm->mutable_matches_tuple()
+            ->mutable_field_value_matcher(0)
+            ->mutable_replace_string()
+            ->set_replacement("");
+
+    optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+    ASSERT_EQ(actualInvalidConfigReason, nullopt);
+}
+
+TEST_F(MetricsManagerUtilTest, TestStringReplaceWithPositionAllNested) {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    // Replace attribution_node[ALL].tag using "[0-9]+$" -> "".
+    AtomMatcher* matcher = config.add_atom_matcher();
+    matcher->set_id(111);
+    matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+    FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(1 /*attribution_node*/);
+    fvm->set_position(Position::ALL);
+    fvm = fvm->mutable_matches_tuple()->add_field_value_matcher();
+    fvm->set_field(2 /* tag */);
+    fvm->mutable_replace_string()->set_regex(R"([0-9]+$)");
+    fvm->mutable_replace_string()->set_replacement("");
+
+    optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+    ASSERT_EQ(actualInvalidConfigReason, nullopt);
+}
+
+TEST_F(MetricsManagerUtilTest, TestMatcherWithStringReplaceAndNonStringValueMatcher) {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* matcher = config.add_atom_matcher();
+    matcher->set_id(111);
+    matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+    FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(2 /*int_field*/);
+    fvm->set_eq_int(1);
+    fvm->mutable_replace_string()->set_regex(R"([0-9]+$)");
+    fvm->mutable_replace_string()->set_replacement("#");
+
+    optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+    ASSERT_NE(actualInvalidConfigReason, nullopt);
+    EXPECT_EQ(actualInvalidConfigReason->reason,
+              INVALID_CONFIG_REASON_MATCHER_INVALID_VALUE_MATCHER_WITH_STRING_REPLACE);
+    EXPECT_THAT(actualInvalidConfigReason->matcherIds, ElementsAre(111));
+}
+
+TEST_F(MetricsManagerUtilTest, TestCombinationMatcherWithStringReplace) {
+    StatsdConfig config;
+    config.set_id(12345);
+
+    AtomMatcher* matcher = config.add_atom_matcher();
+    matcher->set_id(111);
+    matcher->mutable_simple_atom_matcher()->set_atom_id(util::TEST_ATOM_REPORTED);
+    FieldValueMatcher* fvm = matcher->mutable_simple_atom_matcher()->add_field_value_matcher();
+    fvm->set_field(5 /*string_field*/);
+    fvm->mutable_replace_string()->set_regex(R"([0-9]+$)");
+    fvm->mutable_replace_string()->set_replacement("#");
+
+    matcher = config.add_atom_matcher();
+    matcher->set_id(222);
+    matcher->mutable_combination()->set_operation(LogicalOperation::NOT);
+    matcher->mutable_combination()->add_matcher(111);
+
+    optional<InvalidConfigReason> actualInvalidConfigReason = initConfig(config);
+
+    ASSERT_NE(actualInvalidConfigReason, nullopt);
+    EXPECT_EQ(actualInvalidConfigReason->reason,
+              INVALID_CONFIG_REASON_MATCHER_COMBINATION_WITH_STRING_REPLACE);
+    EXPECT_THAT(actualInvalidConfigReason->matcherIds, ElementsAre(222));
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/statsd/tests/shell/ShellSubscriber_test.cpp b/statsd/tests/shell/ShellSubscriber_test.cpp
index a06f76f..bc4bd9c 100644
--- a/statsd/tests/shell/ShellSubscriber_test.cpp
+++ b/statsd/tests/shell/ShellSubscriber_test.cpp
@@ -222,7 +222,7 @@
     TestAtomReported t;
     auto* attributionNode = t.add_attribution_node();
     attributionNode->set_uid(1001);
-    attributionNode->set_tag("app1");
+    attributionNode->set_tag("app");  // String transformation removes trailing digits.
     t.set_int_field(intFieldValue);
     t.set_long_field(0);
     t.set_float_field(0.0f);
@@ -252,6 +252,15 @@
 
         ShellSubscription config;
         config.add_pushed()->set_atom_id(TEST_ATOM_REPORTED);
+        FieldValueMatcher* fvm = config.mutable_pushed(0)->add_field_value_matcher();
+        fvm->set_field(1);  // attribution_chain
+        fvm->set_position(Position::FIRST);
+        fvm = fvm->mutable_matches_tuple()->add_field_value_matcher();
+        fvm->set_field(2);  // tag field
+        fvm->mutable_replace_string()->set_regex(
+                R"([0-9]+$)");  // match trailing digits, example "42" in "foo42".
+        fvm->mutable_replace_string()->set_replacement("");
+
         config.add_pushed()->set_atom_id(SCREEN_STATE_CHANGED);
         config.add_pushed()->set_atom_id(PHONE_SIGNAL_STRENGTH_CHANGED);
         configBytes = protoToBytes(config);
diff --git a/statsd/tests/statsd_test_util.cpp b/statsd/tests/statsd_test_util.cpp
index 084fb2f..8e80a20 100644
--- a/statsd/tests/statsd_test_util.cpp
+++ b/statsd/tests/statsd_test_util.cpp
@@ -1079,6 +1079,18 @@
                                        repeatedBoolFieldLength, repeatedEnumField);
 }
 
+std::unique_ptr<LogEvent> CreateTestAtomReportedEventWithPrimitives(
+        uint64_t timestampNs, int intField, long longField, float floatField,
+        const string& stringField, bool boolField, TestAtomReported::State enumField) {
+    return CreateTestAtomReportedEvent(
+            timestampNs, /* attributionUids */ {1001},
+            /* attributionTags */ {"app1"}, intField, longField, floatField, stringField, boolField,
+            enumField, /* bytesField */ {},
+            /* repeatedIntField */ {}, /* repeatedLongField */ {}, /* repeatedFloatField */ {},
+            /* repeatedStringField */ {}, /* repeatedBoolField */ {},
+            /* repeatedBoolFieldLength */ 0, /* repeatedEnumField */ {});
+}
+
 std::unique_ptr<LogEvent> CreateTestAtomReportedEvent(
         uint64_t timestampNs, const vector<int>& attributionUids,
         const vector<string>& attributionTags, const int intField, const long longField,
@@ -1160,14 +1172,15 @@
 }
 
 std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent(
-        uint64_t timestampNs, const int uid, const ActivityForegroundStateChanged::State state) {
+        uint64_t timestampNs, const int uid, const string& pkgName, const string& className,
+        const ActivityForegroundStateChanged::State state) {
     AStatsEvent* statsEvent = AStatsEvent_obtain();
     AStatsEvent_setAtomId(statsEvent, util::ACTIVITY_FOREGROUND_STATE_CHANGED);
     AStatsEvent_overwriteTimestamp(statsEvent, timestampNs);
 
     AStatsEvent_writeInt32(statsEvent, uid);
-    AStatsEvent_writeString(statsEvent, "pkg_name");
-    AStatsEvent_writeString(statsEvent, "class_name");
+    AStatsEvent_writeString(statsEvent, pkgName.c_str());
+    AStatsEvent_writeString(statsEvent, className.c_str());
     AStatsEvent_writeInt32(statsEvent, state);
 
     std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(/*uid=*/0, /*pid=*/0);
@@ -1176,12 +1189,12 @@
 }
 
 std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(uint64_t timestampNs, const int uid) {
-    return CreateActivityForegroundStateChangedEvent(timestampNs, uid,
+    return CreateActivityForegroundStateChangedEvent(timestampNs, uid, "pkg_name", "class_name",
                                                      ActivityForegroundStateChanged::BACKGROUND);
 }
 
 std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(uint64_t timestampNs, const int uid) {
-    return CreateActivityForegroundStateChangedEvent(timestampNs, uid,
+    return CreateActivityForegroundStateChangedEvent(timestampNs, uid, "pkg_name", "class_name",
                                                      ActivityForegroundStateChanged::FOREGROUND);
 }
 
diff --git a/statsd/tests/statsd_test_util.h b/statsd/tests/statsd_test_util.h
index ca38944..d2d10b2 100644
--- a/statsd/tests/statsd_test_util.h
+++ b/statsd/tests/statsd_test_util.h
@@ -334,31 +334,29 @@
                                                     const std::vector<Position>& positions,
                                                     const std::vector<int>& fields);
 
-EventMetric createEventMetric(const string& name, const int64_t what,
-                              const optional<int64_t>& condition);
+EventMetric createEventMetric(const string& name, int64_t what, const optional<int64_t>& condition);
 
-CountMetric createCountMetric(const string& name, const int64_t what,
-                              const optional<int64_t>& condition, const vector<int64_t>& states);
+CountMetric createCountMetric(const string& name, int64_t what, const optional<int64_t>& condition,
+                              const vector<int64_t>& states);
 
-DurationMetric createDurationMetric(const string& name, const int64_t what,
+DurationMetric createDurationMetric(const string& name, int64_t what,
                                     const optional<int64_t>& condition,
                                     const vector<int64_t>& states);
 
-GaugeMetric createGaugeMetric(const string& name, const int64_t what,
+GaugeMetric createGaugeMetric(const string& name, int64_t what,
                               const GaugeMetric::SamplingType samplingType,
                               const optional<int64_t>& condition,
                               const optional<int64_t>& triggerEvent);
 
-ValueMetric createValueMetric(const string& name, const AtomMatcher& what, const int valueField,
+ValueMetric createValueMetric(const string& name, const AtomMatcher& what, int valueField,
                               const optional<int64_t>& condition, const vector<int64_t>& states);
 
-KllMetric createKllMetric(const string& name, const AtomMatcher& what, const int kllField,
+KllMetric createKllMetric(const string& name, const AtomMatcher& what, int kllField,
                           const optional<int64_t>& condition);
 
-Alert createAlert(const string& name, const int64_t metricId, const int buckets,
-                  const int64_t triggerSum);
+Alert createAlert(const string& name, int64_t metricId, int buckets, const int64_t triggerSum);
 
-Alarm createAlarm(const string& name, const int64_t offsetMillis, const int64_t periodMillis);
+Alarm createAlarm(const string& name, int64_t offsetMillis, int64_t periodMillis);
 
 Subscription createSubscription(const string& name, const Subscription_RuleType type,
                                 const int64_t ruleId);
@@ -477,11 +475,15 @@
 // Create malformed log event for battery state change.
 std::unique_ptr<LogEvent> CreateMalformedBatteryStateChangedEvent(const uint64_t timestampNs);
 
+std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent(
+        uint64_t timestampNs, const int uid, const string& pkgName, const string& className,
+        const ActivityForegroundStateChanged::State state);
+
 // Create log event for app moving to background.
-std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(uint64_t timestampNs, const int uid);
+std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(uint64_t timestampNs, int uid);
 
 // Create log event for app moving to foreground.
-std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(uint64_t timestampNs, const int uid);
+std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(uint64_t timestampNs, int uid);
 
 // Create log event when the app sync starts.
 std::unique_ptr<LogEvent> CreateSyncStartEvent(uint64_t timestampNs, const vector<int>& uids,
@@ -492,10 +494,10 @@
                                              const vector<string>& tags, const string& name);
 
 // Create log event when the app sync ends.
-std::unique_ptr<LogEvent> CreateAppCrashEvent(uint64_t timestampNs, const int uid);
+std::unique_ptr<LogEvent> CreateAppCrashEvent(uint64_t timestampNs, int uid);
 
 // Create log event for an app crash.
-std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(uint64_t timestampNs, const int uid);
+std::unique_ptr<LogEvent> CreateAppCrashOccurredEvent(uint64_t timestampNs, int uid);
 
 // Create log event for acquiring wakelock.
 std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(uint64_t timestampNs, const vector<int>& uids,
@@ -528,7 +530,7 @@
                                                          const OverlayStateChanged::State state);
 
 std::unique_ptr<LogEvent> CreateAppStartOccurredEvent(
-        uint64_t timestampNs, const int uid, const string& pkg_name,
+        uint64_t timestampNs, int uid, const string& pkg_name,
         AppStartOccurred::TransitionType type, const string& activity_name,
         const string& calling_pkg_name, const bool is_instant_app, int64_t activity_start_msec);
 
@@ -543,6 +545,10 @@
         const vector<string>& repeatedStringField, const bool* repeatedBoolField,
         const size_t repeatedBoolFieldLength, const vector<int>& repeatedEnumField);
 
+std::unique_ptr<LogEvent> CreateTestAtomReportedEventWithPrimitives(
+        uint64_t timestampNs, int intField, long longField, float floatField,
+        const string& stringField, bool boolField, TestAtomReported::State enumField);
+
 std::unique_ptr<LogEvent> CreateRestrictedLogEvent(int atomTag, int64_t timestampNs = 0);
 std::unique_ptr<LogEvent> CreateNonRestrictedLogEvent(int atomTag, int64_t timestampNs = 0);
 
@@ -551,7 +557,7 @@
 
 std::unique_ptr<LogEvent> CreateTestAtomReportedEvent(
         uint64_t timestampNs, const vector<int>& attributionUids,
-        const vector<string>& attributionTags, const int intField, const long longField,
+        const vector<string>& attributionTags, int intField, const long longField,
         const float floatField, const string& stringField, const bool boolField,
         const TestAtomReported::State enumField, const vector<uint8_t>& bytesField,
         const vector<int>& repeatedIntField, const vector<int64_t>& repeatedLongField,
@@ -565,7 +571,7 @@
 
 // Create a statsd log event processor upon the start time in seconds, config and key.
 sp<StatsLogProcessor> CreateStatsLogProcessor(
-        const int64_t timeBaseNs, const int64_t currentTimeNs, const StatsdConfig& config,
+        const int64_t timeBaseNs, int64_t currentTimeNs, const StatsdConfig& config,
         const ConfigKey& key, const shared_ptr<IPullAtomCallback>& puller = nullptr,
         const int32_t atomTag = 0 /*for puller only*/, const sp<UidMap> = new UidMap(),
         const shared_ptr<LogEventFilter>& logEventFilter = std::make_shared<LogEventFilter>());
@@ -585,7 +591,7 @@
                                                                      const int uid);
 
 void ValidateUidDimension(const DimensionsValue& value, int atomId, int uid);
-void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, const int atomId,
+void ValidateWakelockAttributionUidAndTagDimension(const DimensionsValue& value, int atomId,
                                                    const int uid, const string& tag);
 void ValidateUidDimension(const DimensionsValue& value, int node_idx, int atomId, int uid);
 void ValidateAttributionUidDimension(const DimensionsValue& value, int atomId, int uid);
@@ -712,8 +718,8 @@
 }
 
 template <typename T>
-void backfillStartEndTimestampForFullBucket(
-    const int64_t timeBaseNs, const int64_t bucketSizeNs, T* bucket) {
+void backfillStartEndTimestampForFullBucket(const int64_t timeBaseNs, int64_t bucketSizeNs,
+                                            T* bucket) {
     bucket->set_start_bucket_elapsed_nanos(timeBaseNs + bucketSizeNs * bucket->bucket_num());
     bucket->set_end_bucket_elapsed_nanos(
         timeBaseNs + bucketSizeNs * bucket->bucket_num() + bucketSizeNs);
@@ -735,7 +741,7 @@
 }
 
 template <typename T>
-void backfillStartEndTimestampForMetrics(const int64_t timeBaseNs, const int64_t bucketSizeNs,
+void backfillStartEndTimestampForMetrics(const int64_t timeBaseNs, int64_t bucketSizeNs,
                                          T* metrics) {
     for (int i = 0; i < metrics->data_size(); ++i) {
         auto data = metrics->mutable_data(i);
@@ -790,10 +796,10 @@
 
 PackageInfoSnapshot getPackageInfoSnapshot(const sp<UidMap> uidMap);
 
-ApplicationInfo createApplicationInfo(const int32_t uid, const int64_t version,
-                                      const string& versionString, const string& package);
+ApplicationInfo createApplicationInfo(int32_t uid, int64_t version, const string& versionString,
+                                      const string& package);
 
-PackageInfo buildPackageInfo(const std::string& name, const int32_t uid, const int64_t version,
+PackageInfo buildPackageInfo(const std::string& name, const int32_t uid, int64_t version,
                              const std::string& versionString,
                              const std::optional<std::string> installer,
                              const std::vector<uint8_t>& certHash, const bool deleted,
diff --git a/tests/apps/statsdapp/Android.bp b/tests/apps/statsdapp/Android.bp
index a45b95c..7dd3d19 100644
--- a/tests/apps/statsdapp/Android.bp
+++ b/tests/apps/statsdapp/Android.bp
@@ -51,7 +51,7 @@
         "androidx.legacy_legacy-support-v4",
         "androidx.test.rules",
         "cts-net-utils",
-        "BlobStoreTestUtils"
+        "BlobStoreTestUtils",
     ],
     jni_libs: ["liblmkhelper"],
     compile_multilib: "both",
@@ -60,6 +60,10 @@
 genrule {
     name: "statslog-statsd-cts-java-gen",
     tools: ["stats-log-api-gen"],
-    cmd: "$(location stats-log-api-gen) --java $(out) --module cts --javaPackage com.android.server.cts.device.statsd --javaClass StatsLogStatsdCts",
+    cmd: "$(location stats-log-api-gen) " +
+        "--java $(out) " +
+        "--module cts " +
+        "--javaPackage com.android.server.cts.device.statsd " +
+        "--javaClass StatsLogStatsdCts",
     out: ["com/android/server/cts/device/statsd/StatsLogStatsdCts.java"],
 }
diff --git a/tests/src/android/cts/statsd/atom/AtomTestCase.java b/tests/src/android/cts/statsd/atom/AtomTestCase.java
index 0e6c9c6..b0825a7 100644
--- a/tests/src/android/cts/statsd/atom/AtomTestCase.java
+++ b/tests/src/android/cts/statsd/atom/AtomTestCase.java
@@ -215,7 +215,7 @@
 
         TraceConfig.IncidentReportConfig incident = TraceConfig.IncidentReportConfig
             .newBuilder()
-            .setDestinationPackage("foo.bar.baz")
+            .setSkipIncidentd(true)
             .build();
         builder.setIncidentReportConfig(incident);
 
diff --git a/tests/utils/tests/Android.bp b/tests/utils/tests/Android.bp
index ae7b582..58063ff 100644
--- a/tests/utils/tests/Android.bp
+++ b/tests/utils/tests/Android.bp
@@ -48,6 +48,10 @@
 genrule {
     name: "statslog-statsdtest-java-gen",
     tools: ["stats-log-api-gen"],
-    cmd: "$(location stats-log-api-gen) --java $(out) --module statsdtest --javaPackage android.util --javaClass StatsdTestStatsLog",
+    cmd: "$(location stats-log-api-gen) " +
+        "--java $(out) " +
+        "--module statsdtest " +
+        "--javaPackage android.util " +
+        "--javaClass StatsdTestStatsLog",
     out: ["android/util/StatsdTestStatsLog.java"],
 }