Remove the hacky HashableDimensionKey.

+ Add a real HashableDimensionKey as a wrapper of the dimension.
So we can get rid of the maps that we kept.

Pay down technical debt and reduce memory usage.

Test: statsd_test & manual
Change-Id: I233280cf1e2ce93da6a8cd4e8514abb066f4016d
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 3e517bb..f98ee3d 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -54,7 +54,7 @@
     src/storage/StorageManager.cpp \
     src/StatsLogProcessor.cpp \
     src/StatsService.cpp \
-    src/stats_util.cpp \
+    src/HashableDimensionKey.cpp \
     src/guardrail/MemoryLeakTrackUtil.cpp \
     src/guardrail/StatsdStats.cpp
 
@@ -174,7 +174,8 @@
     tests/metrics/EventMetricProducer_test.cpp \
     tests/metrics/ValueMetricProducer_test.cpp \
     tests/metrics/GaugeMetricProducer_test.cpp \
-    tests/guardrail/StatsdStats_test.cpp
+    tests/guardrail/StatsdStats_test.cpp \
+    tests/metrics/metrics_test_helper.cpp
 
 LOCAL_STATIC_LIBRARIES := \
     $(statsd_common_static_libraries) \
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
new file mode 100644
index 0000000..0b6f8f2
--- /dev/null
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -0,0 +1,110 @@
+/*
+ * 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 "HashableDimensionKey.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::string;
+
+string HashableDimensionKey::toString() const {
+    string flattened;
+    for (const auto& pair : mKeyValuePairs) {
+        flattened += std::to_string(pair.key());
+        flattened += ":";
+        switch (pair.value_case()) {
+            case KeyValuePair::ValueCase::kValueStr:
+                flattened += pair.value_str();
+                break;
+            case KeyValuePair::ValueCase::kValueInt:
+                flattened += std::to_string(pair.value_int());
+                break;
+            case KeyValuePair::ValueCase::kValueLong:
+                flattened += std::to_string(pair.value_long());
+                break;
+            case KeyValuePair::ValueCase::kValueBool:
+                flattened += std::to_string(pair.value_bool());
+                break;
+            case KeyValuePair::ValueCase::kValueFloat:
+                flattened += std::to_string(pair.value_float());
+                break;
+            default:
+                break;
+        }
+        flattened += "|";
+    }
+    return flattened;
+}
+
+bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const {
+    const auto& keyValue2 = that.getKeyValuePairs();
+    if (mKeyValuePairs.size() != keyValue2.size()) {
+        return false;
+    }
+
+    for (size_t i = 0; i < keyValue2.size(); i++) {
+        const auto& kv1 = mKeyValuePairs[i];
+        const auto& kv2 = keyValue2[i];
+        if (kv1.key() != kv2.key()) {
+            return false;
+        }
+
+        if (kv1.value_case() != kv2.value_case()) {
+            return false;
+        }
+
+        switch (kv1.value_case()) {
+            case KeyValuePair::ValueCase::kValueStr:
+                if (kv1.value_str() != kv2.value_str()) {
+                    return false;
+                }
+                break;
+            case KeyValuePair::ValueCase::kValueInt:
+                if (kv1.value_int() != kv2.value_int()) {
+                    return false;
+                }
+                break;
+            case KeyValuePair::ValueCase::kValueLong:
+                if (kv1.value_long() != kv2.value_long()) {
+                    return false;
+                }
+                break;
+            case KeyValuePair::ValueCase::kValueBool:
+                if (kv1.value_bool() != kv2.value_bool()) {
+                    return false;
+                }
+                break;
+            case KeyValuePair::ValueCase::kValueFloat: {
+                if (kv1.value_float() != kv2.value_float()) {
+                    return false;
+                }
+                break;
+            }
+            case KeyValuePair::ValueCase::VALUE_NOT_SET:
+                break;
+        }
+    }
+    return true;
+};
+
+bool HashableDimensionKey::operator<(const HashableDimensionKey& that) const {
+    return toString().compare(that.toString()) < 0;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
new file mode 100644
index 0000000..85215552
--- /dev/null
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <utils/JenkinsHash.h>
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class HashableDimensionKey {
+public:
+    explicit HashableDimensionKey(const std::vector<KeyValuePair>& keyValuePairs)
+        : mKeyValuePairs(keyValuePairs){};
+
+    HashableDimensionKey(){};
+
+    HashableDimensionKey(const HashableDimensionKey& that)
+        : mKeyValuePairs(that.getKeyValuePairs()){};
+
+    HashableDimensionKey& operator=(const HashableDimensionKey& from) = default;
+
+    std::string toString() const;
+
+    inline const std::vector<KeyValuePair>& getKeyValuePairs() const {
+        return mKeyValuePairs;
+    }
+
+    bool operator==(const HashableDimensionKey& that) const;
+
+    bool operator<(const HashableDimensionKey& that) const;
+
+    inline const char* c_str() const {
+        return toString().c_str();
+    }
+
+private:
+    std::vector<KeyValuePair> mKeyValuePairs;
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+
+namespace std {
+
+using android::os::statsd::HashableDimensionKey;
+using android::os::statsd::KeyValuePair;
+
+template <>
+struct hash<HashableDimensionKey> {
+    std::size_t operator()(const HashableDimensionKey& key) const {
+        android::hash_t hash = 0;
+        for (const auto& pair : key.getKeyValuePairs()) {
+            hash = android::JenkinsHashMix(hash, android::hash_type(pair.key()));
+            hash = android::JenkinsHashMix(
+                    hash, android::hash_type(static_cast<int32_t>(pair.value_case())));
+            switch (pair.value_case()) {
+                case KeyValuePair::ValueCase::kValueStr:
+                    hash = android::JenkinsHashMix(
+                            hash,
+                            static_cast<uint32_t>(std::hash<std::string>()(pair.value_str())));
+                    break;
+                case KeyValuePair::ValueCase::kValueInt:
+                    hash = android::JenkinsHashMix(hash, android::hash_type(pair.value_int()));
+                    break;
+                case KeyValuePair::ValueCase::kValueLong:
+                    hash = android::JenkinsHashMix(
+                            hash, android::hash_type(static_cast<int64_t>(pair.value_long())));
+                    break;
+                case KeyValuePair::ValueCase::kValueBool:
+                    hash = android::JenkinsHashMix(hash, android::hash_type(pair.value_bool()));
+                    break;
+                case KeyValuePair::ValueCase::kValueFloat: {
+                    float floatVal = pair.value_float();
+                    hash = android::JenkinsHashMixBytes(hash, (uint8_t*)&floatVal, sizeof(float));
+                    break;
+                }
+                case KeyValuePair::ValueCase::VALUE_NOT_SET:
+                    break;
+            }
+        }
+        return hash;
+    }
+};
+
+}  // namespace std
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index 18b93ee..a63bc04 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -279,7 +279,7 @@
     }
 
     // outputKey is the output key values. e.g, uid:1234
-    const HashableDimensionKey outputKey = getHashableKey(getDimensionKey(event, mOutputDimension));
+    const HashableDimensionKey outputKey(getDimensionKey(event, mOutputDimension));
     handleConditionEvent(outputKey, matchedState == 1, conditionCache, conditionChangedCache);
 }
 
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
index ff0e3bc..53ef9d5 100644
--- a/cmds/statsd/src/condition/condition_util.cpp
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -109,7 +109,7 @@
         kv.set_key(link.key_in_condition(i).key());
     }
 
-    return getHashableKey(dimensionKey);
+    return HashableDimensionKey(dimensionKey);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index bc12a78..9031ed0 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -107,17 +107,14 @@
 
     for (const auto& counter : mPastBuckets) {
         const HashableDimensionKey& hashableKey = counter.first;
+        const vector<KeyValuePair>& kvs = hashableKey.getKeyValuePairs();
         VLOG("  dimension key %s", hashableKey.c_str());
-        auto it = mDimensionKeyMap.find(hashableKey);
-        if (it == mDimensionKeyMap.end()) {
-            ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
-            continue;
-        }
+
         long long wrapperToken =
                 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
 
         // First fill dimension (KeyValuePairs).
-        for (const auto& kv : it->second) {
+        for (const auto& kv : kvs) {
             long long dimensionToken = protoOutput->start(
                     FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
             protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 220861d..1c8f422 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -159,18 +159,14 @@
 
     for (const auto& pair : mPastBuckets) {
         const HashableDimensionKey& hashableKey = pair.first;
+        const vector<KeyValuePair>& kvs = hashableKey.getKeyValuePairs();
         VLOG("  dimension key %s", hashableKey.c_str());
-        auto it = mDimensionKeyMap.find(hashableKey);
-        if (it == mDimensionKeyMap.end()) {
-            ALOGW("Dimension key %s not found?!?! skip...", hashableKey.c_str());
-            continue;
-        }
 
         long long wrapperToken =
                 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
 
         // First fill dimension (KeyValuePairs).
-        for (const auto& kv : it->second) {
+        for (const auto& kv : kvs) {
             long long dimensionToken = protoOutput->start(
                     FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
             protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
@@ -260,7 +256,7 @@
         return;
     }
 
-    HashableDimensionKey atomKey = getHashableKey(getDimensionKey(event, mInternalDimension));
+    HashableDimensionKey atomKey(getDimensionKey(event, mInternalDimension));
 
     if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
         if (hitGuardRailLocked(eventKey)) {
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 6402633..47cca0e 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -128,18 +128,14 @@
 
     for (const auto& pair : mPastBuckets) {
         const HashableDimensionKey& hashableKey = pair.first;
-        auto it = mDimensionKeyMap.find(hashableKey);
-        if (it == mDimensionKeyMap.end()) {
-            ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
-            continue;
-        }
+        const vector<KeyValuePair>& kvs = hashableKey.getKeyValuePairs();
 
         VLOG("  dimension key %s", hashableKey.c_str());
         long long wrapperToken =
                 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
 
         // First fill dimension (KeyValuePairs).
-        for (const auto& kv : it->second) {
+        for (const auto& kv : kvs) {
             long long dimensionToken = protoOutput->start(
                     FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
             protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index f38f3df..5286908 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -32,12 +32,7 @@
 
     if (mDimension.size() > 0) {
         vector<KeyValuePair> key = getDimensionKey(event, mDimension);
-        eventKey = getHashableKey(key);
-        // Add the HashableDimensionKey->vector<KeyValuePair> to the map, because StatsLogReport
-        // expects vector<KeyValuePair>.
-        if (mDimensionKeyMap.find(eventKey) == mDimensionKeyMap.end()) {
-            mDimensionKeyMap[eventKey] = key;
-        }
+        eventKey = HashableDimensionKey(key);
     } else {
         eventKey = DEFAULT_DIMENSION_KEY;
     }
@@ -58,7 +53,6 @@
     } else {
         condition = mCondition;
     }
-
     onMatchedLogEventInternalLocked(matcherIndex, eventKey, conditionKeys, condition, event);
 }
 
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 7bd274d..85ef4ad 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -49,10 +49,7 @@
           mCondition(conditionIndex >= 0 ? false : true),
           mConditionSliced(false),
           mWizard(wizard),
-          mConditionTrackerIndex(conditionIndex) {
-        // reuse the same map for non-sliced metrics too. this way, we avoid too many if-else.
-        mDimensionKeyMap[DEFAULT_DIMENSION_KEY] = std::vector<KeyValuePair>();
-    };
+          mConditionTrackerIndex(conditionIndex){};
     virtual ~MetricProducer(){};
 
     void notifyAppUpgrade(const string& apk, const int uid, const int64_t version) override{
@@ -145,10 +142,6 @@
 
     std::vector<KeyMatcher> mDimension;  // The dimension defined in statsd_config
 
-    // Keep the map from the internal HashableDimensionKey to std::vector<KeyValuePair>
-    // that StatsLogReport wants.
-    std::unordered_map<HashableDimensionKey, std::vector<KeyValuePair>> mDimensionKeyMap;
-
     std::vector<MetricConditionLink> mConditionLinks;
 
     std::vector<sp<AnomalyTracker>> mAnomalyTrackers;
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 7efa6cd..40aed7b 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -132,16 +132,12 @@
     for (const auto& pair : mPastBuckets) {
         const HashableDimensionKey& hashableKey = pair.first;
         VLOG("  dimension key %s", hashableKey.c_str());
-        auto it = mDimensionKeyMap.find(hashableKey);
-        if (it == mDimensionKeyMap.end()) {
-            ALOGE("Dimension key %s not found?!?! skip...", hashableKey.c_str());
-            continue;
-        }
+        const vector<KeyValuePair>& kvs = hashableKey.getKeyValuePairs();
         long long wrapperToken =
                 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
 
         // First fill dimension (KeyValuePairs).
-        for (const auto& kv : it->second) {
+        for (const auto& kv : kvs) {
             long long dimensionToken = protoOutput->start(
                     FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
             protoOutput->write(FIELD_TYPE_INT32 | FIELD_ID_KEY, kv.key());
diff --git a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
index 95c8a59..6050f43 100644
--- a/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -42,7 +42,7 @@
     // 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,
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName + mEventKey.toString(),
                                                            newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
diff --git a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
index 36e25edf..5c43096 100644
--- a/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/cmds/statsd/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -45,7 +45,7 @@
     }
     if (mConditionKeyMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
         size_t newTupleCount = mConditionKeyMap.size() + 1;
-        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName + mEventKey,
+        StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mName + mEventKey.toString(),
                                                            newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
diff --git a/cmds/statsd/src/stats_util.cpp b/cmds/statsd/src/stats_util.cpp
deleted file mode 100644
index 7527a64..0000000
--- a/cmds/statsd/src/stats_util.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "stats_util.h"
-
-namespace android {
-namespace os {
-namespace statsd {
-
-// There is no existing hash function for the dimension key ("repeated KeyValuePair").
-// Temporarily use a string concatenation as the hashable key.
-// TODO: Find a better hash function for std::vector<KeyValuePair>.
-HashableDimensionKey getHashableKey(std::vector<KeyValuePair> keys) {
-    std::string flattened;
-    for (const KeyValuePair& pair : keys) {
-        flattened += std::to_string(pair.key());
-        flattened += ":";
-        switch (pair.value_case()) {
-            case KeyValuePair::ValueCase::kValueStr:
-                flattened += pair.value_str();
-                break;
-            case KeyValuePair::ValueCase::kValueInt:
-                flattened += std::to_string(pair.value_int());
-                break;
-            case KeyValuePair::ValueCase::kValueLong:
-                flattened += std::to_string(pair.value_long());
-                break;
-            case KeyValuePair::ValueCase::kValueBool:
-                flattened += std::to_string(pair.value_bool());
-                break;
-            case KeyValuePair::ValueCase::kValueFloat:
-                flattened += std::to_string(pair.value_float());
-                break;
-            default:
-                break;
-        }
-        flattened += "|";
-    }
-    return flattened;
-}
-
-}  // namespace statsd
-}  // namespace os
-}  // namespace android
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index 8fd1ea8c..1cdf031 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -16,8 +16,9 @@
 
 #pragma once
 
-#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include <sstream>
+#include "HashableDimensionKey.h"
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "logd/LogReader.h"
 
 #include <unordered_map>
@@ -26,13 +27,11 @@
 namespace os {
 namespace statsd {
 
-#define DEFAULT_DIMENSION_KEY ""
+const HashableDimensionKey DEFAULT_DIMENSION_KEY = HashableDimensionKey(vector<KeyValuePair>());
 
 // Minimum bucket size in seconds
 const long kMinBucketSizeSec = 5 * 60;
 
-typedef std::string HashableDimensionKey;
-
 typedef std::map<std::string, HashableDimensionKey> ConditionKey;
 
 typedef std::unordered_map<HashableDimensionKey, int64_t> DimToValMap;
@@ -67,13 +66,6 @@
 } EventKV;
 
 typedef std::unordered_map<HashableDimensionKey, std::shared_ptr<EventKV>> DimToEventKVMap;
-
-EventMetricData parse(log_msg msg);
-
-int getTagId(log_msg msg);
-
-std::string getHashableKey(std::vector<KeyValuePair> key);
-
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index f385763..f62171d 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "src/anomaly/AnomalyTracker.h"
+#include "../metrics/metrics_test_helper.h"
 
 #include <gtest/gtest.h>
 #include <stdio.h>
@@ -32,7 +33,18 @@
 
 const ConfigKey kConfigKey(0, "test");
 
-void AddValueToBucket(const std::vector<std::pair<string, long>>& key_value_pair_list,
+HashableDimensionKey getMockDimensionKey(int key, string value) {
+    KeyValuePair pair;
+    pair.set_key(key);
+    pair.set_value_str(value);
+
+    vector<KeyValuePair> pairs;
+    pairs.push_back(pair);
+
+    return HashableDimensionKey(pairs);
+}
+
+void AddValueToBucket(const std::vector<std::pair<HashableDimensionKey, long>>& key_value_pair_list,
                       std::shared_ptr<DimToValMap> bucket) {
     for (auto itr = key_value_pair_list.begin(); itr != key_value_pair_list.end(); itr++) {
         (*bucket)[itr->first] += itr->second;
@@ -40,7 +52,7 @@
 }
 
 std::shared_ptr<DimToValMap> MockBucket(
-        const std::vector<std::pair<string, long>>& key_value_pair_list) {
+        const std::vector<std::pair<HashableDimensionKey, long>>& key_value_pair_list) {
     std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>();
     AddValueToBucket(key_value_pair_list, bucket);
     return bucket;
@@ -54,20 +66,23 @@
     alert.set_trigger_if_sum_gt(2);
 
     AnomalyTracker anomalyTracker(alert, kConfigKey);
+    HashableDimensionKey keyA = getMockDimensionKey(1, "a");
+    HashableDimensionKey keyB = getMockDimensionKey(1, "b");
+    HashableDimensionKey keyC = getMockDimensionKey(1, "c");
 
-    std::shared_ptr<DimToValMap> bucket0 = MockBucket({{"a", 1}, {"b", 2}, {"c", 1}});
+    std::shared_ptr<DimToValMap> bucket0 = MockBucket({{keyA, 1}, {keyB, 2}, {keyC, 1}});
     int64_t eventTimestamp0 = 10;
-    std::shared_ptr<DimToValMap> bucket1 = MockBucket({{"a", 1}});
+    std::shared_ptr<DimToValMap> bucket1 = MockBucket({{keyA, 1}});
     int64_t eventTimestamp1 = bucketSizeNs + 11;
-    std::shared_ptr<DimToValMap> bucket2 = MockBucket({{"b", 1}});
+    std::shared_ptr<DimToValMap> bucket2 = MockBucket({{keyB, 1}});
     int64_t eventTimestamp2 = 2 * bucketSizeNs + 12;
-    std::shared_ptr<DimToValMap> bucket3 = MockBucket({{"a", 2}});
+    std::shared_ptr<DimToValMap> bucket3 = MockBucket({{keyA, 2}});
     int64_t eventTimestamp3 = 3 * bucketSizeNs + 13;
-    std::shared_ptr<DimToValMap> bucket4 = MockBucket({{"b", 1}});
+    std::shared_ptr<DimToValMap> bucket4 = MockBucket({{keyB, 1}});
     int64_t eventTimestamp4 = 4 * bucketSizeNs + 14;
-    std::shared_ptr<DimToValMap> bucket5 = MockBucket({{"a", 2}});
+    std::shared_ptr<DimToValMap> bucket5 = MockBucket({{keyA, 2}});
     int64_t eventTimestamp5 = 5 * bucketSizeNs + 15;
-    std::shared_ptr<DimToValMap> bucket6 = MockBucket({{"a", 2}});
+    std::shared_ptr<DimToValMap> bucket6 = MockBucket({{keyA, 2}});
     int64_t eventTimestamp6 = 6 * bucketSizeNs + 16;
 
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0u);
@@ -79,9 +94,9 @@
     // Adds past bucket #0
     anomalyTracker.addPastBucket(bucket0, 0);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 1LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL);
     EXPECT_FALSE(anomalyTracker.detectAnomaly(1, *bucket1));
     anomalyTracker.detectAndDeclareAnomaly(eventTimestamp1, 1, *bucket1);
@@ -90,9 +105,9 @@
     // Adds past bucket #0 again. The sum does not change.
     anomalyTracker.addPastBucket(bucket0, 0);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3u);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 1LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 0LL);
     EXPECT_FALSE(anomalyTracker.detectAnomaly(1, *bucket1));
     anomalyTracker.detectAndDeclareAnomaly(eventTimestamp1 + 1, 1, *bucket1);
@@ -102,9 +117,9 @@
     anomalyTracker.addPastBucket(bucket1, 1);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
     EXPECT_TRUE(anomalyTracker.detectAnomaly(2, *bucket2));
     anomalyTracker.detectAndDeclareAnomaly(eventTimestamp2, 2, *bucket2);
     EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
@@ -113,9 +128,9 @@
     anomalyTracker.addPastBucket(bucket1, 1);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 1L);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
     EXPECT_TRUE(anomalyTracker.detectAnomaly(2, *bucket2));
     anomalyTracker.detectAndDeclareAnomaly(eventTimestamp2 + 1, 2, *bucket2);
     EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
@@ -124,8 +139,8 @@
     anomalyTracker.addPastBucket(bucket2, 2);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 2L);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 1LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
     EXPECT_TRUE(anomalyTracker.detectAnomaly(3, *bucket3));
     anomalyTracker.detectAndDeclareAnomaly(eventTimestamp3, 3, *bucket3);
     // Within refractory period.
@@ -135,8 +150,8 @@
     anomalyTracker.addPastBucket(bucket3, 3L);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 3L);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
     EXPECT_FALSE(anomalyTracker.detectAnomaly(4, *bucket4));
     anomalyTracker.detectAndDeclareAnomaly(eventTimestamp4, 4, *bucket4);
     EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
@@ -145,8 +160,8 @@
     anomalyTracker.addPastBucket(bucket4, 4);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 4L);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
     EXPECT_TRUE(anomalyTracker.detectAnomaly(5, *bucket5));
     anomalyTracker.detectAndDeclareAnomaly(eventTimestamp5, 5, *bucket5);
     EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp5);
@@ -155,8 +170,8 @@
     anomalyTracker.addPastBucket(bucket5, 5);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 5L);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 2LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 2LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
     EXPECT_TRUE(anomalyTracker.detectAnomaly(6, *bucket6));
     // Within refractory period.
     anomalyTracker.detectAndDeclareAnomaly(eventTimestamp6, 6, *bucket6);
@@ -171,13 +186,18 @@
     alert.set_trigger_if_sum_gt(2);
 
     AnomalyTracker anomalyTracker(alert, kConfigKey);
+    HashableDimensionKey keyA = getMockDimensionKey(1, "a");
+    HashableDimensionKey keyB = getMockDimensionKey(1, "b");
+    HashableDimensionKey keyC = getMockDimensionKey(1, "c");
+    HashableDimensionKey keyD = getMockDimensionKey(1, "d");
+    HashableDimensionKey keyE = getMockDimensionKey(1, "e");
 
-    std::shared_ptr<DimToValMap> bucket9 = MockBucket({{"a", 1}, {"b", 2}, {"c", 1}});
-    std::shared_ptr<DimToValMap> bucket16 = MockBucket({{"b", 4}});
-    std::shared_ptr<DimToValMap> bucket18 = MockBucket({{"b", 1}, {"c", 1}});
-    std::shared_ptr<DimToValMap> bucket20 = MockBucket({{"b", 3}, {"c", 1}});
-    std::shared_ptr<DimToValMap> bucket25 = MockBucket({{"d", 1}});
-    std::shared_ptr<DimToValMap> bucket28 = MockBucket({{"e", 2}});
+    std::shared_ptr<DimToValMap> bucket9 = MockBucket({{keyA, 1}, {keyB, 2}, {keyC, 1}});
+    std::shared_ptr<DimToValMap> bucket16 = MockBucket({{keyB, 4}});
+    std::shared_ptr<DimToValMap> bucket18 = MockBucket({{keyB, 1}, {keyC, 1}});
+    std::shared_ptr<DimToValMap> bucket20 = MockBucket({{keyB, 3}, {keyC, 1}});
+    std::shared_ptr<DimToValMap> bucket25 = MockBucket({{keyD, 1}});
+    std::shared_ptr<DimToValMap> bucket28 = MockBucket({{keyE, 2}});
 
     int64_t eventTimestamp1 = bucketSizeNs * 8 + 1;
     int64_t eventTimestamp2 = bucketSizeNs * 15 + 11;
@@ -196,9 +216,9 @@
     anomalyTracker.addPastBucket(bucket9, 9);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 9L);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 3UL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("a"), 1LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 2LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyA), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 2LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
     EXPECT_TRUE(anomalyTracker.detectAnomaly(16, *bucket16));
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 15L);
@@ -211,27 +231,27 @@
     anomalyTracker.addPastBucket(bucket16, 16);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 16L);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 4LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL);
     EXPECT_TRUE(anomalyTracker.detectAnomaly(18, *bucket18));
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 4LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL);
     // Within refractory period.
     anomalyTracker.detectAndDeclareAnomaly(eventTimestamp3, 18, *bucket18);
     EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp2);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 4LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 4LL);
 
     // Add past bucket #18
     anomalyTracker.addPastBucket(bucket18, 18);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 18L);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
     EXPECT_TRUE(anomalyTracker.detectAnomaly(20, *bucket20));
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
     anomalyTracker.detectAndDeclareAnomaly(eventTimestamp4, 20, *bucket20);
     EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp4);
 
@@ -239,12 +259,12 @@
     anomalyTracker.addPastBucket(bucket18, 18);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 19L);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
     EXPECT_TRUE(anomalyTracker.detectAnomaly(20, *bucket20));
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 1LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
     anomalyTracker.detectAndDeclareAnomaly(eventTimestamp4 + 1, 20, *bucket20);
     // Within refractory period.
     EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp4);
@@ -253,8 +273,8 @@
     anomalyTracker.addPastBucket(bucket20, 20);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 20L);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 2UL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("b"), 3LL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("c"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyB), 3LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyC), 1LL);
     EXPECT_FALSE(anomalyTracker.detectAnomaly(25, *bucket25));
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 24L);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
@@ -265,7 +285,7 @@
     anomalyTracker.addPastBucket(bucket25, 25);
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 25L);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 1UL);
-    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets("d"), 1LL);
+    EXPECT_EQ(anomalyTracker.getSumOverPastBuckets(keyD), 1LL);
     EXPECT_FALSE(anomalyTracker.detectAnomaly(28, *bucket28));
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
@@ -274,7 +294,7 @@
     EXPECT_EQ(anomalyTracker.mLastAlarmTimestampNs, eventTimestamp4);
 
     // Updates current bucket #28.
-    (*bucket28)["e"] = 5;
+    (*bucket28)[keyE] = 5;
     EXPECT_TRUE(anomalyTracker.detectAnomaly(28, *bucket28));
     EXPECT_EQ(anomalyTracker.mMostRecentBucketNum, 27L);
     EXPECT_EQ(anomalyTracker.mSumOverPastBuckets.size(), 0UL);
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 01ba82d..eb0fafe 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -63,7 +63,7 @@
     vector<KeyValuePair> kv_list;
     kv_list.push_back(kv1);
     map<string, HashableDimensionKey> queryKey;
-    queryKey[conditionName] = getHashableKey(kv_list);
+    queryKey[conditionName] = HashableDimensionKey(kv_list);
     return queryKey;
 }
 
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index 51eabd5..eec94539 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_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/CountMetricProducer.h"
+#include "metrics_test_helper.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -150,13 +150,13 @@
     event1.write("111");  // uid
     event1.init();
     ConditionKey key1;
-    key1["APP_IN_BACKGROUND_PER_UID"] = "2:111|";
+    key1["APP_IN_BACKGROUND_PER_UID"] = getMockedDimensionKey(2, "111");
 
     LogEvent event2(1, bucketStartTimeNs + 10);
     event2.write("222");  // uid
     event2.init();
     ConditionKey key2;
-    key2["APP_IN_BACKGROUND_PER_UID"] = "2:222|";
+    key2["APP_IN_BACKGROUND_PER_UID"] = getMockedDimensionKey(2, "222");
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse));
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index e4fc67f..baaac67 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_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/EventMetricProducer.h"
+#include "metrics_test_helper.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -100,13 +100,13 @@
     event1.write("111");  // uid
     event1.init();
     ConditionKey key1;
-    key1["APP_IN_BACKGROUND_PER_UID"] = "2:111|";
+    key1["APP_IN_BACKGROUND_PER_UID"] = getMockedDimensionKey(2, "111");
 
     LogEvent event2(1, bucketStartTimeNs + 10);
     event2.write("222");  // uid
     event2.init();
     ConditionKey key2;
-    key2["APP_IN_BACKGROUND_PER_UID"] = "2:222|";
+    key2["APP_IN_BACKGROUND_PER_UID"] = getMockedDimensionKey(2, "222");
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse));
diff --git a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
index 68b7dcb..5204834 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "src/metrics/GaugeMetricProducer.h"
 #include "logd/LogEvent.h"
 #include "metrics_test_helper.h"
-#include "src/metrics/GaugeMetricProducer.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 4e5e0d6..7dac0fb 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "src/metrics/duration_helper/MaxDurationTracker.h"
 #include "metrics_test_helper.h"
 #include "src/condition/ConditionWizard.h"
-#include "src/metrics/duration_helper/MaxDurationTracker.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -37,13 +37,18 @@
 namespace statsd {
 
 const ConfigKey kConfigKey(0, "test");
-const string eventKey = "event";
+
+const HashableDimensionKey eventKey = getMockedDimensionKey(0, "1");
+const HashableDimensionKey conditionKey = getMockedDimensionKey(4, "1");
+const HashableDimensionKey key1 = getMockedDimensionKey(1, "1");
+const HashableDimensionKey key2 = getMockedDimensionKey(1, "2");
 
 TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
-    ConditionKey key1;
+    ConditionKey conditionKey1;
+    conditionKey1["condition"] = conditionKey;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
@@ -51,15 +56,15 @@
     MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, false, bucketStartTimeNs,
                                bucketSizeNs, {});
 
-    tracker.noteStart("1", true, bucketStartTimeNs, key1);
+    tracker.noteStart(key1, true, bucketStartTimeNs, conditionKey1);
     // Event starts again. This would not change anything as it already starts.
-    tracker.noteStart("1", true, bucketStartTimeNs + 3, key1);
+    tracker.noteStart(key1, true, bucketStartTimeNs + 3, conditionKey1);
     // Stopped.
-    tracker.noteStop("1", bucketStartTimeNs + 10, false);
+    tracker.noteStop(key1, bucketStartTimeNs + 10, false);
 
     // Another event starts in this bucket.
-    tracker.noteStart("2", true, bucketStartTimeNs + 20, key1);
-    tracker.noteStop("2", bucketStartTimeNs + 40, false /*stop all*/);
+    tracker.noteStart(key2, true, bucketStartTimeNs + 20, conditionKey1);
+    tracker.noteStop(key2, bucketStartTimeNs + 40, false /*stop all*/);
 
     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
     EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
