Add StatsdStats and guardrail.

+ StatsdStats is the global class that tracks the stats about statsd.

+ Added guardrail for classes that have a map which could potentially grow
  unboundedly with the number of logs.

TODO: add unit tests & CTS for StatsdStats, and guardrail
      add stats for pulled atoms.

Test: statsd_test

Change-Id: I0ea562de4dd3f6162f7923a9c193420b482c1d51
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 4d975fc..1f15c5e 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -57,7 +57,8 @@
     src/StatsLogProcessor.cpp \
     src/StatsService.cpp \
     src/stats_util.cpp \
-    src/guardrail/MemoryLeakTrackUtil.cpp
+    src/guardrail/MemoryLeakTrackUtil.cpp \
+    src/guardrail/StatsdStats.cpp
 
 statsd_common_c_includes := \
     $(LOCAL_PATH)/src \
@@ -167,7 +168,8 @@
     tests/metrics/MaxDurationTracker_test.cpp \
     tests/metrics/CountMetricProducer_test.cpp \
     tests/metrics/EventMetricProducer_test.cpp \
-    tests/metrics/ValueMetricProducer_test.cpp
+    tests/metrics/ValueMetricProducer_test.cpp \
+    tests/guardrail/StatsdStats_test.cpp
 
 LOCAL_STATIC_LIBRARIES := \
     libgmock
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index b5cb20c..2690c7e 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 
+#define DEBUG true  // STOPSHIP if true
 #include "Log.h"
 #include "statslog.h"
 
@@ -21,6 +22,7 @@
 #include <dirent.h>
 #include "StatsLogProcessor.h"
 #include "android-base/stringprintf.h"
+#include "guardrail/StatsdStats.h"
 #include "metrics/CountMetricProducer.h"
 #include "stats_util.h"
 #include "storage/StorageManager.h"
@@ -82,6 +84,7 @@
 
 // TODO: what if statsd service restarts? How do we know what logs are already processed before?
 void StatsLogProcessor::OnLogEvent(const LogEvent& msg) {
+    StatsdStats::getInstance().noteAtomLogged(msg.GetTagId(), msg.GetTimestampNs() / NS_PER_SEC);
     // pass the event to metrics managers.
     for (auto& pair : mMetricsManagers) {
         pair.second->onLogEvent(msg);
@@ -106,23 +109,26 @@
 }
 
 void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) {
+    ALOGD("Updated configuration for key %s", key.ToString().c_str());
+    unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(key, config);
+
     auto it = mMetricsManagers.find(key);
     if (it != mMetricsManagers.end()) {
         it->second->finish();
+    } else if (mMetricsManagers.size() > StatsdStats::kMaxConfigCount) {
+        ALOGE("Can't accept more configs!");
+        return;
     }
 
-    ALOGD("Updated configuration for key %s", key.ToString().c_str());
-
-    unique_ptr<MetricsManager> newMetricsManager = std::make_unique<MetricsManager>(config);
     if (newMetricsManager->isConfigValid()) {
         mUidMap->OnConfigUpdated(key);
         newMetricsManager->setAnomalyMonitor(mAnomalyMonitor);
         mMetricsManagers[key] = std::move(newMetricsManager);
         // Why doesn't this work? mMetricsManagers.insert({key, std::move(newMetricsManager)});
-        ALOGD("StatsdConfig valid");
+        VLOG("StatsdConfig valid");
     } else {
         // If there is any error in the config, don't use it.
-        ALOGD("StatsdConfig NOT valid");
+        ALOGE("StatsdConfig NOT valid");
     }
 }
 
@@ -204,6 +210,7 @@
         mMetricsManagers.erase(it);
         mUidMap->OnConfigRemoved(key);
     }
+    StatsdStats::getInstance().noteConfigRemoved(key);
 
     std::lock_guard<std::mutex> lock(mBroadcastTimesMutex);
     mLastBroadcastTimes.erase(key);
@@ -223,12 +230,14 @@
             }
         }
         mLastBroadcastTimes[key] = timestampNs;
-        ALOGD("StatsD requesting broadcast for %s", key.ToString().c_str());
+        VLOG("StatsD requesting broadcast for %s", key.ToString().c_str());
         mSendBroadcast(key);
+        StatsdStats::getInstance().noteBroadcastSent(key);
     } else if (totalBytes > kMaxSerializedBytes) { // Too late. We need to start clearing data.
         // We ignore the return value so we force each metric producer to clear its contents.
         metricsManager->onDumpReport();
-        ALOGD("StatsD had to toss out metrics for %s", key.ToString().c_str());
+        StatsdStats::getInstance().noteDataDrop(key);
+        VLOG("StatsD had to toss out metrics for %s", key.ToString().c_str());
     }
 }
 
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index ec2650e..8b64f0d 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -22,6 +22,7 @@
 #include "config/ConfigKey.h"
 #include "config/ConfigManager.h"
 #include "guardrail/MemoryLeakTrackUtil.h"
+#include "guardrail/StatsdStats.h"
 #include "storage/DropboxReader.h"
 #include "storage/StorageManager.h"
 
@@ -222,7 +223,7 @@
         }
 
         if (!args[0].compare(String8("print-stats"))) {
-            return cmd_print_stats(out);
+            return cmd_print_stats(out, args);
         }
 
         if (!args[0].compare(String8("clear-config"))) {
@@ -305,8 +306,9 @@
     fprintf(out, "  NAME          The name of the configuration\n");
     fprintf(out, "\n");
     fprintf(out, "\n");
-    fprintf(out, "usage: adb shell cmd stats print-stats\n");
+    fprintf(out, "usage: adb shell cmd stats print-stats [reset]\n");
     fprintf(out, "  Prints some basic stats.\n");
+    fprintf(out, "  reset: 1 to reset the statsd stats. default=0.\n");
 }
 
 status_t StatsService::cmd_trigger_broadcast(FILE* out, Vector<String8>& args) {
@@ -474,12 +476,20 @@
     }
 }
 