@@ -71,7 +76,8 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
-    ConditionKey key1;
+    ConditionKey conditionKey1;
+    conditionKey1["condition"] = conditionKey;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
@@ -79,10 +85,10 @@
     MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, false, bucketStartTimeNs,
                                bucketSizeNs, {});
 
-    tracker.noteStart("1", true, bucketStartTimeNs + 1, key1);
+    tracker.noteStart(key1, true, bucketStartTimeNs + 1, conditionKey1);
 
     // Another event starts in this bucket.
-    tracker.noteStart("2", true, bucketStartTimeNs + 20, key1);
+    tracker.noteStart(key2, true, bucketStartTimeNs + 20, conditionKey1);
     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 40, &buckets);
     tracker.noteStopAll(bucketStartTimeNs + bucketSizeNs + 40);
     EXPECT_TRUE(tracker.mInfos.empty());
@@ -101,7 +107,8 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
-    ConditionKey key1;
+    ConditionKey conditionKey1;
+    conditionKey1["condition"] = conditionKey;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
@@ -110,14 +117,16 @@
                                bucketSizeNs, {});
 
     // The event starts.
-    tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
+    tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, conditionKey1);
 
     // Starts again. Does not change anything.
-    tracker.noteStart("", true, bucketStartTimeNs + bucketSizeNs + 1, key1);
+    tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + bucketSizeNs + 1,
+                      conditionKey1);
 
     // The event stops at early 4th bucket.
     tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 20, &buckets);