-status_t StatsService::cmd_print_stats(FILE* out) {
+status_t StatsService::cmd_print_stats(FILE* out, const Vector<String8>& args) {
     vector<ConfigKey> configs = mConfigManager->GetAllConfigKeys();
     for (const ConfigKey& key : configs) {
         fprintf(out, "Config %s uses %zu bytes\n", key.ToString().c_str(),
                 mProcessor->GetMetricsSize(key));
     }
+    fprintf(out, "Detailed statsd stats in logcat...");
+    StatsdStats& statsdStats = StatsdStats::getInstance();
+    bool reset = false;
+    if (args.size() > 1) {
+        reset = strtol(args[1].string(), NULL, 10);
+    }
+    vector<int8_t> output;
+    statsdStats.dumpStats(&output, reset);
     return NO_ERROR;
 }
 
diff --git a/cmds/statsd/src/StatsService.h b/cmds/statsd/src/StatsService.h
index 6b5c156..a32595a 100644
--- a/cmds/statsd/src/StatsService.h
+++ b/cmds/statsd/src/StatsService.h
@@ -134,7 +134,7 @@
     /**
      * Prints some basic stats to std out.
      */
-    status_t cmd_print_stats(FILE* out);
+    status_t cmd_print_stats(FILE* out, const Vector<String8>& args);
 
     /**
      * Print the event log.
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index d8099b3..50cd130 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -18,6 +18,7 @@
 #include "Log.h"
 
 #include "SimpleConditionTracker.h"
+#include "guardrail/StatsdStats.h"
 
 #include <log/logprint.h>
 
@@ -32,9 +33,10 @@
 using std::vector;
 
 SimpleConditionTracker::SimpleConditionTracker(
-        const string& name, const int index, const SimpleCondition& simpleCondition,
+        const ConfigKey& key, const string& name, const int index,
+        const SimpleCondition& simpleCondition,
         const unordered_map<string, int>& trackerNameIndexMap)
-    : ConditionTracker(name, index) {
+    : ConditionTracker(name, index), mConfigKey(key) {
     VLOG("creating SimpleConditionTracker %s", mName.c_str());
     mCountNesting = simpleCondition.count_nesting();
 
@@ -126,6 +128,24 @@
     conditionCache[mIndex] = ConditionState::kFalse;
 }
 
+bool SimpleConditionTracker::hitGuardRail(const HashableDimensionKey& newKey) {
+    if (!mSliced || mSlicedConditionState.find(newKey) != mSlicedConditionState.end()) {
+        // if the condition is not sliced or the key is not new, we are good!
+        return false;
+    }
+    // 1. Report the tuple count if the tuple count > soft limit
+    if (mSlicedConditionState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+        size_t newTupleCount = mSlicedConditionState.size() + 1;
+        StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mName, newTupleCount);
+        // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+        if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+            ALOGE("Condition %s dropping data for dimension key %s", mName.c_str(), newKey.c_str());
+            return true;
+        }
+    }
+    return false;
+}
+
 void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& outputKey,
                                                   bool matchStart,
                                                   std::vector<ConditionState>& conditionCache,
@@ -133,6 +153,12 @@
     bool changed = false;
     auto outputIt = mSlicedConditionState.find(outputKey);
     ConditionState newCondition;
+    if (hitGuardRail(outputKey)) {
+        conditionChangedCache[mIndex] = false;
+        // Tells the caller it's evaluated.
+        conditionCache[mIndex] = ConditionState::kUnknown;
+        return;
+    }
     if (outputIt == mSlicedConditionState.end()) {
         // We get a new output key.
         newCondition = matchStart ? ConditionState::kTrue : ConditionState::kFalse;
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index c287d8f..d21afd1 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -19,6 +19,7 @@
 
 #include <gtest/gtest_prod.h>
 #include "ConditionTracker.h"
+#include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "stats_util.h"
 
@@ -28,7 +29,7 @@
 
 class SimpleConditionTracker : public virtual ConditionTracker {
 public:
-    SimpleConditionTracker(const std::string& name, const int index,
+    SimpleConditionTracker(const ConfigKey& key, const std::string& name, const int index,
                            const SimpleCondition& simpleCondition,
                            const std::unordered_map<std::string, int>& trackerNameIndexMap);
 
@@ -50,6 +51,7 @@
                         std::vector<ConditionState>& conditionCache) const override;
 
 private:
+    const ConfigKey mConfigKey;
     // The index of the LogEventMatcher which defines the start.
     int mStartLogMatcherIndex;
 
@@ -75,6 +77,8 @@
                               std::vector<ConditionState>& conditionCache,
                               std::vector<bool>& changedCache);
 
+    bool hitGuardRail(const HashableDimensionKey& newKey);
+
     FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition);
     FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim);
     FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll);
diff --git a/cmds/statsd/src/guardrail/StatsdStats.cpp b/cmds/statsd/src/guardrail/StatsdStats.cpp
new file mode 100644
index 0000000..815e03f
--- /dev/null
+++ b/cmds/statsd/src/guardrail/StatsdStats.cpp
@@ -0,0 +1,342 @@
+/*
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define DEBUG true  // STOPSHIP if true
+#include "Log.h"
+
+#include "StatsdStats.h"
+
+#include <android/util/ProtoOutputStream.h>
+#include "statslog.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using android::util::FIELD_COUNT_REPEATED;
+using android::util::FIELD_TYPE_BOOL;
+using android::util::FIELD_TYPE_FLOAT;
+using android::util::FIELD_TYPE_INT32;
+using android::util::FIELD_TYPE_INT64;
+using android::util::FIELD_TYPE_MESSAGE;
+using android::util::FIELD_TYPE_STRING;
+using android::util::ProtoOutputStream;
+using std::lock_guard;
+using std::map;
+using std::string;
+using std::vector;
+
+const int FIELD_ID_BEGIN_TIME = 1;
+const int FIELD_ID_END_TIME = 2;
+const int FIELD_ID_CONFIG_STATS = 3;
+const int FIELD_ID_MATCHER_STATS = 4;
+const int FIELD_ID_CONDITION_STATS = 5;
+const int FIELD_ID_METRIC_STATS = 6;
+const int FIELD_ID_ATOM_STATS = 7;
+
+const int FIELD_ID_MATCHER_STATS_NAME = 1;
+const int FIELD_ID_MATCHER_STATS_COUNT = 2;
+
+const int FIELD_ID_CONDITION_STATS_NAME = 1;
+const int FIELD_ID_CONDITION_STATS_COUNT = 2;
+
+const int FIELD_ID_METRIC_STATS_NAME = 1;
+const int FIELD_ID_METRIC_STATS_COUNT = 2;
+
+const int FIELD_ID_ATOM_STATS_TAG = 1;
+const int FIELD_ID_ATOM_STATS_COUNT = 2;
+
+// TODO: add stats for pulled atoms.
+StatsdStats::StatsdStats() {
+    mPushedAtomStats.resize(android::util::kMaxPushedAtomId + 1);
+    mStartTime = time(nullptr);
+}
+
+StatsdStats& StatsdStats::getInstance() {
+    static StatsdStats statsInstance;
+    return statsInstance;
+}
+
+void StatsdStats::noteConfigReceived(const ConfigKey& key, int metricsCount, int conditionsCount,
+                                     int matchersCount, int alertsCount, bool isValid) {
+    lock_guard<std::mutex> lock(mLock);
+    int32_t nowTimeSec = time(nullptr);
+
+    // If there is an existing config for the same key, icebox the old config.
+    noteConfigRemovedInternalLocked(key);
+
+    StatsdStatsReport_ConfigStats configStats;
+    configStats.set_uid(key.GetUid());
+    configStats.set_name(key.GetName());
+    configStats.set_creation_time_sec(nowTimeSec);
+    configStats.set_metric_count(metricsCount);
+    configStats.set_condition_count(conditionsCount);
+    configStats.set_matcher_count(matchersCount);
+    configStats.set_alert_count(alertsCount);
+    configStats.set_is_valid(isValid);
+
+    if (isValid) {
+        mConfigStats[key] = configStats;
+    } else {
+        configStats.set_deletion_time_sec(nowTimeSec);
+        mIceBox.push_back(configStats);
+    }
+}
+
+void StatsdStats::noteConfigRemovedInternalLocked(const ConfigKey& key) {
+    auto it = mConfigStats.find(key);
+    if (it != mConfigStats.end()) {
+        int32_t nowTimeSec = time(nullptr);
+        it->second.set_deletion_time_sec(nowTimeSec);
+        // Add condition stats, metrics stats, matcher stats
+        addSubStatsToConfig(key, it->second);
+        // Remove them after they are added to the config stats.
+        mMatcherStats.erase(key);
+        mMetricsStats.erase(key);
+        mConditionStats.erase(key);
+        mIceBox.push_back(it->second);
+    }
+}
+
+void StatsdStats::noteConfigRemoved(const ConfigKey& key) {
+    lock_guard<std::mutex> lock(mLock);
+    noteConfigRemovedInternalLocked(key);
+}
+
+void StatsdStats::noteBroadcastSent(const ConfigKey& key) {
+    lock_guard<std::mutex> lock(mLock);
+    auto it = mConfigStats.find(key);
+    if (it == mConfigStats.end()) {
+        ALOGE("Config key %s not found!", key.ToString().c_str());
+        return;
+    }
+
+    it->second.add_broadcast_sent_time_sec(time(nullptr));
+}
+
+void StatsdStats::noteDataDrop(const ConfigKey& key) {
+    lock_guard<std::mutex> lock(mLock);
+    auto it = mConfigStats.find(key);
+    if (it == mConfigStats.end()) {
+        ALOGE("Config key %s not found!", key.ToString().c_str());
+        return;
+    }
+
+    it->second.add_data_drop_time_sec(time(nullptr));
+}
+
+void StatsdStats::noteConditionDimensionSize(const ConfigKey& key, const string& name, int size) {
+    lock_guard<std::mutex> lock(mLock);
+    // if name doesn't exist before, it will create the key with count 0.
+    auto& conditionSizeMap = mConditionStats[key];
+    if (size > conditionSizeMap[name]) {
+        conditionSizeMap[name] = size;
+    }
+}
+
+void StatsdStats::noteMetricDimensionSize(const ConfigKey& key, const string& name, int size) {
+    lock_guard<std::mutex> lock(mLock);
+    // if name doesn't exist before, it will create the key with count 0.
+    auto& metricsDimensionMap = mMetricsStats[key];
+    if (size > metricsDimensionMap[name]) {
+        metricsDimensionMap[name] = size;
+    }
+}
+
+void StatsdStats::noteMatcherMatched(const ConfigKey& key, const string& name) {
+    lock_guard<std::mutex> lock(mLock);
+    auto& matcherStats = mMatcherStats[key];
+    matcherStats[name]++;
+}
+
+void StatsdStats::noteAtomLogged(int atomId, int32_t timeSec) {
+    lock_guard<std::mutex> lock(mLock);
+
+    if (timeSec < mStartTime) {
+        return;
+    }
+
+    if (atomId > android::util::kMaxPushedAtomId) {
+        ALOGW("not interested in atom %d", atomId);
+        return;
+    }
+
+    mPushedAtomStats[atomId]++;
+}
+
+void StatsdStats::reset() {
+    lock_guard<std::mutex> lock(mLock);
+    resetInternalLocked();
+}
+
+void StatsdStats::resetInternalLocked() {
+    // Reset the historical data, but keep the active ConfigStats
+    mStartTime = time(nullptr);
+    mIceBox.clear();
+    mConditionStats.clear();
+    mMetricsStats.clear();
+    std::fill(mPushedAtomStats.begin(), mPushedAtomStats.end(), 0);
+    mMatcherStats.clear();
+}
+
+void StatsdStats::addSubStatsToConfig(const ConfigKey& key,
+                                      StatsdStatsReport_ConfigStats& configStats) {
+    // Add matcher stats
+    if (mMatcherStats.find(key) != mMatcherStats.end()) {
+        const auto& matcherStats = mMatcherStats[key];
+        for (const auto& stats : matcherStats) {
+            auto output = configStats.add_matcher_stats();
+            output->set_name(stats.first);
+            output->set_matched_times(stats.second);
+            VLOG("matcher %s matched %d times", stats.first.c_str(), stats.second);
+        }
+    }
+    // Add condition stats
+    if (mConditionStats.find(key) != mConditionStats.end()) {
+        const auto& conditionStats = mConditionStats[key];
+        for (const auto& stats : conditionStats) {
+            auto output = configStats.add_condition_stats();
+            output->set_name(stats.first);
+            output->set_max_tuple_counts(stats.second);
+            VLOG("condition %s max output tuple size %d", stats.first.c_str(), stats.second);
+        }
+    }
+    // Add metrics stats
+    if (mMetricsStats.find(key) != mMetricsStats.end()) {
+        const auto& conditionStats = mMetricsStats[key];
+        for (const auto& stats : conditionStats) {
+            auto output = configStats.add_metric_stats();
+            output->set_name(stats.first);
+            output->set_max_tuple_counts(stats.second);
+            VLOG("metrics %s max output tuple size %d", stats.first.c_str(), stats.second);
+        }
+    }
+}
+
+void StatsdStats::dumpStats(std::vector<int8_t>* output, bool reset) {
+    lock_guard<std::mutex> lock(mLock);
+
+    if (DEBUG) {
+        time_t t = time(nullptr);
+        struct tm* tm = localtime(&t);
+        char timeBuffer[80];
+        strftime(timeBuffer, sizeof(timeBuffer), "%Y-%m-%d %I:%M%p", tm);
+        VLOG("=================StatsdStats dump begins====================");
+        VLOG("Stats collection start second: %s", timeBuffer);
+    }
+    ProtoOutputStream proto;
+    proto.write(FIELD_TYPE_INT32 | FIELD_ID_BEGIN_TIME, mStartTime);
+    proto.write(FIELD_TYPE_INT32 | FIELD_ID_END_TIME, (int32_t)time(nullptr));
+
+    VLOG("%lu Config in icebox: ", (unsigned long)mIceBox.size());
+    for (const auto& configStats : mIceBox) {
+        const int numBytes = configStats.ByteSize();
+        vector<char> buffer(numBytes);
+        configStats.SerializeToArray(&buffer[0], numBytes);
+        proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS, &buffer[0],
+                    buffer.size());
+
+        // surround the whole block with DEBUG, so that compiler can strip out the code
+        // in production.
+        if (DEBUG) {
+            VLOG("*****ICEBOX*****");
+            VLOG("Config {%d-%s}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
+                 "#matcher=%d, #alert=%d,  #valid=%d",
+                 configStats.uid(), configStats.name().c_str(), configStats.creation_time_sec(),
+                 configStats.deletion_time_sec(), configStats.metric_count(),
+                 configStats.condition_count(), configStats.matcher_count(),
+                 configStats.alert_count(), configStats.is_valid());
+
+            for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) {
+                VLOG("\tbroadcast time: %d", broadcastTime);
+            }
+
+            for (const auto& dataDropTime : configStats.data_drop_time_sec()) {
+                VLOG("\tdata drop time: %d", dataDropTime);
+            }
+        }
+    }
+
+    for (auto& pair : mConfigStats) {
+        auto& configStats = pair.second;
+        if (DEBUG) {
+            VLOG("********Active Configs***********");
+            VLOG("Config {%d-%s}: creation=%d, deletion=%d, #metric=%d, #condition=%d, "
+                 "#matcher=%d, #alert=%d,  #valid=%d",
+                 configStats.uid(), configStats.name().c_str(), configStats.creation_time_sec(),
+                 configStats.deletion_time_sec(), configStats.metric_count(),
+                 configStats.condition_count(), configStats.matcher_count(),
+                 configStats.alert_count(), configStats.is_valid());
+            for (const auto& broadcastTime : configStats.broadcast_sent_time_sec()) {
+                VLOG("\tbroadcast time: %d", broadcastTime);
+            }
+
+            for (const auto& dataDropTime : configStats.data_drop_time_sec()) {
+                VLOG("\tdata drop time: %d", dataDropTime);
+            }
+        }
+
+        addSubStatsToConfig(pair.first, configStats);
+
+        const int numBytes = configStats.ByteSize();
+        vector<char> buffer(numBytes);
+        configStats.SerializeToArray(&buffer[0], numBytes);
+        proto.write(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_CONFIG_STATS, &buffer[0],
+                    buffer.size());
+        // reset the sub stats, the source of truth is in the individual map
+        // they will be repopulated when dumpStats() is called again.
+        configStats.clear_matcher_stats();
+        configStats.clear_condition_stats();
+        configStats.clear_metric_stats();
+    }
+
+    VLOG("********Atom stats***********");
+    const size_t atomCounts = mPushedAtomStats.size();
+    for (size_t i = 2; i < atomCounts; i++) {
+        if (mPushedAtomStats[i] > 0) {
+            long long token =
+                    proto.start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM_STATS | FIELD_COUNT_REPEATED);
+            proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_TAG, (int32_t)i);
+            proto.write(FIELD_TYPE_INT32 | FIELD_ID_ATOM_STATS_COUNT, mPushedAtomStats[i]);
+            proto.end(token);
+
+            VLOG("Atom %lu->%d\n", (unsigned long)i, mPushedAtomStats[i]);
+        }
+    }
+
+    output->clear();
+    size_t bufferSize = proto.size();
+    output->resize(bufferSize);
+
+    size_t pos = 0;
+    auto it = proto.data();
+    while (it.readBuffer() != NULL) {
+        size_t toRead = it.currentToRead();
+        std::memcpy(&((*output)[pos]), it.readBuffer(), toRead);
+        pos += toRead;
+        it.rp()->move(toRead);
+    }
+
+    if (reset) {
+        resetInternalLocked();
+    }
+
+    VLOG("reset=%d, returned proto size %lu", reset, (unsigned long)bufferSize);
+    VLOG("=================StatsdStats dump ends====================");
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/guardrail/StatsdStats.h b/cmds/statsd/src/guardrail/StatsdStats.h
new file mode 100644
index 0000000..73ce279
--- /dev/null
+++ b/cmds/statsd/src/guardrail/StatsdStats.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright 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 "config/ConfigKey.h"
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+
+#include <mutex>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Keeps track of stats of statsd.
+// Single instance shared across the process. All methods are thread safe.
+class StatsdStats {
+public:
+    static StatsdStats& getInstance();
+    ~StatsdStats(){};
+
+    // TODO: set different limit if the device is low ram.
+    const static int kDimensionKeySizeSoftLimit = 300;
+    const static int kDimensionKeySizeHardLimit = 500;
+
+    const static int kMaxConfigCount = 10;
+    const static int kMaxConditionCountPerConfig = 200;
+    const static int kMaxMetricCountPerConfig = 300;
+    const static int kMaxMatcherCountPerConfig = 500;
+
+    /**
+     * Report a new config has been received and report the static stats about the config.
+     *
+     * The static stats include: the count of metrics, conditions, matchers, and alerts.
+     * If the config is not valid, this config stats will be put into icebox immediately.
+     */
+    void noteConfigReceived(const ConfigKey& key, int metricsCount, int conditionsCount,
+                            int matchersCount, int alertCount, bool isValid);
+    /**
+     * Report a config has been removed.
+     */
+    void noteConfigRemoved(const ConfigKey& key);
+
+    /**
+     * Report a broadcast has been sent to a config owner to collect the data.
+     */
+    void noteBroadcastSent(const ConfigKey& key);
+
+    /**
+     * Report a config's metrics data has been dropped.
+     */
+    void noteDataDrop(const ConfigKey& key);
+
+    /**
+     * Report the size of output tuple of a condition.
+     *
+     * Note: only report when the condition has an output dimension, and the tuple
+     * count > kDimensionKeySizeSoftLimit.
+     *
+     * [key]: The config key that this condition belongs to.
+     * [name]: The name of the condition.
+     * [size]: The output tuple size.
+     */
+    void noteConditionDimensionSize(const ConfigKey& key, const std::string& name, int size);
+
+    /**
+     * Report the size of output tuple of a metric.
+     *
+     * Note: only report when the metric has an output dimension, and the tuple
+     * count > kDimensionKeySizeSoftLimit.
+     *
+     * [key]: The config key that this metric belongs to.
+     * [name]: The name of the metric.
+     * [size]: The output tuple size.
+     */
+    void noteMetricDimensionSize(const ConfigKey& key, const std::string& name, int size);
+
+    /**
+     * Report a matcher has been matched.
+     *
+     * [key]: The config key that this matcher belongs to.
+     * [name]: The name of the matcher.
+     */
+    void noteMatcherMatched(const ConfigKey& key, const std::string& name);
+
+    /**
+     * Report an atom event has been logged.
+     */
+    void noteAtomLogged(int atomId, int32_t timeSec);
+
+    /**
+     * Reset the historical stats. Including all stats in icebox, and the tracked stats about
+     * metrics, matchers, and atoms. The active configs will be kept and StatsdStats will continue
+     * to collect stats after reset() has been called.
+     */
+    void reset();
+
+    /**
+     * Output the stats in protobuf binary format to [buffer].
+     *
+     * [reset]: whether to clear the historical stats after the call.
+     */
+    void dumpStats(std::vector<int8_t>* buffer, bool reset);
+
+private:
+    StatsdStats();
+
+    mutable std::mutex mLock;
+
+    int32_t mStartTime;
+
+    // The stats about the configs that are still in use.
+    std::map<const ConfigKey, StatsdStatsReport_ConfigStats> mConfigStats;
+
+    // Stores the stats for the configs that are no longer in use.
+    std::vector<const StatsdStatsReport_ConfigStats> mIceBox;
+
+    // Stores the number of output tuple of condition trackers when it's bigger than
+    // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1,
+    // it means some data has been dropped.
+    std::map<const ConfigKey, std::map<const std::string, int>> mConditionStats;
+
+    // Stores the number of output tuple of metric producers when it's bigger than
+    // kDimensionKeySizeSoftLimit. When you see the number is kDimensionKeySizeHardLimit +1,
+    // it means some data has been dropped.
+    std::map<const ConfigKey, std::map<const std::string, int>> mMetricsStats;
+
+    // Stores the number of times a pushed atom is logged.
+    // The size of the vector is the largest pushed atom id in atoms.proto + 1. Atoms
+    // out of that range will be dropped (it's either pulled atoms or test atoms).
+    // This is a vector, not a map because it will be accessed A LOT -- for each stats log.
+    std::vector<int> mPushedAtomStats;
+
+    // Stores how many times a matcher have been matched.
+    std::map<const ConfigKey, std::map<const std::string, int>> mMatcherStats;
+
+    void noteConfigRemovedInternalLocked(const ConfigKey& key);
+
+    void resetInternalLocked();
+
+    void addSubStatsToConfig(const ConfigKey& key, StatsdStatsReport_ConfigStats& configStats);
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h
index ffbf248..fea3e9b 100644
--- a/cmds/statsd/src/matchers/LogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/LogMatchingTracker.h
@@ -69,6 +69,10 @@
         return mTagIds;
     }
 