-    tracker.noteStop("", bucketStartTimeNs + (3 * bucketSizeNs) + 20, false /*stop all*/);
+    tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + (3 * bucketSizeNs) + 20,
+                     false /*stop all*/);
     EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
     EXPECT_EQ(3u, buckets[eventKey].size());
     EXPECT_EQ((unsigned long long)(bucketSizeNs - 1), buckets[eventKey][0].mDuration);
@@ -129,7 +138,8 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
-    ConditionKey key1;
+    ConditionKey conditionKey1;
+    conditionKey1["condition"] = conditionKey;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
@@ -138,10 +148,10 @@
                                bucketSizeNs, {});
 
     // 2 starts
-    tracker.noteStart("", true, bucketStartTimeNs + 1, key1);
-    tracker.noteStart("", true, bucketStartTimeNs + 10, key1);
+    tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, conditionKey1);
+    tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 10, conditionKey1);
     // one stop
-    tracker.noteStop("", bucketStartTimeNs + 20, false /*stop all*/);
+    tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + 20, false /*stop all*/);
 
     tracker.flushIfNeeded(bucketStartTimeNs + (2 * bucketSizeNs) + 1, &buckets);
 
@@ -151,7 +161,7 @@
     EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
 
     // real stop now.
-    tracker.noteStop("", bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
+    tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
     tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1, &buckets);
 
     EXPECT_EQ(3u, buckets[eventKey].size());