+    const std::string& getName() const {
+        return mName;
+    }
+
 protected:
     // Name of this matching. We don't really need the name, but it makes log message easy to debug.
     const std::string mName;
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
index b2c88a0..ad37b01 100644
--- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
@@ -19,8 +19,6 @@
 
 #include "SimpleLogMatchingTracker.h"
 
-#include <log/logprint.h>
-
 namespace android {
 namespace os {
 namespace statsd {
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 149b9c1..ce60eb9 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -18,6 +18,7 @@
 #include "Log.h"
 
 #include "CountMetricProducer.h"
+#include "guardrail/StatsdStats.h"
 #include "stats_util.h"
 
 #include <limits.h>
@@ -63,10 +64,11 @@
 
 // TODO: add back AnomalyTracker.
 
-CountMetricProducer::CountMetricProducer(const CountMetric& metric, const int conditionIndex,
+CountMetricProducer::CountMetricProducer(const ConfigKey& key, const CountMetric& metric,
+                                         const int conditionIndex,
                                          const sp<ConditionWizard>& wizard,
                                          const uint64_t startTimeNs)
-    : MetricProducer(startTimeNs, conditionIndex, wizard), mMetric(metric) {
+    : MetricProducer(key, startTimeNs, conditionIndex, wizard), mMetric(metric) {
     // TODO: evaluate initial conditions. and set mConditionMet.
     if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
         mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
@@ -180,6 +182,26 @@
     mCondition = conditionMet;
 }
 
+bool CountMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
+    if (mCurrentSlicedCounter->find(newKey) != mCurrentSlicedCounter->end()) {
+        return false;
+    }
+    // ===========GuardRail==============
+    // 1. Report the tuple count if the tuple count > soft limit
+    if (mCurrentSlicedCounter->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+        size_t newTupleCount = mCurrentSlicedCounter->size() + 1;
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
+                                                           newTupleCount);
+        // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+        if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+            ALOGE("CountMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
+                  newKey.c_str());
+            return true;
+        }
+    }
+
+    return false;
+}
 void CountMetricProducer::onMatchedLogEventInternal(
         const size_t matcherIndex, const HashableDimensionKey& eventKey,
         const map<string, HashableDimensionKey>& conditionKey, bool condition,
@@ -195,6 +217,11 @@
     auto it = mCurrentSlicedCounter->find(eventKey);
 
     if (it == mCurrentSlicedCounter->end()) {
+        // ===========GuardRail==============
+        if (hitGuardRail(eventKey)) {
+            return;
+        }
+
         // create a counter for the new key
         (*mCurrentSlicedCounter)[eventKey] = 1;
     } else {
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index 293e5b9..f78a199 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -42,8 +42,9 @@
 class CountMetricProducer : public MetricProducer {
 public:
     // TODO: Pass in the start time from MetricsManager, it should be consistent for all metrics.
-    CountMetricProducer(const CountMetric& countMetric, const int conditionIndex,
-                        const sp<ConditionWizard>& wizard, const uint64_t startTimeNs);
+    CountMetricProducer(const ConfigKey& key, const CountMetric& countMetric,
+                        const int conditionIndex, const sp<ConditionWizard>& wizard,
+                        const uint64_t startTimeNs);
 
     virtual ~CountMetricProducer();
 
@@ -84,6 +85,8 @@
 
     static const size_t kBucketSize = sizeof(CountBucket{});
 
+    bool hitGuardRail(const HashableDimensionKey& newKey);
+
     FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents);
     FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition);
     FRIEND_TEST(CountMetricProducerTest, TestEventsWithSlicedCondition);
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index c8e6cce..a0374c0 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -18,6 +18,7 @@
 
 #include "Log.h"
 #include "DurationMetricProducer.h"
+#include "guardrail/StatsdStats.h"
 #include "stats_util.h"
 
 #include <limits.h>
@@ -60,14 +61,14 @@
 const int FIELD_ID_END_BUCKET_NANOS = 2;
 const int FIELD_ID_DURATION = 3;
 
-DurationMetricProducer::DurationMetricProducer(const DurationMetric& metric,
+DurationMetricProducer::DurationMetricProducer(const ConfigKey& key, const DurationMetric& metric,
                                                const int conditionIndex, const size_t startIndex,
                                                const size_t stopIndex, const size_t stopAllIndex,
                                                const bool nesting,
                                                const sp<ConditionWizard>& wizard,
                                                const vector<KeyMatcher>& internalDimension,
                                                const uint64_t startTimeNs)
-    : MetricProducer(startTimeNs, conditionIndex, wizard),
+    : MetricProducer(key, startTimeNs, conditionIndex, wizard),
       mMetric(metric),
       mStartIndex(startIndex),
       mStopIndex(stopIndex),
@@ -113,13 +114,13 @@
         const HashableDimensionKey& eventKey, vector<DurationBucket>& bucket) {
     switch (mMetric.aggregation_type()) {
         case DurationMetric_AggregationType_SUM:
-            return make_unique<OringDurationTracker>(eventKey, mWizard, mConditionTrackerIndex,
-                                                     mNested, mCurrentBucketStartTimeNs,
-                                                     mBucketSizeNs, mAnomalyTrackers, bucket);
+            return make_unique<OringDurationTracker>(
+                    mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested,
+                    mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers, bucket);
         case DurationMetric_AggregationType_MAX_SPARSE:
-            return make_unique<MaxDurationTracker>(eventKey, mWizard, mConditionTrackerIndex,
-                                                   mNested, mCurrentBucketStartTimeNs,
-                                                   mBucketSizeNs, mAnomalyTrackers, bucket);
+            return make_unique<MaxDurationTracker>(
+                    mConfigKey, mMetric.name(), eventKey, mWizard, mConditionTrackerIndex, mNested,
+                    mCurrentBucketStartTimeNs, mBucketSizeNs, mAnomalyTrackers, bucket);
     }
 }
 
@@ -238,6 +239,26 @@
     mCurrentBucketNum += numBucketsForward;
 }
 
+bool DurationMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
+    // the key is not new, we are good.
+    if (mCurrentSlicedDuration.find(newKey) != mCurrentSlicedDuration.end()) {
+        return false;
+    }
+    // 1. Report the tuple count if the tuple count > soft limit
+    if (mCurrentSlicedDuration.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+        size_t newTupleCount = mCurrentSlicedDuration.size() + 1;
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
+                                                           newTupleCount);
+        // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+        if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+            ALOGE("DurationMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
+                  newKey.c_str());
+            return true;
+        }
+    }
+    return false;
+}
+
 void DurationMetricProducer::onMatchedLogEventInternal(
         const size_t matcherIndex, const HashableDimensionKey& eventKey,
         const map<string, HashableDimensionKey>& conditionKeys, bool condition,
@@ -254,6 +275,9 @@
     HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension));
 
     if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
+        if (hitGuardRail(eventKey)) {
+            return;
+        }
         mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey, mPastBuckets[eventKey]);
     }
 
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index ebd5e8d..5b5373e 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -37,9 +37,9 @@
 
 class DurationMetricProducer : public MetricProducer {
 public:
-    DurationMetricProducer(const DurationMetric& durationMetric, const int conditionIndex,
-                           const size_t startIndex, const size_t stopIndex,
-                           const size_t stopAllIndex, const bool nesting,
+    DurationMetricProducer(const ConfigKey& key, const DurationMetric& durationMetric,
+                           const int conditionIndex, const size_t startIndex,
+                           const size_t stopIndex, const size_t stopAllIndex, const bool nesting,
                            const sp<ConditionWizard>& wizard,
                            const vector<KeyMatcher>& internalDimension, const uint64_t startTimeNs);
 
@@ -98,6 +98,7 @@
 
     std::unique_ptr<DurationTracker> createDurationTracker(const HashableDimensionKey& eventKey,
                                                            std::vector<DurationBucket>& bucket);
+    bool hitGuardRail(const HashableDimensionKey& newKey);
 
     static const size_t kBucketSize = sizeof(DurationBucket{});
 };
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 567b4c7..95a18f7 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -51,10 +51,11 @@
 const int FIELD_ID_TIMESTAMP_NANOS = 1;
 const int FIELD_ID_ATOMS = 2;
 
-EventMetricProducer::EventMetricProducer(const EventMetric& metric, const int conditionIndex,
+EventMetricProducer::EventMetricProducer(const ConfigKey& key, const EventMetric& metric,
+                                         const int conditionIndex,
                                          const sp<ConditionWizard>& wizard,
                                          const uint64_t startTimeNs)
-    : MetricProducer(startTimeNs, conditionIndex, wizard), mMetric(metric) {
+    : MetricProducer(key, startTimeNs, conditionIndex, wizard), mMetric(metric) {
     if (metric.links().size() > 0) {
         mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
                                metric.links().end());
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 5afcebd..33a9510 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -34,8 +34,9 @@
 class EventMetricProducer : public MetricProducer {
 public:
     // TODO: Pass in the start time from MetricsManager, it should be consistent for all metrics.
-    EventMetricProducer(const EventMetric& eventMetric, const int conditionIndex,
-                        const sp<ConditionWizard>& wizard, const uint64_t startTimeNs);
+    EventMetricProducer(const ConfigKey& key, const EventMetric& eventMetric,
+                        const int conditionIndex, const sp<ConditionWizard>& wizard,
+                        const uint64_t startTimeNs);
 
     virtual ~EventMetricProducer();
 
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index be030d8..1791654 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -18,6 +18,7 @@
 #include "Log.h"
 
 #include "GaugeMetricProducer.h"
+#include "guardrail/StatsdStats.h"
 #include "stats_util.h"
 
 #include <cutils/log.h>
@@ -62,10 +63,11 @@
 const int FIELD_ID_END_BUCKET_NANOS = 2;
 const int FIELD_ID_GAUGE = 3;
 
-GaugeMetricProducer::GaugeMetricProducer(const GaugeMetric& metric, const int conditionIndex,
+GaugeMetricProducer::GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& metric,
+                                         const int conditionIndex,
                                          const sp<ConditionWizard>& wizard, const int pullTagId,
                                          const int64_t startTimeNs)
-    : MetricProducer(startTimeNs, conditionIndex, wizard),
+    : MetricProducer(key, startTimeNs, conditionIndex, wizard),
       mMetric(metric),
       mPullTagId(pullTagId) {
     if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
@@ -225,6 +227,26 @@
     }
 }
 
+bool GaugeMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
+    if (mCurrentSlicedBucket->find(newKey) != mCurrentSlicedBucket->end()) {
+        return false;
+    }
+    // 1. Report the tuple count if the tuple count > soft limit
+    if (mCurrentSlicedBucket->size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+        size_t newTupleCount = mCurrentSlicedBucket->size() + 1;
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
+                                                           newTupleCount);
+        // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+        if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+            ALOGE("GaugeMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
+                  newKey.c_str());
+            return true;
+        }
+    }
+
+    return false;
+}
+
 void GaugeMetricProducer::onMatchedLogEventInternal(
         const size_t matcherIndex, const HashableDimensionKey& eventKey,
         const map<string, HashableDimensionKey>& conditionKey, bool condition,
@@ -244,12 +266,15 @@
         flushIfNeeded(eventTimeNs);
     }
 
-    // For gauge metric, we just simply use the first guage in the given bucket.
+    // For gauge metric, we just simply use the first gauge in the given bucket.
     if (!mCurrentSlicedBucket->empty()) {
         return;
     }
     const long gauge = getGauge(event);
     if (gauge >= 0) {
+        if (hitGuardRail(eventKey)) {
+            return;
+        }
         (*mCurrentSlicedBucket)[eventKey] = gauge;
     }
     for (auto& tracker : mAnomalyTrackers) {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 4b7654b4..f344303 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -47,9 +47,9 @@
 public:
     // TODO: Pass in the start time from MetricsManager, it should be consistent
     // for all metrics.
-    GaugeMetricProducer(const GaugeMetric& countMetric, const int conditionIndex,
-                        const sp<ConditionWizard>& wizard, const int pullTagId,
-                        const int64_t startTimeNs);
+    GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& countMetric,
+                        const int conditionIndex, const sp<ConditionWizard>& wizard,
+                        const int pullTagId, const int64_t startTimeNs);
 
     virtual ~GaugeMetricProducer();
 