@@ -163,10 +173,11 @@
 TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    ConditionKey key1;
-    key1["APP_BACKGROUND"] = "1:maps|";
+    ConditionKey conditionKey1;
+    HashableDimensionKey eventKey = getMockedDimensionKey(2, "maps");
+    conditionKey1["APP_BACKGROUND"] = conditionKey;
 
-    EXPECT_CALL(*wizard, query(_, key1))  // #4
+    EXPECT_CALL(*wizard, query(_, conditionKey1))  // #4
             .WillOnce(Return(ConditionState::kFalse));
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
@@ -180,11 +191,11 @@
                                bucketSizeNs, {});
     EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
 
-    tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+    tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1);
 
     tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
 
-    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
+    tracker.noteStop(key1, eventStartTimeNs + durationTimeNs, false);
 
     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
     EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
@@ -195,15 +206,15 @@
 TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
     Alert alert;
     alert.set_name("alert");
-    alert.set_metric_name("1");
+    alert.set_metric_name("metric");
     alert.set_trigger_if_sum_gt(32 * NS_PER_SEC);
     alert.set_number_of_buckets(2);
     alert.set_refractory_period_secs(1);
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    ConditionKey key1;
-    key1["APP_BACKGROUND"] = "1:maps|";
+    ConditionKey conditionKey1;
+    conditionKey1["APP_BACKGROUND"] = conditionKey;
     uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
     uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