@@ -100,6 +100,8 @@
 
     int64_t getGauge(const LogEvent& event);
 
+    bool hitGuardRail(const HashableDimensionKey& newKey);
+
     static const size_t kBucketSize = sizeof(GaugeBucket{});
 
     FRIEND_TEST(GaugeMetricProducerTest, TestWithCondition);
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index ddccf9a..b22ff6f 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -19,6 +19,7 @@
 
 #include "anomaly/AnomalyTracker.h"
 #include "condition/ConditionWizard.h"
+#include "config/ConfigKey.h"
 #include "matchers/matcher_util.h"
 #include "packages/PackageInfoListener.h"
 
@@ -35,9 +36,10 @@
 // be a no-op.
 class MetricProducer : public virtual PackageInfoListener {
 public:
-    MetricProducer(const int64_t startTimeNs, const int conditionIndex,
+    MetricProducer(const ConfigKey& key, const int64_t startTimeNs, const int conditionIndex,
                    const sp<ConditionWizard>& wizard)
-        : mStartTimeNs(startTimeNs),
+        : mConfigKey(key),
+          mStartTimeNs(startTimeNs),
           mCurrentBucketStartTimeNs(startTimeNs),
           mCurrentBucketNum(0),
           mCondition(conditionIndex >= 0 ? false : true),
@@ -83,6 +85,8 @@
     }
 
 protected:
+    const ConfigKey mConfigKey;
+
     const uint64_t mStartTimeNs;
 
     uint64_t mCurrentBucketStartTimeNs;
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 5916b040..c866951 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -20,6 +20,7 @@
 #include "CountMetricProducer.h"
 #include "condition/CombinationConditionTracker.h"
 #include "condition/SimpleConditionTracker.h"
+#include "guardrail/StatsdStats.h"
 #include "matchers/CombinationLogMatchingTracker.h"
 #include "matchers/SimpleLogMatchingTracker.h"
 #include "metrics_manager_util.h"
@@ -36,10 +37,24 @@
 namespace os {
 namespace statsd {
 
-MetricsManager::MetricsManager(const StatsdConfig& config) {
-    mConfigValid = initStatsdConfig(config, mTagIds, mAllLogEntryMatchers, mAllConditionTrackers,
-                                    mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap,
-                                    mTrackerToMetricMap, mTrackerToConditionMap);
+MetricsManager::MetricsManager(const ConfigKey& key, const StatsdConfig& config) : mConfigKey(key) {
+    mConfigValid =
+            initStatsdConfig(key, config, mTagIds, mAllLogEntryMatchers, mAllConditionTrackers,
+                             mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap,
+                             mTrackerToMetricMap, mTrackerToConditionMap);
+
+    // TODO: add alert size.
+    // no matter whether this config is valid, log it in the stats.
+    StatsdStats::getInstance().noteConfigReceived(key, mAllMetricProducers.size(),
+                                                  mAllConditionTrackers.size(),
+                                                  mAllLogEntryMatchers.size(), 0, mConfigValid);
+    // Guardrail. Reject the config if it's too big.
+    if (mAllMetricProducers.size() > StatsdStats::kMaxMetricCountPerConfig ||
+        mAllConditionTrackers.size() > StatsdStats::kMaxConditionCountPerConfig ||
+        mAllLogEntryMatchers.size() > StatsdStats::kMaxMatcherCountPerConfig) {
+        ALOGE("This config is too big! Reject!");
+        mConfigValid = false;
+    }
 }
 
 MetricsManager::~MetricsManager() {
@@ -137,6 +152,8 @@
     // For matched LogEntryMatchers, tell relevant metrics that a matched event has come.
     for (size_t i = 0; i < mAllLogEntryMatchers.size(); i++) {
         if (matcherCache[i] == MatchingState::kMatched) {
+            StatsdStats::getInstance().noteMatcherMatched(mConfigKey,
+                                                          mAllLogEntryMatchers[i]->getName());
             auto pair = mTrackerToMetricMap.find(i);
             if (pair != mTrackerToMetricMap.end()) {
                 auto& metricList = pair->second;
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index a6054e3..517831d 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -19,6 +19,7 @@
 #include "anomaly/AnomalyMonitor.h"
 #include "anomaly/AnomalyTracker.h"
 #include "condition/ConditionTracker.h"
+#include "config/ConfigKey.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "logd/LogEvent.h"
 #include "matchers/LogMatchingTracker.h"
@@ -33,7 +34,7 @@
 // A MetricsManager is responsible for managing metrics from one single config source.
 class MetricsManager {
 public:
-    MetricsManager(const StatsdConfig& config);
+    MetricsManager(const ConfigKey& configKey, const StatsdConfig& config);
 
     ~MetricsManager();
 
@@ -57,6 +58,8 @@
     size_t byteSize();
 
 private:
+    const ConfigKey mConfigKey;
+
     // All event tags that are interesting to my metrics.
     std::set<int> mTagIds;
 
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index cc02c69..66c8419 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -18,6 +18,7 @@
 #include "Log.h"
 
 #include "ValueMetricProducer.h"
+#include "guardrail/StatsdStats.h"
 
 #include <cutils/log.h>
 #include <limits.h>
@@ -67,11 +68,12 @@
 static const uint64_t kDefaultBucketSizeMillis = 60 * 60 * 1000L;
 
 // ValueMetric has a minimum bucket size of 10min so that we don't pull too frequently
-ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int conditionIndex,
+ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric& metric,
+                                         const int conditionIndex,
                                          const sp<ConditionWizard>& wizard, const int pullTagId,
                                          const uint64_t startTimeNs,
                                          shared_ptr<StatsPullerManager> statsPullerManager)
-    : MetricProducer(startTimeNs, conditionIndex, wizard),
+    : MetricProducer(key, startTimeNs, conditionIndex, wizard),
       mMetric(metric),
       mStatsPullerManager(statsPullerManager),
       mPullTagId(pullTagId) {
@@ -103,10 +105,11 @@
 }
 
 // for testing
-ValueMetricProducer::ValueMetricProducer(const ValueMetric& metric, const int conditionIndex,
+ValueMetricProducer::ValueMetricProducer(const ConfigKey& key, const ValueMetric& metric,
+                                         const int conditionIndex,
                                          const sp<ConditionWizard>& wizard, const int pullTagId,
                                          const uint64_t startTimeNs)
-    : ValueMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs,
+    : ValueMetricProducer(key, metric, conditionIndex, wizard, pullTagId, startTimeNs,
                           make_shared<StatsPullerManager>()) {
 }
 
@@ -238,6 +241,27 @@
     }
 }
 
+bool ValueMetricProducer::hitGuardRail(const HashableDimensionKey& newKey) {
+    // ===========GuardRail==============
+    // 1. Report the tuple count if the tuple count > soft limit
+    if (mCurrentSlicedBucket.find(newKey) != mCurrentSlicedBucket.end()) {
+        return false;
+    }
+    if (mCurrentSlicedBucket.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+        size_t newTupleCount = mCurrentSlicedBucket.size() + 1;
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetric.name(),
+                                                           newTupleCount);
+        // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+        if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+            ALOGE("ValueMetric %s dropping data for dimension key %s", mMetric.name().c_str(),
+                  newKey.c_str());
+            return true;
+        }
+    }
+
+    return false;
+}
+
 void ValueMetricProducer::onMatchedLogEventInternal(
         const size_t matcherIndex, const HashableDimensionKey& eventKey,
         const map<string, HashableDimensionKey>& conditionKey, bool condition,
@@ -249,6 +273,9 @@
         return;
     }
 
+    if (hitGuardRail(eventKey)) {
+        return;
+    }
     Interval& interval = mCurrentSlicedBucket[eventKey];
 
     long value = get_value(event);
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 24c76f2..a024bd8 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -38,9 +38,9 @@
 
 class ValueMetricProducer : public virtual MetricProducer, public virtual PullDataReceiver {
 public:
-    ValueMetricProducer(const ValueMetric& valueMetric, const int conditionIndex,
-                        const sp<ConditionWizard>& wizard, const int pullTagId,
-                        const uint64_t startTimeNs);
+    ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
+                        const int conditionIndex, const sp<ConditionWizard>& wizard,
+                        const int pullTagId, const uint64_t startTimeNs);
 
     virtual ~ValueMetricProducer();
 
@@ -77,9 +77,9 @@
     std::shared_ptr<StatsPullerManager> mStatsPullerManager;
 
     // for testing
-    ValueMetricProducer(const ValueMetric& valueMetric, const int conditionIndex,
-                        const sp<ConditionWizard>& wizard, const int pullTagId,
-                        const uint64_t startTimeNs,
+    ValueMetricProducer(const ConfigKey& key, const ValueMetric& valueMetric,
+                        const int conditionIndex, const sp<ConditionWizard>& wizard,
+                        const int pullTagId, const uint64_t startTimeNs,
                         std::shared_ptr<StatsPullerManager> statsPullerManager);
 
     Mutex mLock;
@@ -104,6 +104,8 @@
 
     long get_value(const LogEvent& event);
 
+    bool hitGuardRail(const HashableDimensionKey& newKey);
+
     static const size_t kBucketSize = sizeof(ValueBucket{});
 
     FRIEND_TEST(ValueMetricProducerTest, TestNonDimensionalEvents);
diff --git a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
index 7ce7f02..834f7f5 100644
--- a/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/DurationTracker.h
@@ -19,6 +19,7 @@
 
 #include "anomaly/AnomalyTracker.h"
 #include "condition/ConditionWizard.h"
+#include "config/ConfigKey.h"
 #include "stats_util.h"
 
 namespace android {
@@ -59,11 +60,14 @@
 
 class DurationTracker {
 public:
-    DurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
-                    int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
-                    uint64_t bucketSizeNs, const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
+    DurationTracker(const ConfigKey& key, const string& name, const HashableDimensionKey& eventKey,
+                    sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
+                    uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
+                    const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
                     std::vector<DurationBucket>& bucket)
-        : mEventKey(eventKey),
+        : mConfigKey(key),
+          mName(name),
+          mEventKey(eventKey),
           mWizard(wizard),
           mConditionTrackerIndex(conditionIndex),
           mBucketSizeNs(bucketSizeNs),
@@ -138,6 +142,10 @@
             }
         }
     }
+    // A reference to the DurationMetricProducer's config key.
+    const ConfigKey& mConfigKey;
+
+    const std::string mName;
 
     HashableDimensionKey mEventKey;
 
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index e4b3693..4b346dd 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -18,23 +18,50 @@
 
 #include "Log.h"
 #include "MaxDurationTracker.h"
+#include "guardrail/StatsdStats.h"
 
 namespace android {
 namespace os {
 namespace statsd {
 
-MaxDurationTracker::MaxDurationTracker(const HashableDimensionKey& eventKey,
+MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const string& name,
+                                       const HashableDimensionKey& eventKey,
                                        sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
                                        uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
                                        const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
                                        std::vector<DurationBucket>& bucket)
-    : DurationTracker(eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs,
-                      anomalyTrackers, bucket) {
+    : DurationTracker(key, name, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
+                      bucketSizeNs, anomalyTrackers, bucket) {
+}
+
+bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
+    // ===========GuardRail==============
+    if (mInfos.find(newKey) != mInfos.end()) {
+        // if the key existed, we are good!
+        return false;
+    }
+    // 1. Report the tuple count if the tuple count > soft limit
+    if (mInfos.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+        size_t newTupleCount = mInfos.size() + 1;
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName + mEventKey,
+                                                           newTupleCount);
+        // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+        if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+            ALOGE("MaxDurTracker %s dropping data for dimension key %s", mName.c_str(),
+                  newKey.c_str());
+            return true;
+        }
+    }
+    return false;
 }
 
 void MaxDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
                                    const uint64_t eventTime, const ConditionKey& conditionKey) {
     // this will construct a new DurationInfo if this key didn't exist.
+    if (hitGuardRail(key)) {
+        return;
+    }
+
     DurationInfo& duration = mInfos[key];
     duration.conditionKeys = conditionKey;
     VLOG("MaxDuration: key %s start condition %d", key.c_str(), condition);
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
index 0f79ffe..e0d1466 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.h
@@ -28,7 +28,8 @@
 // they stop or bucket expires.
 class MaxDurationTracker : public DurationTracker {
 public:
-    MaxDurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
+    MaxDurationTracker(const ConfigKey& key, const string& name,
+                       const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
                        int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
                        uint64_t bucketSizeNs,
                        const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
@@ -53,6 +54,9 @@
     void noteConditionChanged(const HashableDimensionKey& key, bool conditionMet,
                               const uint64_t timestamp);
 
+    // return true if we should not allow newKey to be tracked because we are above the threshold
+    bool hitGuardRail(const HashableDimensionKey& newKey);
+
     FRIEND_TEST(MaxDurationTrackerTest, TestSimpleMaxDuration);
     FRIEND_TEST(MaxDurationTrackerTest, TestCrossBucketBoundary);
     FRIEND_TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition);
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index 76f8514..22c33d6 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -16,6 +16,7 @@
 #define DEBUG true
 #include "Log.h"
 #include "OringDurationTracker.h"
+#include "guardrail/StatsdStats.h"
 
 namespace android {
 namespace os {
@@ -23,21 +24,45 @@
 
 using std::pair;
 
-OringDurationTracker::OringDurationTracker(const HashableDimensionKey& eventKey,
+OringDurationTracker::OringDurationTracker(const ConfigKey& key, const string& name,
+                                           const HashableDimensionKey& eventKey,
                                            sp<ConditionWizard> wizard, int conditionIndex,
                                            bool nesting, uint64_t currentBucketStartNs,
                                            uint64_t bucketSizeNs,
                                            const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
                                            std::vector<DurationBucket>& bucket)
-    : DurationTracker(eventKey, wizard, conditionIndex, nesting, currentBucketStartNs, bucketSizeNs,
-                      anomalyTrackers, bucket),
+    : DurationTracker(key, name, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
+                      bucketSizeNs, anomalyTrackers, bucket),
       mStarted(),
       mPaused() {
     mLastStartTime = 0;
 }
 
+bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
+    // ===========GuardRail==============
+    // 1. Report the tuple count if the tuple count > soft limit
+    if (mConditionKeyMap.find(newKey) != mConditionKeyMap.end()) {
+        return false;
+    }
+    if (mConditionKeyMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+        size_t newTupleCount = mConditionKeyMap.size() + 1;
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName + mEventKey,
+                                                           newTupleCount);
+        // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+        if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+            ALOGE("OringDurTracker %s dropping data for dimension key %s", mName.c_str(),
+                  newKey.c_str());
+            return true;
+        }
+    }
+    return false;
+}
+
 void OringDurationTracker::noteStart(const HashableDimensionKey& key, bool condition,
                                      const uint64_t eventTime, const ConditionKey& conditionKey) {
+    if (hitGuardRail(key)) {
+        return;
+    }
     if (condition) {
         if (mStarted.size() == 0) {
             mLastStartTime = eventTime;
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
index ef32fdb..a8404a9 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.h
@@ -27,7 +27,8 @@
 // Tracks the "Or'd" duration -- if 2 durations are overlapping, they won't be double counted.
 class OringDurationTracker : public DurationTracker {
 public:
-    OringDurationTracker(const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
+    OringDurationTracker(const ConfigKey& key, const string& name,
+                         const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
                          int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
                          uint64_t bucketSizeNs,
                          const std::vector<sp<AnomalyTracker>>& anomalyTrackers,
@@ -58,6 +59,9 @@
     int64_t mLastStartTime;
     std::map<HashableDimensionKey, ConditionKey> mConditionKeyMap;
 
+    // return true if we should not allow newKey to be tracked because we are above the threshold
+    bool hitGuardRail(const HashableDimensionKey& newKey);
+
     FRIEND_TEST(OringDurationTrackerTest, TestDurationOverlap);
     FRIEND_TEST(OringDurationTrackerTest, TestCrossBucketBoundary);
     FRIEND_TEST(OringDurationTrackerTest, TestDurationConditionChange);
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 4660263..1b9efc5 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -134,7 +134,8 @@
     return true;
 }
 
-bool initConditions(const StatsdConfig& config, const unordered_map<string, int>& logTrackerMap,
+bool initConditions(const ConfigKey& key, const StatsdConfig& config,
+                    const unordered_map<string, int>& logTrackerMap,
                     unordered_map<string, int>& conditionTrackerMap,
                     vector<sp<ConditionTracker>>& allConditionTrackers,
                     unordered_map<int, std::vector<int>>& trackerToConditionMap) {
@@ -149,7 +150,7 @@
         switch (condition.contents_case()) {
             case Condition::ContentsCase::kSimpleCondition: {
                 allConditionTrackers.push_back(new SimpleConditionTracker(
-                        condition.name(), index, condition.simple_condition(), logTrackerMap));
+                        key, condition.name(), index, condition.simple_condition(), logTrackerMap));
                 break;
             }
             case Condition::ContentsCase::kCombination: {
@@ -184,7 +185,8 @@
     return true;
 }
 
-bool initMetrics(const StatsdConfig& config, const unordered_map<string, int>& logTrackerMap,
+bool initMetrics(const ConfigKey& key, const StatsdConfig& config,
+                 const unordered_map<string, int>& logTrackerMap,
                  const unordered_map<string, int>& conditionTrackerMap,
                  const vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
                  vector<sp<ConditionTracker>>& allConditionTrackers,
@@ -230,7 +232,7 @@
         }
 
         sp<MetricProducer> countProducer =
-                new CountMetricProducer(metric, conditionIndex, wizard, startTimeNs);
+                new CountMetricProducer(key, metric, conditionIndex, wizard, startTimeNs);
         allMetricProducers.push_back(countProducer);
     }
 
@@ -298,8 +300,8 @@
         }
 
         sp<MetricProducer> durationMetric = new DurationMetricProducer(
-                metric, conditionIndex, trackerIndices[0], trackerIndices[1], trackerIndices[2],
-                nesting, wizard, internalDimension, startTimeNs);
+                key, metric, conditionIndex, trackerIndices[0], trackerIndices[1],
+                trackerIndices[2], nesting, wizard, internalDimension, startTimeNs);
 
         allMetricProducers.push_back(durationMetric);
     }
@@ -332,7 +334,7 @@
         }
 
         sp<MetricProducer> eventMetric =
-                new EventMetricProducer(metric, conditionIndex, wizard, startTimeNs);
+                new EventMetricProducer(key, metric, conditionIndex, wizard, startTimeNs);
 
         allMetricProducers.push_back(eventMetric);
     }
@@ -378,8 +380,8 @@
             }
         }
 
-        sp<MetricProducer> valueProducer =
-                new ValueMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs);
+        sp<MetricProducer> valueProducer = new ValueMetricProducer(key, metric, conditionIndex,
+                                                                   wizard, pullTagId, startTimeNs);
         allMetricProducers.push_back(valueProducer);
     }
 
@@ -424,8 +426,8 @@
             }
         }
 