@@ -212,14 +223,14 @@
     MaxDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, -1, true, bucketStartTimeNs,
                                bucketSizeNs, {anomalyTracker});
 
-    tracker.noteStart("1", true, eventStartTimeNs, key1);
-    tracker.noteStop("1", eventStartTimeNs + 10, false);
+    tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1);
+    tracker.noteStop(key1, eventStartTimeNs + 10, false);
     EXPECT_EQ(anomalyTracker->mLastAlarmTimestampNs, -1);
     EXPECT_EQ(10LL, tracker.mDuration);
 
-    tracker.noteStart("2", true, eventStartTimeNs + 20, key1);
+    tracker.noteStart(key2, true, eventStartTimeNs + 20, conditionKey1);
     tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, &buckets);
-    tracker.noteStop("2", eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, false);
+    tracker.noteStop(key2, eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC, false);
     EXPECT_EQ((long long)(4 * NS_PER_SEC + 1LL), tracker.mDuration);
     EXPECT_EQ(anomalyTracker->mLastAlarmTimestampNs,
               (long long)(eventStartTimeNs + 2 * bucketSizeNs + 3 * NS_PER_SEC));
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index 99d3e05..9ec302f 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -12,9 +12,9 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include "src/metrics/duration_helper/OringDurationTracker.h"
 #include "metrics_test_helper.h"
 #include "src/condition/ConditionWizard.h"
-#include "src/metrics/duration_helper/OringDurationTracker.h"
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
@@ -35,13 +35,17 @@
 namespace statsd {
 
 const ConfigKey kConfigKey(0, "test");
-const string eventKey = "event";
+const HashableDimensionKey eventKey = getMockedDimensionKey(0, "event");
+
+const HashableDimensionKey kConditionKey1 = getMockedDimensionKey(1, "maps");
+const HashableDimensionKey kEventKey1 = getMockedDimensionKey(2, "maps");
+const HashableDimensionKey kEventKey2 = getMockedDimensionKey(3, "maps");
 
 TEST(OringDurationTrackerTest, TestDurationOverlap) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
-    key1["APP_BACKGROUND"] = "1:maps|";
+    key1["APP_BACKGROUND"] = kConditionKey1;
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
@@ -53,12 +57,12 @@
     OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false,
                                  bucketStartTimeNs, bucketSizeNs, {});
 
-    tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+    tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
-    tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1);  // overlapping wl
+    tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, key1);  // overlapping wl
     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
 
-    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
+    tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false);
     tracker.flushIfNeeded(eventStartTimeNs + bucketSizeNs + 1, &buckets);
     EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
 
@@ -70,7 +74,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
-    key1["APP_BACKGROUND"] = "1:maps|";
+    key1["APP_BACKGROUND"] = kConditionKey1;
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
@@ -81,11 +85,11 @@
     OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs,
                                  bucketSizeNs, {});
 