-        sp<MetricProducer> gaugeProducer =
-                new GaugeMetricProducer(metric, conditionIndex, wizard, pullTagId, startTimeNs);
+        sp<MetricProducer> gaugeProducer = new GaugeMetricProducer(key, metric, conditionIndex,
+                                                                   wizard, pullTagId, startTimeNs);
         allMetricProducers.push_back(gaugeProducer);
     }
     return true;
@@ -451,7 +453,7 @@
     return true;
 }
 
-bool initStatsdConfig(const StatsdConfig& config, set<int>& allTagIds,
+bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, set<int>& allTagIds,
                       vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
                       vector<sp<ConditionTracker>>& allConditionTrackers,
                       vector<sp<MetricProducer>>& allMetricProducers,
@@ -469,13 +471,13 @@
     }
     ALOGD("initLogMatchingTrackers succeed...");
 
-    if (!initConditions(config, logTrackerMap, conditionTrackerMap, allConditionTrackers,
+    if (!initConditions(key, config, logTrackerMap, conditionTrackerMap, allConditionTrackers,
                         trackerToConditionMap)) {
         ALOGE("initConditionTrackers failed");
         return false;
     }
 
-    if (!initMetrics(config, logTrackerMap, conditionTrackerMap, allLogEntryMatchers,
+    if (!initMetrics(key, config, logTrackerMap, conditionTrackerMap, allLogEntryMatchers,
                      allConditionTrackers, allMetricProducers, conditionToMetricMap,
                      trackerToMetricMap, metricProducerMap)) {
         ALOGE("initMetricProducers failed");
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 7d7e0c3..e7cbd53 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -36,6 +36,7 @@
 
 // Initialize the LogMatchingTrackers.
 // input:
+// [key]: the config key that this config belongs to
 // [config]: the input StatsdConfig
 // output:
 // [logTrackerMap]: this map should contain matcher name to index mapping
@@ -48,6 +49,7 @@
 
 // Initialize ConditionTrackers
 // input:
+// [key]: the config key that this config belongs to
 // [config]: the input config
 // [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
 // output:
@@ -55,7 +57,7 @@
 // [allConditionTrackers]: stores the sp to all the ConditionTrackers
 // [trackerToConditionMap]: contain the mapping from index of
 //                        log tracker to condition trackers that use the log tracker
-bool initConditions(const StatsdConfig& config,
+bool initConditions(const ConfigKey& key, const StatsdConfig& config,
                     const std::unordered_map<std::string, int>& logTrackerMap,
                     std::unordered_map<std::string, int>& conditionTrackerMap,
                     std::vector<sp<ConditionTracker>>& allConditionTrackers,
@@ -64,6 +66,7 @@
 
 // Initialize MetricProducers.
 // input:
+// [key]: the config key that this config belongs to
 // [config]: the input config
 // [logTrackerMap]: LogMatchingTracker name to index mapping from previous step.
 // [conditionTrackerMap]: condition name to index mapping
@@ -73,7 +76,8 @@
 //                          the list of MetricProducer index
 // [trackerToMetricMap]: contains the mapping from log tracker to MetricProducer index.
 bool initMetrics(
-        const StatsdConfig& config, const std::unordered_map<std::string, int>& logTrackerMap,
+        const ConfigKey& key, const StatsdConfig& config,
+        const std::unordered_map<std::string, int>& logTrackerMap,
         const std::unordered_map<std::string, int>& conditionTrackerMap,
         const std::unordered_map<int, std::vector<EventConditionLink>>& eventConditionLinks,
         const vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
@@ -84,7 +88,7 @@
 
 // Initialize MetricsManager from StatsdConfig.
 // Parameters are the members of MetricsManager. See MetricsManager for declaration.
-bool initStatsdConfig(const StatsdConfig& config, std::set<int>& allTagIds,
+bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, std::set<int>& allTagIds,
                       std::vector<sp<LogMatchingTracker>>& allLogEntryMatchers,
                       std::vector<sp<ConditionTracker>>& allConditionTrackers,
                       std::vector<sp<MetricProducer>>& allMetricProducers,
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 4783cd89..81f8eb6 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -172,3 +172,51 @@
 
   repeated ConfigMetricsReport reports = 2;
 }
+
+message StatsdStatsReport {
+    optional int32 stats_begin_time_sec = 1;
+
+    optional int32 stats_end_time_sec = 2;
+
+    message MatcherStats {
+        optional string name = 1;
+        optional int32 matched_times = 2;
+    }
+
+    message ConditionStats {
+        optional string name = 1;
+        optional int32 max_tuple_counts = 2;
+    }
+
+    message MetricStats {
+        optional string name = 1;
+        optional int32 max_tuple_counts = 2;
+    }
+
+    message ConfigStats {
+        optional int32 uid = 1;
+        optional string name = 2;
+        optional int32 creation_time_sec = 3;
+        optional int32 deletion_time_sec = 4;
+        optional int32 metric_count = 5;
+        optional int32 condition_count = 6;
+        optional int32 matcher_count = 7;
+        optional int32 alert_count = 8;
+        optional bool is_valid = 9;
+
+        repeated int32 broadcast_sent_time_sec = 10;
+        repeated int32 data_drop_time_sec = 11;
+        repeated MatcherStats matcher_stats = 12;
+        repeated ConditionStats condition_stats = 13;
+        repeated MetricStats metric_stats = 14;
+    }
+
+    repeated ConfigStats config_stats = 3;
+
+    message AtomStats {
+        optional int32 tag = 1;
+        optional int32 count = 2;
+    }
+
+    repeated AtomStats atom_stats = 7;
+}
\ No newline at end of file
diff --git a/cmds/statsd/tests/ConfigManager_test.cpp b/cmds/statsd/tests/ConfigManager_test.cpp
index 618aef6..696fddf 100644
--- a/cmds/statsd/tests/ConfigManager_test.cpp
+++ b/cmds/statsd/tests/ConfigManager_test.cpp
@@ -62,7 +62,8 @@
 }
 
 TEST(ConfigManagerTest, TestFakeConfig) {
-    auto metricsManager = std::make_unique<MetricsManager>(build_fake_config());
+    auto metricsManager =
+            std::make_unique<MetricsManager>(ConfigKey(0, "test"), build_fake_config());
     EXPECT_TRUE(metricsManager->isConfigValid());
 }
 
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index 3dd4e70..2adec94 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -40,6 +40,8 @@
 
 // TODO: ADD MORE TEST CASES.
 
+const ConfigKey kConfigKey(0, "test");
+
 StatsdConfig buildGoodConfig() {
     StatsdConfig config;
     config.set_name("12345");
@@ -254,9 +256,9 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
-    EXPECT_TRUE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
-                                 allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
-                                 trackerToMetricMap, trackerToConditionMap));
+    EXPECT_TRUE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
+                                 allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+                                 conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
     EXPECT_EQ(1u, allMetricProducers.size());
     EXPECT_EQ(1u, allAnomalyTrackers.size());
 }
@@ -272,9 +274,9 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
-    EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
-                                  allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap));
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
+                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
 
 TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
@@ -288,9 +290,9 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
-    EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
-                                  allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap));
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
+                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
 
 TEST(MetricsManagerTest, TestMissingMatchers) {
@@ -303,9 +305,9 @@
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
-    EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
-                                  allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap));
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
+                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
 
 TEST(MetricsManagerTest, TestCircleConditionDependency) {
@@ -319,9 +321,9 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
-    EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
-                                  allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap));
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
+                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
 
 TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
@@ -335,9 +337,9 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
-    EXPECT_FALSE(initStatsdConfig(config, allTagIds, allLogEntryMatchers, allConditionTrackers,
-                                  allMetricProducers, allAnomalyTrackers, conditionToMetricMap,
-                                  trackerToMetricMap, trackerToConditionMap));
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, allTagIds, allLogEntryMatchers,
+                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
+                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
 
 #else
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 11fb011..92b4ffc 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -28,6 +28,8 @@
 namespace os {
 namespace statsd {
 
+const ConfigKey kConfigKey(0, "test");
+
 SimpleCondition getWakeLockHeldCondition(bool countNesting, bool defaultFalse,
                                          bool outputSlicedUid) {
     SimpleCondition simpleCondition;
@@ -76,8 +78,8 @@
     trackerNameIndexMap["SCREEN_TURNED_ON"] = 0;
     trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1;
 
-    SimpleConditionTracker conditionTracker("SCREEN_IS_ON", 0 /*tracker index*/, simpleCondition,
-                                            trackerNameIndexMap);
+    SimpleConditionTracker conditionTracker(kConfigKey, "SCREEN_IS_ON", 0 /*tracker index*/,
+                                            simpleCondition, trackerNameIndexMap);
 
     LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
 
@@ -158,8 +160,9 @@
     trackerNameIndexMap["SCREEN_TURNED_ON"] = 0;
     trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1;
 
-    SimpleConditionTracker conditionTracker("SCREEN_IS_ON", 0 /*condition tracker index*/,
-                                            simpleCondition, trackerNameIndexMap);
+    SimpleConditionTracker conditionTracker(kConfigKey, "SCREEN_IS_ON",
+                                            0 /*condition tracker index*/, simpleCondition,
+                                            trackerNameIndexMap);
 
     LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
 
@@ -227,8 +230,9 @@
     trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
     trackerNameIndexMap["RELEASE_ALL"] = 2;
 
-    SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
-                                            simpleCondition, trackerNameIndexMap);
+    SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
+                                            0 /*condition tracker index*/, simpleCondition,
+                                            trackerNameIndexMap);
     int uid = 111;
 
     LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
@@ -308,8 +312,9 @@
     trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
     trackerNameIndexMap["RELEASE_ALL"] = 2;
 
-    SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
-                                            simpleCondition, trackerNameIndexMap);
+    SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
+                                            0 /*condition tracker index*/, simpleCondition,
+                                            trackerNameIndexMap);
     int uid1 = 111;
     string uid1_wl1 = "wl1_1";
     int uid2 = 222;
@@ -392,8 +397,9 @@
     trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
     trackerNameIndexMap["RELEASE_ALL"] = 2;
 
-    SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
-                                            simpleCondition, trackerNameIndexMap);
+    SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
+                                            0 /*condition tracker index*/, simpleCondition,
+                                            trackerNameIndexMap);
     int uid1 = 111;
     int uid2 = 222;
 