-    tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
-    tracker.noteStart("2:maps", true, eventStartTimeNs + 10, key1);  // overlapping wl
+    tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
+    tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, key1);  // overlapping wl
 
-    tracker.noteStop("2:maps", eventStartTimeNs + 2000, false);
-    tracker.noteStop("2:maps", eventStartTimeNs + 2003, false);
+    tracker.noteStop(kEventKey1, eventStartTimeNs + 2000, false);
+    tracker.noteStop(kEventKey1, eventStartTimeNs + 2003, false);
 
     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
     EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
@@ -97,7 +101,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
-    key1["APP_BACKGROUND"] = "1:maps|";
+    key1["APP_BACKGROUND"] = kConditionKey1;
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
@@ -108,8 +112,8 @@
     OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs,
                                  bucketSizeNs, {});
 
-    tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
-    tracker.noteStart("3:maps", true, eventStartTimeNs + 10, key1);  // overlapping wl
+    tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
+    tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, key1);  // overlapping wl
 
     tracker.noteStopAll(eventStartTimeNs + 2003);
 
@@ -123,7 +127,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
-    key1["APP_BACKGROUND"] = "1:maps|";
+    key1["APP_BACKGROUND"] = kConditionKey1;
 
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
 
@@ -135,18 +139,18 @@
     OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs,
                                  bucketSizeNs, {});
 
-    tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+    tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
     tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs, &buckets);
-    tracker.noteStart("2:maps", true, eventStartTimeNs + 2 * bucketSizeNs, key1);
+    tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2 * bucketSizeNs, key1);
     EXPECT_EQ((long long)(bucketStartTimeNs + 2 * bucketSizeNs), tracker.mLastStartTime);
 
     EXPECT_EQ(2u, buckets[eventKey].size());
     EXPECT_EQ(bucketSizeNs - 1, buckets[eventKey][0].mDuration);
     EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
 
-    tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + 10, false);
-    tracker.noteStop("2:maps", eventStartTimeNs + 2 * bucketSizeNs + 12, false);
+    tracker.noteStop(kEventKey1, eventStartTimeNs + 2 * bucketSizeNs + 10, false);
+    tracker.noteStop(kEventKey1, eventStartTimeNs + 2 * bucketSizeNs + 12, false);
     tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 12, &buckets);
     EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
     EXPECT_EQ(2u, buckets[eventKey].size());
@@ -158,7 +162,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
-    key1["APP_BACKGROUND"] = "1:maps|";
+    key1["APP_BACKGROUND"] = kConditionKey1;
 
     EXPECT_CALL(*wizard, query(_, key1))  // #4
             .WillOnce(Return(ConditionState::kFalse));
@@ -173,11 +177,11 @@
     OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false,
                                  bucketStartTimeNs, bucketSizeNs, {});
 
-    tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+    tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
 
     tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
 
-    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
+    tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false);
 
     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
     EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
@@ -189,7 +193,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
-    key1["APP_BACKGROUND"] = "1:maps|";
+    key1["APP_BACKGROUND"] = kConditionKey1;
 
     EXPECT_CALL(*wizard, query(_, key1))
             .Times(2)
@@ -206,13 +210,13 @@
     OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, false,
                                  bucketStartTimeNs, bucketSizeNs, {});
 
-    tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
+    tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
     // condition to false; record duration 5n
     tracker.onSlicedConditionMayChange(eventStartTimeNs + 5);
     // condition to true.
     tracker.onSlicedConditionMayChange(eventStartTimeNs + 1000);
     // 2nd duration: 1000ns
-    tracker.noteStop("2:maps", eventStartTimeNs + durationTimeNs, false);
+    tracker.noteStop(kEventKey1, eventStartTimeNs + durationTimeNs, false);
 
     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
     EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
@@ -224,7 +228,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
-    key1["APP_BACKGROUND"] = "1:maps|";
+    key1["APP_BACKGROUND"] = kConditionKey1;
 
     EXPECT_CALL(*wizard, query(_, key1))  // #4
             .WillOnce(Return(ConditionState::kFalse));
@@ -238,14 +242,14 @@
     OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true, bucketStartTimeNs,
                                  bucketSizeNs, {});
 
-    tracker.noteStart("2:maps", true, eventStartTimeNs, key1);
-    tracker.noteStart("2:maps", true, eventStartTimeNs + 2, key1);
+    tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
+    tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2, key1);
 
-    tracker.noteStop("2:maps", eventStartTimeNs + 3, false);
+    tracker.noteStop(kEventKey1, eventStartTimeNs + 3, false);
 
     tracker.onSlicedConditionMayChange(eventStartTimeNs + 15);
 
-    tracker.noteStop("2:maps", eventStartTimeNs + 2003, false);
+    tracker.noteStop(kEventKey1, eventStartTimeNs + 2003, false);
 
     tracker.flushIfNeeded(bucketStartTimeNs + bucketSizeNs + 1, &buckets);
     EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
@@ -264,7 +268,7 @@
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     ConditionKey key1;
-    key1["APP_BACKGROUND"] = "1:maps|";
+    key1["APP_BACKGROUND"] = kConditionKey1;
     uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
     uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