diff --git a/cmds/statsd/tests/guardrail/StatsdStats_test.cpp b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
new file mode 100644
index 0000000..286f6bd
--- /dev/null
+++ b/cmds/statsd/tests/guardrail/StatsdStats_test.cpp
@@ -0,0 +1,50 @@
+// 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 "src/guardrail/StatsdStats.h"
+
+#include <gtest/gtest.h>
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+TEST(StatsdStatsTest, TestConfigAdd) {
+    // TODO: implement
+}
+
+TEST(StatsdStatsTest, TestConfigRemove) {
+    // TODO: implement
+}
+
+TEST(StatsdStatsTest, TestMatcherReport) {
+    // TODO: implement
+}
+
+TEST(StatsdStatsTest, TestConditionReport) {
+    // TODO: implement
+}
+
+TEST(StatsdStatsTest, TestAtomLog) {
+    // TODO: implement
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 35e08af..df74364 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -32,6 +32,8 @@
 namespace os {
 namespace statsd {
 
+const ConfigKey kConfigKey(0, "test");
+
 TEST(CountMetricProducerTest, TestNonDimensionalEvents) {
     int64_t bucketStartTimeNs = 10000000000;
     int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
@@ -48,7 +50,7 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    CountMetricProducer countProducer(metric, -1 /*-1 meaning no condition*/, wizard,
+    CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       bucketStartTimeNs);
 
     // 2 events in bucket 1.
@@ -106,7 +108,7 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    CountMetricProducer countProducer(metric, 1, wizard, bucketStartTimeNs);
+    CountMetricProducer countProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
 
     countProducer.onConditionChanged(true, bucketStartTimeNs);
     countProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
@@ -161,7 +163,7 @@
 
     EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue));
 
-    CountMetricProducer countProducer(metric, 1 /*condition tracker index*/, wizard,
+    CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard,
                                       bucketStartTimeNs);
 
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event1, false);
@@ -201,7 +203,7 @@
     metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    CountMetricProducer countProducer(metric, -1 /*-1 meaning no condition*/, wizard,
+    CountMetricProducer countProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       bucketStartTimeNs);
     countProducer.addAnomalyTracker(anomalyTracker);
 
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index 18d177c..724ad59 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -32,6 +32,8 @@
 namespace os {
 namespace statsd {
 
+const ConfigKey kConfigKey(0, "test");
+
 TEST(EventMetricProducerTest, TestNoCondition) {
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
@@ -45,7 +47,7 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    EventMetricProducer eventProducer(metric, -1 /*-1 meaning no condition*/, wizard,
+    EventMetricProducer eventProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       bucketStartTimeNs);
 
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
@@ -69,7 +71,7 @@
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    EventMetricProducer eventProducer(metric, 1, wizard, bucketStartTimeNs);
+    EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
 
     eventProducer.onConditionChanged(true /*condition*/, bucketStartTimeNs);
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
@@ -111,7 +113,7 @@
 
     EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue));
 
-    EventMetricProducer eventProducer(metric, 1, wizard, bucketStartTimeNs);
+    EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
 
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event1, false /*pulled*/);
     eventProducer.onMatchedLogEvent(1 /*matcher index*/, event2, false /*pulled*/);
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index d82ccfe..0763c94 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -36,6 +36,8 @@
 namespace os {
 namespace statsd {
 
+const ConfigKey kConfigKey(0, "test");
+
 TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
@@ -45,8 +47,8 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {},
-                               buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs,
+                               bucketSizeNs, {}, buckets);
 
     tracker.noteStart("1", true, bucketStartTimeNs, key1);
     // Event starts again. This would not change anything as it already starts.
@@ -72,8 +74,8 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {},
-                               buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs,
+                               bucketSizeNs, {}, buckets);
 
     tracker.noteStart("1", true, bucketStartTimeNs + 1, key1);
 
@@ -100,8 +102,8 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker("event", wizard, -1, false, bucketStartTimeNs, bucketSizeNs, {},
-                               buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, false, bucketStartTimeNs,
+                               bucketSizeNs, {}, buckets);
 
     // The event starts.
     tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
@@ -127,8 +129,8 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    MaxDurationTracker tracker("event", wizard, -1, true, bucketStartTimeNs, bucketSizeNs, {},
-                               buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, true, bucketStartTimeNs,
+                               bucketSizeNs, {}, buckets);
 
     // 2 starts
     tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
@@ -168,8 +170,8 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     int64_t durationTimeNs = 2 * 1000;
 
-    MaxDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {},
-                               buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs,
+                               bucketSizeNs, {}, buckets);
     EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
@@ -200,8 +202,8 @@
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
-    MaxDurationTracker tracker("event", wizard, -1, true, bucketStartTimeNs, bucketSizeNs,
-                               {anomalyTracker}, buckets);
+    MaxDurationTracker tracker(kConfigKey, "metric", "event", wizard, -1, true, bucketStartTimeNs,
+                               bucketSizeNs, {anomalyTracker}, buckets);
 
     tracker.noteStart("1", true, eventStartTimeNs, key1);
     tracker.noteStop("1", eventStartTimeNs + 10, false);
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 54618b5..63a0e2b 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -34,6 +34,8 @@
 namespace os {
 namespace statsd {
 
+const ConfigKey kConfigKey(0, "test");
+
 TEST(OringDurationTrackerTest, TestDurationOverlap) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
@@ -47,8 +49,8 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {},
-                                 buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs,
+                                 bucketSizeNs, {}, buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
@@ -73,8 +75,8 @@
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
-                                 buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
+                                 bucketSizeNs, {}, buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1);  // overlapping wl
@@ -99,8 +101,8 @@
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
-                                 buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
+                                 bucketSizeNs, {}, buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     tracker.noteStart("3:maps", true, eventStartTimeNs + 10, key1);  // overlapping wl
@@ -125,8 +127,8 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
-                                 buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
+                                 bucketSizeNs, {}, buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
@@ -162,8 +164,8 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {},
-                                 buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs,
+                                 bucketSizeNs, {}, buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
 
@@ -194,8 +196,8 @@
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker("event", wizard, 1, false, bucketStartTimeNs, bucketSizeNs, {},
-                                 buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, false, bucketStartTimeNs,
+                                 bucketSizeNs, {}, buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     // condition to false; record duration 5n
@@ -225,8 +227,8 @@
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs, {},
-                                 buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
+                                 bucketSizeNs, {}, buckets);
 
     tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
     tracker.noteStart("2:maps", true, eventStartTimeNs + 2, key1);
@@ -259,8 +261,8 @@
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
-    OringDurationTracker tracker("event", wizard, 1, true, bucketStartTimeNs, bucketSizeNs,
-                                 {anomalyTracker}, buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true, bucketStartTimeNs,
+                                 bucketSizeNs, {anomalyTracker}, buckets);
 
     // Nothing in the past bucket.
     tracker.noteStart("", true, eventStartTimeNs, key1);
@@ -319,8 +321,8 @@
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<AnomalyTracker> anomalyTracker = new AnomalyTracker(alert, bucketSizeNs);
-    OringDurationTracker tracker("event", wizard, 1, true /*nesting*/, bucketStartTimeNs,
-                                 bucketSizeNs, {anomalyTracker}, buckets);
+    OringDurationTracker tracker(kConfigKey, "metric", "event", wizard, 1, true /*nesting*/,
+                                 bucketStartTimeNs, bucketSizeNs, {anomalyTracker}, buckets);
 
     tracker.noteStart("", true, eventStartTimeNs, key1);
     tracker.noteStop("", eventStartTimeNs + 10, false);
diff --git a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
index 1ed3636..9d78466 100644
--- a/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/ValueMetricProducer_test.cpp
@@ -12,8 +12,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-#include "metrics_test_helper.h"
 #include "src/metrics/ValueMetricProducer.h"
+#include "metrics_test_helper.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -22,11 +22,11 @@
 
 using namespace testing;
 using android::sp;
+using std::make_shared;
 using std::set;
+using std::shared_ptr;
 using std::unordered_map;
 using std::vector;
-using std::shared_ptr;
-using std::make_shared;
 
 #ifdef __ANDROID__
 
@@ -34,6 +34,7 @@
 namespace os {
 namespace statsd {
 
+const ConfigKey kConfigKey(0, "test");
 /*
  * Tests pulled atoms with no conditions
  */
@@ -42,7 +43,7 @@
     int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
 
     int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
-    int64_t bucket3StartTimeNs = bucketStartTimeNs + 2*bucketSizeNs;
+    int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
 
     ValueMetric metric;
     metric.set_name("1");
@@ -54,12 +55,13 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     // TODO: pending refactor of StatsPullerManager
     // For now we still need this so that it doesn't do real pulling.
-    shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>();
+    shared_ptr<MockStatsPullerManager> pullerManager =
+            make_shared<StrictMock<MockStatsPullerManager>>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillOnce(Return());
 
-    ValueMetricProducer valueProducer(metric, -1 /*-1 meaning no condition*/, wizard,tagId,
-                                      bucketStartTimeNs, pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
+                                      tagId, bucketStartTimeNs, pullerManager);
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
@@ -144,41 +146,43 @@
     int tagId = 1;
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>();
+    shared_ptr<MockStatsPullerManager> pullerManager =
+            make_shared<StrictMock<MockStatsPullerManager>>();
     EXPECT_CALL(*pullerManager, RegisterReceiver(tagId, _, _)).WillOnce(Return());
     EXPECT_CALL(*pullerManager, UnRegisterReceiver(tagId, _)).WillRepeatedly(Return());
 
-    EXPECT_CALL(*pullerManager, Pull(tagId, _)).WillOnce(Invoke([] (int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-        int64_t bucketStartTimeNs = 10000000000;
-        int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+    EXPECT_CALL(*pullerManager, Pull(tagId, _))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                int64_t bucketStartTimeNs = 10000000000;
+                int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
 
-        int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
-        int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
-        data->clear();
-        shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-        event->write(1);
-        event->write(100);
-        event->init();
-        data->push_back(event);
-        return true;
-    }))
-    .WillOnce(Invoke([] (int tagId, vector<std::shared_ptr<LogEvent>>* data) {
-        int64_t bucketStartTimeNs = 10000000000;
-        int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
+                int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+                int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
+                event->write(1);
+                event->write(100);
+                event->init();
+                data->push_back(event);
+                return true;
+            }))
+            .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
+                int64_t bucketStartTimeNs = 10000000000;
+                int64_t bucketSizeNs = 60 * 1000 * 1000 * 1000LL;
 
-        int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
-        int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
-        data->clear();
-        shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
-        event->write(1);
-        event->write(120);
-        event->init();
-        data->push_back(event);
-        return true;
-    }));
+                int64_t bucket2StartTimeNs = bucketStartTimeNs + bucketSizeNs;
+                int64_t bucket3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs;
+                data->clear();
+                shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 10);
+                event->write(1);
+                event->write(120);
+                event->init();
+                data->push_back(event);
+                return true;
+            }));
 
-    ValueMetricProducer valueProducer(metric, 1, wizard,tagId,
-                                      bucketStartTimeNs, pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, 1, wizard, tagId, bucketStartTimeNs,
+                                      pullerManager);
 
     valueProducer.onConditionChanged(true, bucketStartTimeNs + 10);
 
@@ -241,10 +245,11 @@
     int tagId = 1;
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    shared_ptr<MockStatsPullerManager> pullerManager = make_shared<StrictMock<MockStatsPullerManager>>();
+    shared_ptr<MockStatsPullerManager> pullerManager =
+            make_shared<StrictMock<MockStatsPullerManager>>();
 
-    ValueMetricProducer valueProducer(metric, -1, wizard,-1,
-                                      bucketStartTimeNs, pullerManager);
+    ValueMetricProducer valueProducer(kConfigKey, metric, -1, wizard, -1, bucketStartTimeNs,
+                                      pullerManager);
 
     shared_ptr<LogEvent> event1 = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
     event1->write(1);