@@ -274,22 +278,22 @@
                                  bucketSizeNs, {anomalyTracker});
 
     // Nothing in the past bucket.
-    tracker.noteStart("", true, eventStartTimeNs, key1);
+    tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, key1);
     EXPECT_EQ((long long)(alert.trigger_if_sum_gt() + eventStartTimeNs),
               tracker.predictAnomalyTimestampNs(*anomalyTracker, eventStartTimeNs));
 
-    tracker.noteStop("", eventStartTimeNs + 3, false);
+    tracker.noteStop(DEFAULT_DIMENSION_KEY, eventStartTimeNs + 3, false);
     EXPECT_EQ(0u, buckets[eventKey].size());
 
     uint64_t event1StartTimeNs = eventStartTimeNs + 10;
-    tracker.noteStart("1", true, event1StartTimeNs, key1);
+    tracker.noteStart(kEventKey1, true, event1StartTimeNs, key1);
     // No past buckets. The anomaly will happen in bucket #0.
     EXPECT_EQ((long long)(event1StartTimeNs + alert.trigger_if_sum_gt() - 3),
               tracker.predictAnomalyTimestampNs(*anomalyTracker, event1StartTimeNs));
 
     uint64_t event1StopTimeNs = eventStartTimeNs + bucketSizeNs + 10;
     tracker.flushIfNeeded(event1StopTimeNs, &buckets);
-    tracker.noteStop("1", event1StopTimeNs, false);
+    tracker.noteStop(kEventKey1, event1StopTimeNs, false);
 
     EXPECT_TRUE(buckets.find(eventKey) != buckets.end());
     EXPECT_EQ(1u, buckets[eventKey].size());
@@ -301,16 +305,16 @@
 
     // One past buckets. The anomaly will happen in bucket #1.
     uint64_t event2StartTimeNs = eventStartTimeNs + bucketSizeNs + 15;
-    tracker.noteStart("1", true, event2StartTimeNs, key1);
+    tracker.noteStart(kEventKey1, true, event2StartTimeNs, key1);
     EXPECT_EQ((long long)(event2StartTimeNs + alert.trigger_if_sum_gt() - bucket0Duration -
                           bucket1Duration),
               tracker.predictAnomalyTimestampNs(*anomalyTracker, event2StartTimeNs));
-    tracker.noteStop("1", event2StartTimeNs + 1, false);
+    tracker.noteStop(kEventKey1, event2StartTimeNs + 1, false);
 
     // Only one past buckets is applicable. Bucket +0 should be trashed. The anomaly will happen in
     // bucket #2.
     uint64_t event3StartTimeNs = bucketStartTimeNs + 2 * bucketSizeNs - 9 * NS_PER_SEC;
-    tracker.noteStart("1", true, event3StartTimeNs, key1);
+    tracker.noteStart(kEventKey1, true, event3StartTimeNs, key1);
     EXPECT_EQ((long long)(event3StartTimeNs + alert.trigger_if_sum_gt() - bucket1Duration - 1LL),
               tracker.predictAnomalyTimestampNs(*anomalyTracker, event3StartTimeNs));
 }
@@ -326,7 +330,7 @@
     unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     ConditionKey key1;
-    key1["APP_BACKGROUND"] = "1:maps|";
+    key1["APP_BACKGROUND"] = kConditionKey1;
     uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
     uint64_t eventStartTimeNs = bucketStartTimeNs + NS_PER_SEC + 1;
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
@@ -335,21 +339,21 @@
     OringDurationTracker tracker(kConfigKey, "metric", eventKey, wizard, 1, true /*nesting*/,
                                  bucketStartTimeNs, bucketSizeNs, {anomalyTracker});
 
-    tracker.noteStart("", true, eventStartTimeNs, key1);
-    tracker.noteStop("", eventStartTimeNs + 10, false);
+    tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, key1);
+    tracker.noteStop(DEFAULT_DIMENSION_KEY, eventStartTimeNs + 10, false);
     EXPECT_EQ(anomalyTracker->mLastAlarmTimestampNs, -1);
     EXPECT_TRUE(tracker.mStarted.empty());
     EXPECT_EQ(10LL, tracker.mDuration);
 
     EXPECT_EQ(0u, tracker.mStarted.size());
 
-    tracker.noteStart("", true, eventStartTimeNs + 20, key1);
+    tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs + 20, key1);
     EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
     EXPECT_EQ((long long)(51ULL * NS_PER_SEC),
               (long long)(anomalyTracker->mAlarms.begin()->second->timestampSec * NS_PER_SEC));
     tracker.flushIfNeeded(eventStartTimeNs + 2 * bucketSizeNs + 25, &buckets);
-    tracker.noteStop("", eventStartTimeNs + 2 * bucketSizeNs + 25, false);
-    EXPECT_EQ(anomalyTracker->getSumOverPastBuckets("event"), (long long)(bucketSizeNs));
+    tracker.noteStop(DEFAULT_DIMENSION_KEY, eventStartTimeNs + 2 * bucketSizeNs + 25, false);
+    EXPECT_EQ(anomalyTracker->getSumOverPastBuckets(eventKey), (long long)(bucketSizeNs));
     EXPECT_EQ((long long)(eventStartTimeNs + 2 * bucketSizeNs + 25),
               anomalyTracker->mLastAlarmTimestampNs);
 }
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.cpp b/cmds/statsd/tests/metrics/metrics_test_helper.cpp
new file mode 100644
index 0000000..a0a854a
--- /dev/null
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.cpp
@@ -0,0 +1,34 @@
+// 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 "metrics_test_helper.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+HashableDimensionKey getMockedDimensionKey(int key, string value) {
+    KeyValuePair pair;
+    pair.set_key(key);
+    pair.set_value_str(value);
+
+    vector<KeyValuePair> pairs;
+    pairs.push_back(pair);
+
+    return HashableDimensionKey(pairs);
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index fa221aa..7cb3329 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -38,6 +38,8 @@
     MOCK_METHOD2(Pull, bool(const int pullCode, vector<std::shared_ptr<LogEvent>>* data));
 };
 
+HashableDimensionKey getMockedDimensionKey(int key, std::string value);
+
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
+}  // namespace android
\ No newline at end of file