1/ Support nested message and repeated fields in statsd.
2/ Filter gauge fields by FieldMatcher.
3/ Wire up wakelock attribution chain.
4/ e2e test: wakelock duration metric with aggregated predicate dimensions.
5/ e2e test: count metric with multiple metric condition links for 2 predicates and 1 non-sliced predicate.

Test: statsd unit test passed.

Change-Id: I89db31cb068184a54e0a892fad710966d3127bc9
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index c392540..a365f54 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -20,6 +20,9 @@
     src/stats_log.proto \
     src/statsd_config.proto \
     src/atoms.proto \
+    src/field_util.cpp \
+    src/stats_log_util.cpp \
+    src/dimension.cpp \
     src/anomaly/AnomalyMonitor.cpp \
     src/anomaly/AnomalyTracker.cpp \
     src/anomaly/DurationAnomalyTracker.cpp \
@@ -163,6 +166,7 @@
     tests/indexed_priority_queue_test.cpp \
     tests/LogEntryMatcher_test.cpp \
     tests/LogReader_test.cpp \
+    tests/LogEvent_test.cpp \
     tests/MetricsManager_test.cpp \
     tests/StatsLogProcessor_test.cpp \
     tests/UidMap_test.cpp \
@@ -176,7 +180,10 @@
     tests/metrics/ValueMetricProducer_test.cpp \
     tests/metrics/GaugeMetricProducer_test.cpp \
     tests/guardrail/StatsdStats_test.cpp \
-    tests/metrics/metrics_test_helper.cpp
+    tests/metrics/metrics_test_helper.cpp \
+    tests/statsd_test_util.cpp \
+    tests/e2e/WakelockDuration_e2e_test.cpp \
+    tests/e2e/MetricConditionLink_e2e_test.cpp
 
 LOCAL_STATIC_LIBRARIES := \
     $(statsd_common_static_libraries) \
@@ -198,4 +205,4 @@
 
 ##############################
 
-include $(call all-makefiles-under,$(LOCAL_PATH))
+include $(call all-makefiles-under,$(LOCAL_PATH))
\ No newline at end of file
diff --git a/cmds/statsd/src/HashableDimensionKey.cpp b/cmds/statsd/src/HashableDimensionKey.cpp
index 0b6f8f2..6da1243 100644
--- a/cmds/statsd/src/HashableDimensionKey.cpp
+++ b/cmds/statsd/src/HashableDimensionKey.cpp
@@ -13,92 +13,104 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 #include "HashableDimensionKey.h"
+#include "dimension.h"
 
 namespace android {
 namespace os {
 namespace statsd {
 
+android::hash_t hashDimensionsValue(const DimensionsValue& value) {
+    android::hash_t hash = 0;
+    hash = android::JenkinsHashMix(hash, android::hash_type(value.field()));
+
+    hash = android::JenkinsHashMix(hash, android::hash_type((int)value.value_case()));
+    switch (value.value_case()) {
+        case DimensionsValue::ValueCase::kValueStr:
+            hash = android::JenkinsHashMix(
+                    hash,
+                    static_cast<uint32_t>(std::hash<std::string>()(value.value_str())));
+            break;
+        case DimensionsValue::ValueCase::kValueInt:
+            hash = android::JenkinsHashMix(hash, android::hash_type(value.value_int()));
+            break;
+        case DimensionsValue::ValueCase::kValueLong:
+            hash = android::JenkinsHashMix(
+                    hash, android::hash_type(static_cast<int64_t>(value.value_long())));
+            break;
+        case DimensionsValue::ValueCase::kValueBool:
+            hash = android::JenkinsHashMix(hash, android::hash_type(value.value_bool()));
+            break;
+        case DimensionsValue::ValueCase::kValueFloat: {
+            float floatVal = value.value_float();
+            hash = android::JenkinsHashMixBytes(hash, (uint8_t*)&floatVal, sizeof(float));
+            break;
+        }
+        case DimensionsValue::ValueCase::kValueTuple: {
+            hash = android::JenkinsHashMix(hash, android::hash_type(
+                value.value_tuple().dimensions_value_size()));
+            for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) {
+                hash = android::JenkinsHashMix(
+                    hash,
+                    hashDimensionsValue(value.value_tuple().dimensions_value(i)));
+            }
+            break;
+        }
+        case DimensionsValue::ValueCase::VALUE_NOT_SET:
+            break;
+    }
+    return JenkinsHashWhiten(hash);
+}
+
 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 += "|";
-    }
+    DimensionsValueToString(getDimensionsValue(), &flattened);
     return flattened;
 }
 
-bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const {
-    const auto& keyValue2 = that.getKeyValuePairs();
-    if (mKeyValuePairs.size() != keyValue2.size()) {
+bool compareDimensionsValue(const DimensionsValue& s1, const DimensionsValue& s2) {
+    if (s1.field() != s2.field()) {
         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;
-        }
+    if (s1.value_case() != s1.value_case()) {
+        return false;
     }
-    return true;
+    switch (s1.value_case()) {
+        case DimensionsValue::ValueCase::kValueStr:
+            return (s1.value_str() == s2.value_str());
+        case DimensionsValue::ValueCase::kValueInt:
+            return s1.value_int() == s2.value_int();
+        case DimensionsValue::ValueCase::kValueLong:
+            return s1.value_long() == s2.value_long();
+        case DimensionsValue::ValueCase::kValueBool:
+            return s1.value_bool() == s2.value_bool();
+        case DimensionsValue::ValueCase::kValueFloat:
+            return s1.value_float() == s2.value_float();
+        case DimensionsValue::ValueCase::kValueTuple:
+            {
+                if (s1.value_tuple().dimensions_value_size() !=
+                        s2.value_tuple().dimensions_value_size()) {
+                    return false;
+                }
+                bool allMatched = true;
+                for (int i = 0; allMatched && i < s1.value_tuple().dimensions_value_size(); ++i) {
+                    allMatched &= compareDimensionsValue(s1.value_tuple().dimensions_value(i),
+                                                    s2.value_tuple().dimensions_value(i));
+                }
+                return allMatched;
+            }
+        case DimensionsValue::ValueCase::VALUE_NOT_SET:
+        default:
+            return true;
+    }
+}
+
+bool HashableDimensionKey::operator==(const HashableDimensionKey& that) const {
+    return compareDimensionsValue(getDimensionsValue(), that.getDimensionsValue());
 };
 
 bool HashableDimensionKey::operator<(const HashableDimensionKey& that) const {
diff --git a/cmds/statsd/src/HashableDimensionKey.h b/cmds/statsd/src/HashableDimensionKey.h
index 85215552..3a4ffce 100644
--- a/cmds/statsd/src/HashableDimensionKey.h
+++ b/cmds/statsd/src/HashableDimensionKey.h
@@ -25,20 +25,20 @@
 
 class HashableDimensionKey {
 public:
-    explicit HashableDimensionKey(const std::vector<KeyValuePair>& keyValuePairs)
-        : mKeyValuePairs(keyValuePairs){};
+    explicit HashableDimensionKey(const DimensionsValue& dimensionsValue)
+        : mDimensionsValue(dimensionsValue){};
 
     HashableDimensionKey(){};
 
     HashableDimensionKey(const HashableDimensionKey& that)
-        : mKeyValuePairs(that.getKeyValuePairs()){};
+        : mDimensionsValue(that.getDimensionsValue()){};
 
     HashableDimensionKey& operator=(const HashableDimensionKey& from) = default;
 
     std::string toString() const;
 
-    inline const std::vector<KeyValuePair>& getKeyValuePairs() const {
-        return mKeyValuePairs;
+    inline const DimensionsValue& getDimensionsValue() const {
+        return mDimensionsValue;
     }
 
     bool operator==(const HashableDimensionKey& that) const;
@@ -50,9 +50,11 @@
     }
 
 private:
-    std::vector<KeyValuePair> mKeyValuePairs;
+    DimensionsValue mDimensionsValue;
 };
 
+android::hash_t hashDimensionsValue(const DimensionsValue& value);
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
@@ -60,42 +62,11 @@
 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;
+        return hashDimensionsValue(key.getDimensionsValue());
     }
 };
 
diff --git a/cmds/statsd/src/StatsLogProcessor.cpp b/cmds/statsd/src/StatsLogProcessor.cpp
index 5f9b53a..13f332e 100644
--- a/cmds/statsd/src/StatsLogProcessor.cpp
+++ b/cmds/statsd/src/StatsLogProcessor.cpp
@@ -63,11 +63,12 @@
 
 StatsLogProcessor::StatsLogProcessor(const sp<UidMap>& uidMap,
                                      const sp<AnomalyMonitor>& anomalyMonitor,
+                                     const long timeBaseSec,
                                      const std::function<void(const ConfigKey&)>& sendBroadcast)
     : mUidMap(uidMap),
       mAnomalyMonitor(anomalyMonitor),
       mSendBroadcast(sendBroadcast),
-      mTimeBaseSec(time(nullptr)) {
+      mTimeBaseSec(timeBaseSec) {
     // On each initialization of StatsLogProcessor, check stats-data directory to see if there is
     // any left over data to be read.
     StorageManager::sendBroadcast(STATS_DATA_DIR, mSendBroadcast);
@@ -97,7 +98,6 @@
         pair.second->onLogEvent(msg);
         flushIfNecessary(msg.GetTimestampNs(), pair.first, *(pair.second));
     }
-
     // Hard-coded logic to update the isolated uid's in the uid-map.
     // The field numbers need to be currently updated by hand with atoms.proto
     if (msg.GetTagId() == android::util::ISOLATED_UID_CHANGED) {
@@ -117,9 +117,7 @@
 
 void StatsLogProcessor::OnConfigUpdated(const ConfigKey& key, const StatsdConfig& config) {
     ALOGD("Updated configuration for key %s", key.ToString().c_str());
-
     sp<MetricsManager> newMetricsManager = new MetricsManager(key, config, mTimeBaseSec, mUidMap);
-
     auto it = mMetricsManagers.find(key);
     if (it == mMetricsManagers.end() && mMetricsManagers.size() > StatsdStats::kMaxConfigCount) {
         ALOGE("Can't accept more configs!");
@@ -152,6 +150,19 @@
     return it->second->byteSize();
 }
 
+void StatsLogProcessor::onDumpReport(const ConfigKey& key, const uint64_t& dumpTimeStampNs, ConfigMetricsReportList* report) {
+    auto it = mMetricsManagers.find(key);
+    if (it == mMetricsManagers.end()) {
+        ALOGW("Config source %s does not exist", key.ToString().c_str());
+        return;
+    }
+    report->mutable_config_key()->set_uid(key.GetUid());
+    report->mutable_config_key()->set_name(key.GetName());
+    ConfigMetricsReport* configMetricsReport = report->add_reports();
+    it->second->onDumpReport(dumpTimeStampNs, configMetricsReport);
+    // TODO: dump uid mapping.
+}
+
 void StatsLogProcessor::onDumpReport(const ConfigKey& key, vector<uint8_t>* outData) {
     auto it = mMetricsManagers.find(key);
     if (it == mMetricsManagers.end()) {
diff --git a/cmds/statsd/src/StatsLogProcessor.h b/cmds/statsd/src/StatsLogProcessor.h
index 1e5c426..f62fc4e 100644
--- a/cmds/statsd/src/StatsLogProcessor.h
+++ b/cmds/statsd/src/StatsLogProcessor.h
@@ -13,9 +13,10 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-#ifndef STATS_LOG_PROCESSOR_H
-#define STATS_LOG_PROCESSOR_H
 
+#pragma once
+
+#include <gtest/gtest_prod.h>
 #include "config/ConfigListener.h"
 #include "logd/LogReader.h"
 #include "metrics/MetricsManager.h"
@@ -33,6 +34,7 @@
 class StatsLogProcessor : public ConfigListener {
 public:
     StatsLogProcessor(const sp<UidMap>& uidMap, const sp<AnomalyMonitor>& anomalyMonitor,
+                      const long timeBaseSec,
                       const std::function<void(const ConfigKey&)>& sendBroadcast);
     virtual ~StatsLogProcessor();
 
@@ -44,6 +46,7 @@
     size_t GetMetricsSize(const ConfigKey& key) const;
 
     void onDumpReport(const ConfigKey& key, vector<uint8_t>* outData);
+    void onDumpReport(const ConfigKey& key, const uint64_t& dumpTimeStampNs, ConfigMetricsReportList* report);
 
     /* Tells MetricsManager that the alarms in anomalySet have fired. Modifies anomalySet. */
     void onAnomalyAlarmFired(
@@ -81,10 +84,12 @@
     FRIEND_TEST(StatsLogProcessorTest, TestRateLimitByteSize);
     FRIEND_TEST(StatsLogProcessorTest, TestRateLimitBroadcast);
     FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge);
+    FRIEND_TEST(StatsLogProcessorTest, TestDropWhenByteSizeTooLarge);
+    FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions);
+    FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
+
 };
 
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
-
-#endif  // STATS_LOG_PROCESSOR_H
diff --git a/cmds/statsd/src/StatsService.cpp b/cmds/statsd/src/StatsService.cpp
index 76e2e48..e8b0bd2 100644
--- a/cmds/statsd/src/StatsService.cpp
+++ b/cmds/statsd/src/StatsService.cpp
@@ -75,7 +75,7 @@
 {
     mUidMap = new UidMap();
     mConfigManager = new ConfigManager();
-    mProcessor = new StatsLogProcessor(mUidMap, mAnomalyMonitor, [this](const ConfigKey& key) {
+    mProcessor = new StatsLogProcessor(mUidMap, mAnomalyMonitor, time(nullptr), [this](const ConfigKey& key) {
         sp<IStatsCompanionService> sc = getStatsCompanionService();
         auto receiver = mConfigManager->GetConfigReceiver(key);
         if (sc == nullptr) {
diff --git a/cmds/statsd/src/atoms.proto b/cmds/statsd/src/atoms.proto
index 1c6d9b0..1ee86f0 100644
--- a/cmds/statsd/src/atoms.proto
+++ b/cmds/statsd/src/atoms.proto
@@ -104,24 +104,18 @@
 }
 
 /**
- * An attribution represents an application or module that is part of process where a particular bit
- * of work is done.
+ * This proto represents a node of an attribution chain.
+ * Note: All attribution chains are represented as a repeated field of type
+ * AttributionNode. It is understood that in such arrays, the order is that
+ * of calls, that is [A, B, C] if A calls B that calls C.
  */
-message Attribution {
-    // The uid for an application or module.
+message AttributionNode {
+    // The uid for a given element in the attribution chain.
     optional int32 uid = 1;
-    // The string tag for the attribution node.
-    optional string tag = 2;
-}
 
-/**
- * An attribution chain represents the chained attributions of applications or modules that
- * resulted in a particular bit of work being done.
- * The ordering of the attributions is that of calls, that is uid = [A, B, C] if A calls B that
- * calls C.
- */
-message AttributionChain {
-    repeated Attribution attribution = 1;
+    // The (optional) string tag for an element in the attribution chain. If the
+    // element has no tag, it is encoded as an empty string.
+    optional string tag = 2;
 }
 
 /*
@@ -151,7 +145,7 @@
  */
 
 message AttributionChainDummyAtom {
-    optional AttributionChain attribution_chain = 1;
+    repeated AttributionNode attribution_node = 1;
     optional int32 value = 2;
 }
 
@@ -421,8 +415,7 @@
  *   TODO
  */
 message WakelockStateChanged {
-    // TODO: Add attribution instead of uid.
-    optional int32 uid = 1;
+    repeated AttributionNode attribution_node = 1;
 
     // Type of wakelock.
     enum Type {
@@ -780,13 +773,14 @@
   *   frameworks/base/services/core/java/com/android/server/am/ActivityRecord.java
  */
 message ActivityForegroundStateChanged {
+    optional int32 uid = 1;
+    optional string pkg_name = 2;
+    optional string class_name = 3;
+
     enum Activity {
         MOVE_TO_BACKGROUND = 0;
         MOVE_TO_FOREGROUND = 1;
     }
-    optional int32 uid = 1;
-    optional string pkg_name = 2;
-    optional string class_name = 3;
     optional Activity activity = 4;
 }
 
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.cpp b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
index bb4b817..52b83d8 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.cpp
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.cpp
@@ -103,7 +103,7 @@
 }
 
 void CombinationConditionTracker::isConditionMet(
-        const map<string, HashableDimensionKey>& conditionParameters,
+        const ConditionKey& conditionParameters,
         const vector<sp<ConditionTracker>>& allConditions,
         vector<ConditionState>& conditionCache) const {
     for (const int childIndex : mChildren) {
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index 9336914..8942833 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -41,7 +41,7 @@
                            std::vector<ConditionState>& conditionCache,
                            std::vector<bool>& changedCache) override;
 
-    void isConditionMet(const std::map<std::string, HashableDimensionKey>& conditionParameters,
+    void isConditionMet(const ConditionKey& conditionParameters,
                         const std::vector<sp<ConditionTracker>>& allConditions,
                         std::vector<ConditionState>& conditionCache) const override;
 
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index 6f66ad6..1154b6f 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -42,6 +42,8 @@
 
     virtual ~ConditionTracker(){};
 
+    inline const string& getName() { return mName; }
+
     // Initialize this ConditionTracker. This initialization is done recursively (DFS). It can also
     // be done in the constructor, but we do it separately because (1) easy to return a bool to
     // indicate whether the initialization is successful. (2) makes unit test easier.
@@ -83,7 +85,7 @@
     //                  done recursively
     // [conditionCache]: the cache holding the condition evaluation values.
     virtual void isConditionMet(
-            const std::map<std::string, HashableDimensionKey>& conditionParameters,
+            const ConditionKey& conditionParameters,
             const std::vector<sp<ConditionTracker>>& allConditions,
             std::vector<ConditionState>& conditionCache) const = 0;
 
diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp
index 411f7e5..d99c2cc 100644
--- a/cmds/statsd/src/condition/ConditionWizard.cpp
+++ b/cmds/statsd/src/condition/ConditionWizard.cpp
@@ -24,7 +24,7 @@
 using std::vector;
 
 ConditionState ConditionWizard::query(const int index,
-                                      const map<string, HashableDimensionKey>& parameters) {
+                                      const ConditionKey& parameters) {
     vector<ConditionState> cache(mAllConditions.size(), ConditionState::kNotEvaluated);
 
     mAllConditions[index]->isConditionMet(parameters, mAllConditions, cache);
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
index 30a3684..4ff5c07 100644
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ b/cmds/statsd/src/condition/ConditionWizard.h
@@ -19,6 +19,7 @@
 
 #include "ConditionTracker.h"
 #include "condition_util.h"
+#include "stats_util.h"
 
 namespace android {
 namespace os {
@@ -40,7 +41,7 @@
     // the conditionParameters contains the parameters for it's children SimpleConditionTrackers.
     virtual ConditionState query(
             const int conditionIndex,
-            const std::map<std::string, HashableDimensionKey>& conditionParameters);
+            const ConditionKey& conditionParameters);
 
 private:
     std::vector<sp<ConditionTracker>> mAllConditions;
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index a63bc04..1803cbb 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -76,10 +76,9 @@
         mStopAllLogMatcherIndex = -1;
     }
 
-    mOutputDimension.insert(mOutputDimension.begin(), simplePredicate.dimension().begin(),
-                            simplePredicate.dimension().end());
+    mOutputDimensions = simplePredicate.dimensions();
 
-    if (mOutputDimension.size() > 0) {
+    if (mOutputDimensions.child_size() > 0) {
         mSliced = true;
     }
 
@@ -150,6 +149,14 @@
                                                   bool matchStart,
                                                   std::vector<ConditionState>& conditionCache,
                                                   std::vector<bool>& conditionChangedCache) {
+    if ((int)conditionChangedCache.size() <= mIndex) {
+        ALOGE("handleConditionEvent: param conditionChangedCache not initialized.");
+        return;
+    }
+    if ((int)conditionCache.size() <= mIndex) {
+        ALOGE("handleConditionEvent: param conditionCache not initialized.");
+        return;
+    }
     bool changed = false;
     auto outputIt = mSlicedConditionState.find(outputKey);
     ConditionState newCondition;
@@ -278,36 +285,64 @@
         return;
     }
 
-    // outputKey is the output key values. e.g, uid:1234
-    const HashableDimensionKey outputKey(getDimensionKey(event, mOutputDimension));
-    handleConditionEvent(outputKey, matchedState == 1, conditionCache, conditionChangedCache);
+    // outputKey is the output values. e.g, uid:1234
+    const std::vector<DimensionsValue> outputValues = getDimensionKeys(event, mOutputDimensions);
+    if (outputValues.size() == 0) {
+        // The original implementation would generate an empty string dimension hash when condition
+        // is not sliced.
+        handleConditionEvent(
+            DEFAULT_DIMENSION_KEY, matchedState == 1, conditionCache, conditionChangedCache);
+    } else if (outputValues.size() == 1) {
+        handleConditionEvent(HashableDimensionKey(outputValues[0]), matchedState == 1,
+            conditionCache, conditionChangedCache);
+    } else {
+        // If this event has multiple nodes in the attribution chain,  this log event probably will
+        // generate multiple dimensions. If so, we will find if the condition changes for any
+        // dimension and ask the corresponding metric producer to verify whether the actual sliced
+        // condition has changed or not.
+        // A high level assumption is that a predicate is either sliced or unsliced. We will never
+        // have both sliced and unsliced version of a predicate.
+        for (const DimensionsValue& outputValue : outputValues) {
+            vector<ConditionState> dimensionalConditionCache(conditionCache.size(),
+                                                             ConditionState::kNotEvaluated);
+            vector<bool> dimensionalConditionChangedCache(conditionChangedCache.size(), false);
+
+            handleConditionEvent(HashableDimensionKey(outputValue), matchedState == 1,
+                dimensionalConditionCache, dimensionalConditionChangedCache);
+
+            OrConditionState(dimensionalConditionCache, &conditionCache);
+            OrBooleanVector(dimensionalConditionChangedCache, &conditionChangedCache);
+        }
+    }
 }
 
 void SimpleConditionTracker::isConditionMet(
-        const map<string, HashableDimensionKey>& conditionParameters,
+        const ConditionKey& conditionParameters,
         const vector<sp<ConditionTracker>>& allConditions,
         vector<ConditionState>& conditionCache) const {
     const auto pair = conditionParameters.find(mName);
-    HashableDimensionKey key =
-            (pair == conditionParameters.end()) ? DEFAULT_DIMENSION_KEY : pair->second;
 
-    if (pair == conditionParameters.end() && mOutputDimension.size() > 0) {
+    if (pair == conditionParameters.end() && mOutputDimensions.child_size() > 0) {
         ALOGE("Predicate %s output has dimension, but it's not specified in the query!",
               mName.c_str());
         conditionCache[mIndex] = mInitialValue;
         return;
     }
+    std::vector<HashableDimensionKey> defaultKeys = {DEFAULT_DIMENSION_KEY};
+    const std::vector<HashableDimensionKey> &keys =
+            (pair == conditionParameters.end()) ? defaultKeys : pair->second;
 
-    VLOG("simplePredicate %s query key: %s", mName.c_str(), key.c_str());
-
-    auto startedCountIt = mSlicedConditionState.find(key);
-    if (startedCountIt == mSlicedConditionState.end()) {
-        conditionCache[mIndex] = mInitialValue;
-    } else {
-        conditionCache[mIndex] =
-                startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+    ConditionState conditionState = ConditionState::kNotEvaluated;
+    for (const auto& key : keys) {
+        auto startedCountIt = mSlicedConditionState.find(key);
+        if (startedCountIt != mSlicedConditionState.end()) {
+            conditionState = conditionState |
+                    (startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse);
+        } else {
+            conditionState = conditionState | mInitialValue;
+        }
     }
-
+    conditionCache[mIndex] = conditionState;
     VLOG("Predicate %s return %d", mName.c_str(), conditionCache[mIndex]);
 }
 
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index 644d84c..5048635 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -46,7 +46,7 @@
                            std::vector<ConditionState>& conditionCache,
                            std::vector<bool>& changedCache) override;
 
-    void isConditionMet(const std::map<std::string, HashableDimensionKey>& conditionParameters,
+    void isConditionMet(const ConditionKey& conditionParameters,
                         const std::vector<sp<ConditionTracker>>& allConditions,
                         std::vector<ConditionState>& conditionCache) const override;
 
@@ -66,7 +66,7 @@
 
     ConditionState mInitialValue;
 
-    std::vector<KeyMatcher> mOutputDimension;
+    FieldMatcher mOutputDimensions;
 
     std::map<HashableDimensionKey, int> mSlicedConditionState;
 
diff --git a/cmds/statsd/src/condition/condition_util.cpp b/cmds/statsd/src/condition/condition_util.cpp
index 53ef9d5..a5aee73 100644
--- a/cmds/statsd/src/condition/condition_util.cpp
+++ b/cmds/statsd/src/condition/condition_util.cpp
@@ -27,6 +27,7 @@
 #include "ConditionTracker.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "stats_util.h"
+#include "dimension.h"
 
 namespace android {
 namespace os {
@@ -93,23 +94,106 @@
     return newCondition;
 }
 
-HashableDimensionKey getDimensionKeyForCondition(const LogEvent& event,
-                                                 const MetricConditionLink& link) {
-    vector<KeyMatcher> eventKey;
-    eventKey.reserve(link.key_in_what().size());
+ConditionState operator|(ConditionState l, ConditionState r) {
+    return l >= r ? l : r;
+}
 
-    for (const auto& key : link.key_in_what()) {
-        eventKey.push_back(key);
+void OrConditionState(const std::vector<ConditionState>& ref, vector<ConditionState> * ored) {
+    if (ref.size() != ored->size()) {
+        return;
+    }
+    for (size_t i = 0; i < ored->size(); ++i) {
+        ored->at(i) = ored->at(i) | ref.at(i);
+    }
+}
+
+void OrBooleanVector(const std::vector<bool>& ref, vector<bool> * ored) {
+    if (ref.size() != ored->size()) {
+        return;
+    }
+    for (size_t i = 0; i < ored->size(); ++i) {
+        ored->at(i) = ored->at(i) | ref.at(i);
+    }
+}
+
+void getFieldsFromFieldMatcher(const FieldMatcher& matcher, const Field& parentField,
+                       std::vector<Field> *allFields) {
+    Field newParent = parentField;
+    Field* leaf = getSingleLeaf(&newParent);
+    leaf->set_field(matcher.field());
+    if (matcher.child_size() == 0) {
+        allFields->push_back(newParent);
+        return;
+    }
+    for (int i = 0; i < matcher.child_size(); ++i) {
+        leaf->add_child();
+        getFieldsFromFieldMatcher(matcher.child(i), newParent, allFields);
+    }
+}
+
+void getFieldsFromFieldMatcher(const FieldMatcher& matcher, std::vector<Field> *allFields) {
+    Field parentField;
+    getFieldsFromFieldMatcher(matcher, parentField, allFields);
+}
+
+void flattenValueLeaves(const DimensionsValue& value,
+                        std::vector<DimensionsValue> *allLaves) {
+    switch (value.value_case()) {
+        case DimensionsValue::ValueCase::kValueStr:
+        case DimensionsValue::ValueCase::kValueInt:
+        case DimensionsValue::ValueCase::kValueLong:
+        case DimensionsValue::ValueCase::kValueBool:
+        case DimensionsValue::ValueCase::kValueFloat:
+        case DimensionsValue::ValueCase::VALUE_NOT_SET:
+            allLaves->push_back(value);
+            break;
+        case DimensionsValue::ValueCase::kValueTuple:
+            for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) {
+                flattenValueLeaves(value.value_tuple().dimensions_value(i), allLaves);
+            }
+            break;
+    }
+}
+
+std::vector<HashableDimensionKey> getDimensionKeysForCondition(
+    const LogEvent& event, const MetricConditionLink& link) {
+    std::vector<Field> whatFields;
+    getFieldsFromFieldMatcher(link.dimensions_in_what(), &whatFields);
+    std::vector<Field> conditionFields;
+    getFieldsFromFieldMatcher(link.dimensions_in_condition(), &conditionFields);
+
+    std::vector<HashableDimensionKey> hashableDimensionKeys;
+
+    // TODO(yanglu): here we could simplify the logic to get the leaf value node in what and
+    // directly construct the full condition value tree.
+    std::vector<DimensionsValue> whatValues = getDimensionKeys(event, link.dimensions_in_what());
+
+    for (size_t i = 0; i < whatValues.size(); ++i) {
+        std::vector<DimensionsValue> whatLeaves;
+        flattenValueLeaves(whatValues[i], &whatLeaves);
+        if (whatLeaves.size() != whatFields.size() ||
+            whatLeaves.size() != conditionFields.size()) {
+            ALOGE("Dimensions between what and condition not equal.");
+            return hashableDimensionKeys;
+        }
+        FieldValueMap conditionValueMap;
+        for (size_t j = 0; j < whatLeaves.size(); ++j) {
+            if (!setFieldInLeafValueProto(conditionFields[j], &whatLeaves[j])) {
+                ALOGE("Not able to reset the field for condition leaf value.");
+                return hashableDimensionKeys;
+            }
+            conditionValueMap.insert(std::make_pair(conditionFields[j], whatLeaves[j]));
+        }
+        std::vector<DimensionsValue> conditionValues;
+        findDimensionsValues(conditionValueMap, link.dimensions_in_condition(), &conditionValues);
+        if (conditionValues.size() != 1) {
+            ALOGE("Not able to find unambiguous field value in condition atom.");
+            continue;
+        }
+        hashableDimensionKeys.push_back(HashableDimensionKey(conditionValues[0]));
     }
 
-    vector<KeyValuePair> dimensionKey = getDimensionKey(event, eventKey);
-
-    for (int i = 0; i < link.key_in_what_size(); i++) {
-        auto& kv = dimensionKey[i];
-        kv.set_key(link.key_in_condition(i).key());
-    }
-
-    return HashableDimensionKey(dimensionKey);
+    return hashableDimensionKeys;
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/condition/condition_util.h b/cmds/statsd/src/condition/condition_util.h
index 934c207..598027b 100644
--- a/cmds/statsd/src/condition/condition_util.h
+++ b/cmds/statsd/src/condition/condition_util.h
@@ -32,12 +32,16 @@
     kTrue = 1,
 };
 
+ConditionState operator|(ConditionState l, ConditionState r);
+void OrConditionState(const std::vector<ConditionState>& ref, vector<ConditionState> * ored);
+void OrBooleanVector(const std::vector<bool>& ref, vector<bool> * ored);
+
 ConditionState evaluateCombinationCondition(const std::vector<int>& children,
                                             const LogicalOperation& operation,
                                             const std::vector<ConditionState>& conditionCache);
 
-HashableDimensionKey getDimensionKeyForCondition(const LogEvent& event,
-                                                 const MetricConditionLink& link);
+std::vector<HashableDimensionKey> getDimensionKeysForCondition(
+        const LogEvent& event, const MetricConditionLink& link);
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/config/ConfigManager.cpp b/cmds/statsd/src/config/ConfigManager.cpp
index cb3f3d6..addc111 100644
--- a/cmds/statsd/src/config/ConfigManager.cpp
+++ b/cmds/statsd/src/config/ConfigManager.cpp
@@ -209,7 +209,7 @@
     int WAKE_LOCK_ACQUIRE_VALUE = 1;
     int WAKE_LOCK_RELEASE_VALUE = 0;
 
-    int APP_USAGE_ID = 12345;
+    int APP_USAGE_TAG_ID = 12345;
     int APP_USAGE_UID_KEY_ID = 1;
     int APP_USAGE_STATE_KEY = 2;
     int APP_USAGE_FOREGROUND = 1;
@@ -259,8 +259,9 @@
     metric->set_name("METRIC_2");
     metric->set_what("PROCESS_STATE_CHANGE");
     metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
-    KeyMatcher* keyMatcher = metric->add_dimension();
-    keyMatcher->set_key(UID_PROCESS_STATE_UID_KEY);
+    FieldMatcher* dimensions = metric->mutable_dimensions();
+    dimensions->set_field(UID_PROCESS_STATE_TAG_ID);
+    dimensions->add_child()->set_field(UID_PROCESS_STATE_UID_KEY);
 
     // Anomaly threshold for background count.
     // TODO(b/70627390): Uncomment once the bug is fixed.
@@ -280,8 +281,10 @@
     metric->set_name("METRIC_3");
     metric->set_what("PROCESS_STATE_CHANGE");
     metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
-    keyMatcher = metric->add_dimension();
-    keyMatcher->set_key(UID_PROCESS_STATE_UID_KEY);
+
+    dimensions = metric->mutable_dimensions();
+    dimensions->set_field(UID_PROCESS_STATE_TAG_ID);
+    dimensions->add_child()->set_field(UID_PROCESS_STATE_UID_KEY);
     metric->set_condition("SCREEN_IS_OFF");
 
     // Count wake lock, slice by uid, while SCREEN_IS_ON and app in background
@@ -289,41 +292,52 @@
     metric->set_name("METRIC_4");
     metric->set_what("APP_GET_WL");
     metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
-    keyMatcher = metric->add_dimension();
-    keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
+    dimensions = metric->mutable_dimensions();
+    dimensions->set_field(WAKE_LOCK_TAG_ID);
+    dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
+
+
     metric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
     MetricConditionLink* link = metric->add_links();
     link->set_condition("APP_IS_BACKGROUND");
-    link->add_key_in_what()->set_key(WAKE_LOCK_UID_KEY_ID);
-    link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
+    link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID);
+    link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
+    link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID);
+    link->mutable_dimensions_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID);
 
     // Duration of an app holding any wl, while screen on and app in background, slice by uid
     DurationMetric* durationMetric = config.add_duration_metric();
     durationMetric->set_name("METRIC_5");
     durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
     durationMetric->set_aggregation_type(DurationMetric_AggregationType_SUM);
-    keyMatcher = durationMetric->add_dimension();
-    keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
+    dimensions = durationMetric->mutable_dimensions();
+    dimensions->set_field(WAKE_LOCK_TAG_ID);
+    dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
     durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
     durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
     link = durationMetric->add_links();
     link->set_condition("APP_IS_BACKGROUND");
-    link->add_key_in_what()->set_key(WAKE_LOCK_UID_KEY_ID);
-    link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
+    link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID);
+    link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
+    link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID);
+    link->mutable_dimensions_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID);
 
     // max Duration of an app holding any wl, while screen on and app in background, slice by uid
     durationMetric = config.add_duration_metric();
     durationMetric->set_name("METRIC_6");
     durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
     durationMetric->set_aggregation_type(DurationMetric_AggregationType_MAX_SPARSE);
-    keyMatcher = durationMetric->add_dimension();
-    keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
+    dimensions = durationMetric->mutable_dimensions();
+    dimensions->set_field(WAKE_LOCK_TAG_ID);
+    dimensions->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
     durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
     durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
     link = durationMetric->add_links();
     link->set_condition("APP_IS_BACKGROUND");
-    link->add_key_in_what()->set_key(WAKE_LOCK_UID_KEY_ID);
-    link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
+    link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID);
+    link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
+    link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID);
+    link->mutable_dimensions_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID);
 
     // Duration of an app holding any wl, while screen on and app in background
     durationMetric = config.add_duration_metric();
@@ -334,8 +348,11 @@
     durationMetric->set_condition("APP_IS_BACKGROUND_AND_SCREEN_ON");
     link = durationMetric->add_links();
     link->set_condition("APP_IS_BACKGROUND");
-    link->add_key_in_what()->set_key(WAKE_LOCK_UID_KEY_ID);
-    link->add_key_in_condition()->set_key(APP_USAGE_UID_KEY_ID);
+    link->mutable_dimensions_in_what()->set_field(WAKE_LOCK_TAG_ID);
+    link->mutable_dimensions_in_what()->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
+    link->mutable_dimensions_in_condition()->set_field(APP_USAGE_TAG_ID);
+    link->mutable_dimensions_in_condition()->add_child()->set_field(APP_USAGE_UID_KEY_ID);
+
 
     // Duration of screen on time.
     durationMetric = config.add_duration_metric();
@@ -362,8 +379,9 @@
     valueMetric->set_what("KERNEL_WAKELOCK");
     valueMetric->set_value_field(KERNEL_WAKELOCK_COUNT_KEY);
     valueMetric->set_condition("SCREEN_IS_ON");
-    keyMatcher = valueMetric->add_dimension();
-    keyMatcher->set_key(KERNEL_WAKELOCK_NAME_KEY);
+    dimensions = valueMetric->mutable_dimensions();
+    dimensions->set_field(KERNEL_WAKELOCK_TAG_ID);
+    dimensions->add_child()->set_field(KERNEL_WAKELOCK_NAME_KEY);
     // This is for testing easier. We should never set bucket size this small.
     valueMetric->mutable_bucket()->set_bucket_size_millis(60 * 1000L);
 
@@ -376,73 +394,75 @@
     GaugeMetric* gaugeMetric = config.add_gauge_metric();
     gaugeMetric->set_name("METRIC_10");
     gaugeMetric->set_what("DEVICE_TEMPERATURE");
-    gaugeMetric->mutable_gauge_fields()->add_field_num(DEVICE_TEMPERATURE_KEY);
+    auto gaugeFieldMatcher = gaugeMetric->mutable_gauge_fields_filter()->mutable_fields();
+    gaugeFieldMatcher->set_field(DEVICE_TEMPERATURE_TAG_ID);
+    gaugeFieldMatcher->add_child()->set_field(DEVICE_TEMPERATURE_KEY);
     gaugeMetric->mutable_bucket()->set_bucket_size_millis(60 * 1000L);
 
-    // Event matchers............
+    // Event matchers.
     AtomMatcher* temperatureAtomMatcher = config.add_atom_matcher();
     temperatureAtomMatcher->set_name("DEVICE_TEMPERATURE");
-    temperatureAtomMatcher->mutable_simple_atom_matcher()->set_tag(
+    temperatureAtomMatcher->mutable_simple_atom_matcher()->set_atom_id(
         DEVICE_TEMPERATURE_TAG_ID);
 
     AtomMatcher* eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("SCREEN_TURNED_ON");
     SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_tag(SCREEN_EVENT_TAG_ID);
-    KeyValueMatcher* keyValueMatcher = simpleAtomMatcher->add_key_value_matcher();
-    keyValueMatcher->mutable_key_matcher()->set_key(SCREEN_EVENT_STATE_KEY);
-    keyValueMatcher->set_eq_int(SCREEN_EVENT_ON_VALUE);
+    simpleAtomMatcher->set_atom_id(SCREEN_EVENT_TAG_ID);
+    FieldValueMatcher* fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
+    fieldValueMatcher->set_field(SCREEN_EVENT_STATE_KEY);
+    fieldValueMatcher->set_eq_int(SCREEN_EVENT_ON_VALUE);
 
     eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("SCREEN_TURNED_OFF");
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_tag(SCREEN_EVENT_TAG_ID);
-    keyValueMatcher = simpleAtomMatcher->add_key_value_matcher();
-    keyValueMatcher->mutable_key_matcher()->set_key(SCREEN_EVENT_STATE_KEY);
-    keyValueMatcher->set_eq_int(SCREEN_EVENT_OFF_VALUE);
+    simpleAtomMatcher->set_atom_id(SCREEN_EVENT_TAG_ID);
+    fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
+    fieldValueMatcher->set_field(SCREEN_EVENT_STATE_KEY);
+    fieldValueMatcher->set_eq_int(SCREEN_EVENT_OFF_VALUE);
 
     eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("PROCESS_STATE_CHANGE");
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_tag(UID_PROCESS_STATE_TAG_ID);
+    simpleAtomMatcher->set_atom_id(UID_PROCESS_STATE_TAG_ID);
 
     eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("APP_GOES_BACKGROUND");
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_tag(APP_USAGE_ID);
-    keyValueMatcher = simpleAtomMatcher->add_key_value_matcher();
-    keyValueMatcher->mutable_key_matcher()->set_key(APP_USAGE_STATE_KEY);
-    keyValueMatcher->set_eq_int(APP_USAGE_BACKGROUND);
+    simpleAtomMatcher->set_atom_id(APP_USAGE_TAG_ID);
+    fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
+    fieldValueMatcher->set_field(APP_USAGE_STATE_KEY);
+    fieldValueMatcher->set_eq_int(APP_USAGE_BACKGROUND);
 
     eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("APP_GOES_FOREGROUND");
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_tag(APP_USAGE_ID);
-    keyValueMatcher = simpleAtomMatcher->add_key_value_matcher();
-    keyValueMatcher->mutable_key_matcher()->set_key(APP_USAGE_STATE_KEY);
-    keyValueMatcher->set_eq_int(APP_USAGE_FOREGROUND);
+    simpleAtomMatcher->set_atom_id(APP_USAGE_TAG_ID);
+    fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
+    fieldValueMatcher->set_field(APP_USAGE_STATE_KEY);
+    fieldValueMatcher->set_eq_int(APP_USAGE_FOREGROUND);
 
     eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("APP_GET_WL");
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_tag(WAKE_LOCK_TAG_ID);
-    keyValueMatcher = simpleAtomMatcher->add_key_value_matcher();
-    keyValueMatcher->mutable_key_matcher()->set_key(WAKE_LOCK_STATE_KEY);
-    keyValueMatcher->set_eq_int(WAKE_LOCK_ACQUIRE_VALUE);
+    simpleAtomMatcher->set_atom_id(WAKE_LOCK_TAG_ID);
+    fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
+    fieldValueMatcher->set_field(WAKE_LOCK_STATE_KEY);
+    fieldValueMatcher->set_eq_int(WAKE_LOCK_ACQUIRE_VALUE);
 
     eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("APP_RELEASE_WL");
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_tag(WAKE_LOCK_TAG_ID);
-    keyValueMatcher = simpleAtomMatcher->add_key_value_matcher();
-    keyValueMatcher->mutable_key_matcher()->set_key(WAKE_LOCK_STATE_KEY);
-    keyValueMatcher->set_eq_int(WAKE_LOCK_RELEASE_VALUE);
+    simpleAtomMatcher->set_atom_id(WAKE_LOCK_TAG_ID);
+    fieldValueMatcher = simpleAtomMatcher->add_field_value_matcher();
+    fieldValueMatcher->set_field(WAKE_LOCK_STATE_KEY);
+    fieldValueMatcher->set_eq_int(WAKE_LOCK_RELEASE_VALUE);
 
     // pulled events
     eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("KERNEL_WAKELOCK");
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_tag(KERNEL_WAKELOCK_TAG_ID);
+    simpleAtomMatcher->set_atom_id(KERNEL_WAKELOCK_TAG_ID);
 
     // Predicates.............
     Predicate* predicate = config.add_predicate();
@@ -464,8 +484,9 @@
     simplePredicate = predicate->mutable_simple_predicate();
     simplePredicate->set_start("APP_GOES_BACKGROUND");
     simplePredicate->set_stop("APP_GOES_FOREGROUND");
-    KeyMatcher* predicate_dimension1 = simplePredicate->add_dimension();
-    predicate_dimension1->set_key(APP_USAGE_UID_KEY_ID);
+    FieldMatcher* predicate_dimension1 = simplePredicate->mutable_dimensions();
+    predicate_dimension1->set_field(APP_USAGE_TAG_ID);
+    predicate_dimension1->add_child()->set_field(APP_USAGE_UID_KEY_ID);
     simplePredicate->set_count_nesting(false);
 
     predicate = config.add_predicate();
@@ -480,10 +501,10 @@
     simplePredicate = predicate->mutable_simple_predicate();
     simplePredicate->set_start("APP_GET_WL");
     simplePredicate->set_stop("APP_RELEASE_WL");
-    KeyMatcher* predicate_dimension = simplePredicate->add_dimension();
-    predicate_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
-    predicate_dimension = simplePredicate->add_dimension();
-    predicate_dimension->set_key(WAKE_LOCK_NAME_KEY);
+    FieldMatcher* predicate_dimension = simplePredicate->mutable_dimensions();
+    predicate_dimension1->set_field(WAKE_LOCK_TAG_ID);
+    predicate_dimension->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
+    predicate_dimension->add_child()->set_field(WAKE_LOCK_NAME_KEY);
     simplePredicate->set_count_nesting(true);
 
     predicate = config.add_predicate();
@@ -492,8 +513,9 @@
     simplePredicate->set_start("APP_GET_WL");
     simplePredicate->set_stop("APP_RELEASE_WL");
     simplePredicate->set_initial_value(SimplePredicate_InitialValue_FALSE);
-    predicate_dimension = simplePredicate->add_dimension();
-    predicate_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
+    predicate_dimension = simplePredicate->mutable_dimensions();
+    predicate_dimension->set_field(WAKE_LOCK_TAG_ID);
+    predicate_dimension->add_child()->set_field(WAKE_LOCK_UID_KEY_ID);
     simplePredicate->set_count_nesting(true);
 
     return config;
diff --git a/cmds/statsd/src/dimension.cpp b/cmds/statsd/src/dimension.cpp
new file mode 100644
index 0000000..886a33bf
--- /dev/null
+++ b/cmds/statsd/src/dimension.cpp
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Log.h"
+
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "dimension.h"
+#include "field_util.h"
+
+
+namespace android {
+namespace os {
+namespace statsd {
+
+const DimensionsValue* getSingleLeafValue(const DimensionsValue* value) {
+    if (value->value_case() == DimensionsValue::ValueCase::kValueTuple) {
+        return getSingleLeafValue(&value->value_tuple().dimensions_value(0));
+    } else {
+        return value;
+    }
+}
+
+DimensionsValue getSingleLeafValue(const DimensionsValue& value) {
+    const DimensionsValue* leafValue = getSingleLeafValue(&value);
+    return *leafValue;
+}
+
+void appendLeafNodeToParent(const Field& field,
+                            const DimensionsValue& value,
+                            DimensionsValue* parentValue) {
+    if (field.child_size() <= 0) {
+        *parentValue = value;
+        parentValue->set_field(field.field());
+        return;
+    }
+    parentValue->set_field(field.field());
+    int idx = -1;
+    for (int i = 0; i < parentValue->mutable_value_tuple()->dimensions_value_size(); ++i) {
+        if (parentValue->mutable_value_tuple()->dimensions_value(i).field() ==
+                field.child(0).field()) {
+            idx = i;
+        }
+    }
+    if (idx < 0) {
+        parentValue->mutable_value_tuple()->add_dimensions_value();
+        idx = parentValue->mutable_value_tuple()->dimensions_value_size() - 1;
+    }
+    appendLeafNodeToParent(
+        field.child(0), value,
+        parentValue->mutable_value_tuple()->mutable_dimensions_value(idx));
+}
+
+void addNodeToRootDimensionsValues(const Field& field,
+                                   const DimensionsValue& node,
+                                   std::vector<DimensionsValue>* rootValues) {
+    if (rootValues == nullptr) {
+        return;
+    }
+    if (rootValues->empty()) {
+        DimensionsValue rootValue;
+        appendLeafNodeToParent(field, node, &rootValue);
+        rootValues->push_back(rootValue);
+    } else {
+        for (size_t i = 0; i < rootValues->size(); ++i) {
+            appendLeafNodeToParent(field, node, &rootValues->at(i));
+        }
+    }
+}
+
+namespace {
+
+void findDimensionsValues(
+       const FieldValueMap& fieldValueMap,
+       const FieldMatcher& matcher,
+       const Field& field,
+       std::vector<DimensionsValue>* rootDimensionsValues);
+
+void findNonRepeatedDimensionsValues(
+       const FieldValueMap& fieldValueMap,
+       const FieldMatcher& matcher,
+       const Field& field,
+       std::vector<DimensionsValue>* rootValues) {
+    if (matcher.child_size() > 0) {
+        for (const auto& childMatcher : matcher.child()) {
+          Field childField = field;
+          appendLeaf(&childField, childMatcher.field());
+          findDimensionsValues(fieldValueMap, childMatcher, childField, rootValues);
+        }
+    } else {
+        auto ret = fieldValueMap.equal_range(field);
+        int found = 0;
+        for (auto it = ret.first; it != ret.second; ++it) {
+            found++;
+        }
+        // Not found.
+        if (found <= 0) {
+            return;
+        }
+        if (found > 1) {
+            ALOGE("Found multiple values for optional field.");
+            return;
+        }
+        addNodeToRootDimensionsValues(field, ret.first->second, rootValues);
+    }
+}
+
+void findRepeatedDimensionsValues(const FieldValueMap& fieldValueMap,
+                                  const FieldMatcher& matcher,
+                                  const Field& field,
+                                  std::vector<DimensionsValue>* rootValues) {
+    if (matcher.position() == Position::FIRST) {
+        Field first_field = field;
+        setPositionForLeaf(&first_field, 0);
+        findNonRepeatedDimensionsValues(fieldValueMap, matcher, first_field, rootValues);
+    } else {
+        auto itLower = fieldValueMap.lower_bound(field);
+        if (itLower == fieldValueMap.end()) {
+            return;
+        }
+        Field next_field = field;
+        getNextField(&next_field);
+        auto itUpper = fieldValueMap.lower_bound(next_field);
+
+        switch (matcher.position()) {
+             case Position::LAST:
+                 {
+                     itUpper--;
+                     if (itUpper != fieldValueMap.end()) {
+                         Field last_field = field;
+                         int last_index = getPositionByReferenceField(field, itUpper->first);
+                         if (last_index < 0) {
+                            return;
+                         }
+                         setPositionForLeaf(&last_field, last_index);
+                         findNonRepeatedDimensionsValues(
+                            fieldValueMap, matcher, last_field, rootValues);
+                     }
+                 }
+                 break;
+             case Position::ANY:
+                 {
+                    std::set<int> indexes;
+                    for (auto it = itLower; it != itUpper; ++it) {
+                        int index = getPositionByReferenceField(field, it->first);
+                        if (index >= 0) {
+                            indexes.insert(index);
+                        }
+                    }
+                    if (!indexes.empty()) {
+                        Field any_field = field;
+                        std::vector<DimensionsValue> allValues;
+                        for (const int index : indexes) {
+                             setPositionForLeaf(&any_field, index);
+                             std::vector<DimensionsValue> newValues = *rootValues;
+                             findNonRepeatedDimensionsValues(
+                                fieldValueMap, matcher, any_field, &newValues);
+                             allValues.insert(allValues.end(), newValues.begin(), newValues.end());
+                        }
+                        rootValues->clear();
+                        rootValues->insert(rootValues->end(), allValues.begin(), allValues.end());
+                    }
+                 }
+                 break;
+             default:
+                break;
+         }
+    }
+}
+
+void findDimensionsValues(
+       const FieldValueMap& fieldValueMap,
+       const FieldMatcher& matcher,
+       const Field& field,
+       std::vector<DimensionsValue>* rootDimensionsValues) {
+    if (!matcher.has_position()) {
+        findNonRepeatedDimensionsValues(fieldValueMap, matcher, field, rootDimensionsValues);
+    } else {
+        findRepeatedDimensionsValues(fieldValueMap, matcher, field, rootDimensionsValues);
+    }
+}
+
+} // namespace
+
+void findDimensionsValues(
+       const FieldValueMap& fieldValueMap,
+       const FieldMatcher& matcher,
+       std::vector<DimensionsValue>* rootDimensionsValues) {
+    findDimensionsValues(fieldValueMap, matcher,
+                    buildSimpleAtomField(matcher.field()), rootDimensionsValues);
+}
+
+FieldMatcher buildSimpleAtomFieldMatcher(const int tagId) {
+    FieldMatcher matcher;
+    matcher.set_field(tagId);
+    return matcher;
+}
+
+FieldMatcher buildSimpleAtomFieldMatcher(const int tagId, const int atomFieldNum) {
+    FieldMatcher matcher;
+    matcher.set_field(tagId);
+    matcher.add_child()->set_field(atomFieldNum);
+    return matcher;
+}
+
+constexpr int ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO = 1;
+constexpr int UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO = 1;
+constexpr int TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO = 2;
+
+void buildAttributionUidFieldMatcher(const int tagId, const Position position,
+                                     FieldMatcher *matcher) {
+    matcher->set_field(tagId);
+    matcher->add_child()->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO);
+    FieldMatcher* child = matcher->mutable_child(0)->add_child();
+    child->set_field(UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
+}
+
+void buildAttributionTagFieldMatcher(const int tagId, const Position position,
+                                     FieldMatcher *matcher) {
+    matcher->set_field(tagId);
+    FieldMatcher* child = matcher->add_child();
+    child->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO);
+    child->set_position(position);
+    child = child->add_child();
+    child->set_field(TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
+}
+
+void buildAttributionFieldMatcher(const int tagId, const Position position,
+                                  FieldMatcher *matcher) {
+    matcher->set_field(tagId);
+    FieldMatcher* child = matcher->add_child();
+    child->set_field(ATTRIBUTION_FIELD_NUM_IN_ATOM_PROTO);
+    child->set_position(position);
+    child = child->add_child();
+    child->set_field(UID_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
+    child->set_field(TAG_FIELD_NUM_IN_ATTRIBUTION_NODE_PROTO);
+}
+
+void DimensionsValueToString(const DimensionsValue& value, std::string *flattened) {
+    *flattened += std::to_string(value.field());
+    *flattened += ":";
+    switch (value.value_case()) {
+        case DimensionsValue::ValueCase::kValueStr:
+            *flattened += value.value_str();
+            break;
+        case DimensionsValue::ValueCase::kValueInt:
+            *flattened += std::to_string(value.value_int());
+            break;
+        case DimensionsValue::ValueCase::kValueLong:
+            *flattened += std::to_string(value.value_long());
+            break;
+        case DimensionsValue::ValueCase::kValueBool:
+            *flattened += std::to_string(value.value_bool());
+            break;
+        case DimensionsValue::ValueCase::kValueFloat:
+            *flattened += std::to_string(value.value_float());
+            break;
+        case DimensionsValue::ValueCase::kValueTuple:
+            {
+                *flattened += "{";
+                for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) {
+                    DimensionsValueToString(value.value_tuple().dimensions_value(i), flattened);
+                    *flattened += "|";
+                }
+                *flattened += "}";
+            }
+            break;
+        case DimensionsValue::ValueCase::VALUE_NOT_SET:
+            break;
+    }
+}
+
+void getDimensionsValueLeafNodes(
+    const DimensionsValue& value, std::vector<DimensionsValue> *leafNodes) {
+    switch (value.value_case()) {
+        case DimensionsValue::ValueCase::kValueStr:
+        case DimensionsValue::ValueCase::kValueInt:
+        case DimensionsValue::ValueCase::kValueLong:
+        case DimensionsValue::ValueCase::kValueBool:
+        case DimensionsValue::ValueCase::kValueFloat:
+            leafNodes->push_back(value);
+            break;
+        case DimensionsValue::ValueCase::kValueTuple:
+            for (int i = 0; i < value.value_tuple().dimensions_value_size(); ++i) {
+                getDimensionsValueLeafNodes(value.value_tuple().dimensions_value(i), leafNodes);
+            }
+            break;
+        case DimensionsValue::ValueCase::VALUE_NOT_SET:
+            break;
+        default:
+            break;
+    }
+}
+
+std::string DimensionsValueToString(const DimensionsValue& value) {
+    std::string flatten;
+    DimensionsValueToString(value, &flatten);
+    return flatten;
+}
+
+bool IsSubDimension(const DimensionsValue& dimension, const DimensionsValue& sub) {
+    if (dimension.field() != sub.field()) {
+        return false;
+    }
+    if (dimension.value_case() != sub.value_case()) {
+        return false;
+    }
+    switch (dimension.value_case()) {
+        case DimensionsValue::ValueCase::kValueStr:
+            return dimension.value_str() == sub.value_str();
+        case DimensionsValue::ValueCase::kValueInt:
+            return dimension.value_int() == sub.value_int();
+        case DimensionsValue::ValueCase::kValueLong:
+            return dimension.value_long() == sub.value_long();
+        case DimensionsValue::ValueCase::kValueBool:
+            return dimension.value_bool() == sub.value_bool();
+        case DimensionsValue::ValueCase::kValueFloat:
+            return dimension.value_float() == sub.value_float();
+        case DimensionsValue::ValueCase::kValueTuple: {
+            if (dimension.value_tuple().dimensions_value_size() < sub.value_tuple().dimensions_value_size()) {
+                return false;
+            }
+            bool allSub = true;
+            for (int i = 0; i < sub.value_tuple().dimensions_value_size(); ++i) {
+                bool isSub = false;
+                for (int j = 0; !isSub && j < dimension.value_tuple().dimensions_value_size(); ++j) {
+                    isSub |= IsSubDimension(dimension.value_tuple().dimensions_value(j),
+                                            sub.value_tuple().dimensions_value(i));
+                }
+                allSub &= isSub;
+            }
+            return allSub;
+        }
+        break;
+        case DimensionsValue::ValueCase::VALUE_NOT_SET:
+            return false;
+        default:
+            return false;
+    }
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/dimension.h b/cmds/statsd/src/dimension.h
new file mode 100644
index 0000000..c866958
--- /dev/null
+++ b/cmds/statsd/src/dimension.h
@@ -0,0 +1,61 @@
+/*
+ * 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 <log/logprint.h>
+#include <set>
+#include <vector>
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "field_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+
+// Returns the leaf node from the DimensionsValue proto. It assume that the input has only one
+// leaf node at most.
+const DimensionsValue* getSingleLeafValue(const DimensionsValue* value);
+DimensionsValue getSingleLeafValue(const DimensionsValue& value);
+
+// Constructs the DimensionsValue protos from the FieldMatcher. Each DimensionsValue proto
+// represents a tree. When the input proto has repeated fields and the input "dimensions" wants
+// "ANY" locations, it will return multiple trees.
+void findDimensionsValues(
+       const FieldValueMap& fieldValueMap,
+       const FieldMatcher& matcher,
+       std::vector<DimensionsValue>* rootDimensionsValues);
+
+// Utils to build FieldMatcher proto for simple one-depth atoms.
+FieldMatcher buildSimpleAtomFieldMatcher(const int tagId, const int atomFieldNum);
+FieldMatcher buildSimpleAtomFieldMatcher(const int tagId);
+
+// Utils to build FieldMatcher proto for attribution nodes.
+FieldMatcher buildAttributionUidFieldMatcher(const int tagId, const Position position);
+FieldMatcher buildAttributionTagFieldMatcher(const int tagId, const Position position);
+FieldMatcher buildAttributionFieldMatcher(const int tagId, const Position position);
+
+// Utils to print pretty string for DimensionsValue proto.
+std::string DimensionsValueToString(const DimensionsValue& value);
+void DimensionsValueToString(const DimensionsValue& value, std::string *flattened);
+
+bool IsSubDimension(const DimensionsValue& dimension, const DimensionsValue& sub);
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/field_util.cpp b/cmds/statsd/src/field_util.cpp
new file mode 100644
index 0000000..d10e167
--- /dev/null
+++ b/cmds/statsd/src/field_util.cpp
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "Log.h"
+#include "field_util.h"
+
+#include <set>
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// This function is to compare two Field trees where each node has at most one child.
+bool CompareField(const Field& a, const Field& b) {
+    if (a.field() < b.field()) {
+        return true;
+    }
+    if (a.field() > b.field()) {
+        return false;
+    }
+    if (a.position_index() < b.position_index()) {
+        return true;
+    }
+    if (a.position_index() > b.position_index()) {
+        return false;
+    }
+    if (a.child_size() < b.child_size()) {
+        return true;
+    }
+    if (a.child_size() > b.child_size()) {
+        return false;
+    }
+    if (a.child_size() == 0 && b.child_size() == 0) {
+       return false;
+    }
+    return CompareField(a.child(0), b.child(0));
+}
+
+const Field* getSingleLeaf(const Field* field) {
+    if (field->child_size() <= 0) {
+        return field;
+    } else {
+        return getSingleLeaf(&field->child(0));
+    }
+}
+
+Field* getSingleLeaf(Field* field) {
+    if (field->child_size() <= 0) {
+        return field;
+    } else {
+        return getSingleLeaf(field->mutable_child(0));
+    }
+}
+
+void FieldToString(const Field& field, std::string *flattened) {
+    *flattened += std::to_string(field.field());
+    if (field.has_position_index()) {
+        *flattened += "[";
+        *flattened += std::to_string(field.position_index());
+        *flattened += "]";
+    }
+    if (field.child_size() <= 0) {
+        return;
+    }
+    *flattened += ".";
+    *flattened += "{";
+    for (int i = 0 ; i < field.child_size(); ++i) {
+        *flattened += FieldToString(field.child(i));
+    }
+    *flattened += "},";
+}
+
+std::string FieldToString(const Field& field) {
+    std::string flatten;
+    FieldToString(field, &flatten);
+    return flatten;
+}
+
+bool setFieldInLeafValueProto(const Field &field, DimensionsValue* leafValue) {
+    if (field.child_size() <= 0) {
+        leafValue->set_field(field.field());
+        return true;
+    } else if (field.child_size() == 1)  {
+        return setFieldInLeafValueProto(field.child(0), leafValue);
+    } else {
+        ALOGE("Not able to set the 'field' in leaf value for multiple children.");
+        return false;
+    }
+}
+
+Field buildAtomField(const int tagId, const Field &atomField) {
+    Field field;
+    *field.add_child() = atomField;
+    field.set_field(tagId);
+    return field;
+}
+
+Field buildSimpleAtomField(const int tagId, const int atomFieldNum) {
+    Field field;
+    field.set_field(tagId);
+    field.add_child()->set_field(atomFieldNum);
+    return field;
+}
+
+Field buildSimpleAtomField(const int tagId) {
+    Field field;
+    field.set_field(tagId);
+    return field;
+}
+
+void appendLeaf(Field *parent, int node_field_num) {
+    if (!parent->has_field()) {
+        parent->set_field(node_field_num);
+    } else if (parent->child_size() <= 0) {
+        parent->add_child()->set_field(node_field_num);
+    } else {
+        appendLeaf(parent->mutable_child(0), node_field_num);
+    }
+}
+
+void appendLeaf(Field *parent, int node_field_num, int position) {
+    if (!parent->has_field()) {
+        parent->set_field(node_field_num);
+        parent->set_position_index(position);
+    } else if (parent->child_size() <= 0) {
+        auto child = parent->add_child();
+        child->set_field(node_field_num);
+        child->set_position_index(position);
+    } else {
+        appendLeaf(parent->mutable_child(0), node_field_num, position);
+    }
+}
+
+
+void getNextField(Field* field) {
+    if (field->child_size() <= 0) {
+        field->set_field(field->field() + 1);
+        return;
+    }
+    if (field->child_size() != 1) {
+        return;
+    }
+    getNextField(field->mutable_child(0));
+}
+
+void increasePosition(Field *field) {
+    if (!field->has_position_index()) {
+        field->set_position_index(0);
+    } else {
+        field->set_position_index(field->position_index() + 1);
+    }
+}
+
+int getPositionByReferenceField(const Field& ref, const Field& field_with_index) {
+    if (ref.child_size() <= 0) {
+        return field_with_index.position_index();
+    }
+    if (ref.child_size() != 1 ||
+        field_with_index.child_size() != 1) {
+        return -1;
+    }
+    return getPositionByReferenceField(ref.child(0), field_with_index.child(0));
+}
+
+void setPositionForLeaf(Field *field, int index) {
+    if (field->child_size() <= 0) {
+        field->set_position_index(index);
+    } else {
+        setPositionForLeaf(field->mutable_child(0), index);
+    }
+}
+
+void findFields(
+       const FieldValueMap& fieldValueMap,
+       const FieldMatcher& matcher,
+       const Field& field,
+       std::vector<Field>* rootFields);
+
+void findNonRepeatedFields(
+       const FieldValueMap& fieldValueMap,
+       const FieldMatcher& matcher,
+       const Field& field,
+       std::vector<Field>* rootFields) {
+    if (matcher.child_size() > 0) {
+        for (const auto& childMatcher : matcher.child()) {
+          Field childField = field;
+          appendLeaf(&childField, childMatcher.field());
+          findFields(fieldValueMap, childMatcher, childField, rootFields);
+        }
+    } else {
+        auto ret = fieldValueMap.equal_range(field);
+        int found = 0;
+        for (auto it = ret.first; it != ret.second; ++it) {
+            found++;
+        }
+        // Not found.
+        if (found <= 0) {
+            return;
+        }
+        if (found > 1) {
+            ALOGE("Found multiple values for optional field.");
+            return;
+        }
+        rootFields->push_back(ret.first->first);
+    }
+}
+
+void findRepeatedFields(const FieldValueMap& fieldValueMap, const FieldMatcher& matcher,
+                        const Field& field, std::vector<Field>* rootFields) {
+    if (matcher.position() == Position::FIRST) {
+        Field first_field = field;
+        setPositionForLeaf(&first_field, 0);
+        findNonRepeatedFields(fieldValueMap, matcher, first_field, rootFields);
+    } else {
+        auto itLower = fieldValueMap.lower_bound(field);
+        if (itLower == fieldValueMap.end()) {
+            return;
+        }
+        Field next_field = field;
+        getNextField(&next_field);
+        auto itUpper = fieldValueMap.lower_bound(next_field);
+
+        switch (matcher.position()) {
+             case Position::LAST:
+                 {
+                     itUpper--;
+                     if (itUpper != fieldValueMap.end()) {
+                         Field last_field = field;
+                         int last_index = getPositionByReferenceField(field, itUpper->first);
+                         if (last_index < 0) {
+                            return;
+                         }
+                         setPositionForLeaf(&last_field, last_index);
+                         findNonRepeatedFields(
+                            fieldValueMap, matcher, last_field, rootFields);
+                     }
+                 }
+                 break;
+             case Position::ANY:
+                 {
+                    std::set<int> indexes;
+                    for (auto it = itLower; it != itUpper; ++it) {
+                        int index = getPositionByReferenceField(field, it->first);
+                        if (index >= 0) {
+                            indexes.insert(index);
+                        }
+                    }
+                    if (!indexes.empty()) {
+                        Field any_field = field;
+                        for (const int index : indexes) {
+                             setPositionForLeaf(&any_field, index);
+                             findNonRepeatedFields(
+                                fieldValueMap, matcher, any_field, rootFields);
+                        }
+                    }
+                 }
+                 break;
+             default:
+                break;
+         }
+    }
+}
+
+void findFields(
+       const FieldValueMap& fieldValueMap,
+       const FieldMatcher& matcher,
+       const Field& field,
+       std::vector<Field>* rootFields) {
+    if (!matcher.has_position()) {
+        findNonRepeatedFields(fieldValueMap, matcher, field, rootFields);
+    } else {
+        findRepeatedFields(fieldValueMap, matcher, field, rootFields);
+    }
+}
+
+void filterFields(const FieldMatcher& matcher, FieldValueMap* fieldValueMap) {
+    std::vector<Field> rootFields;
+    findFields(*fieldValueMap, matcher, buildSimpleAtomField(matcher.field()), &rootFields);
+    std::set<Field, FieldCmp> rootFieldSet(rootFields.begin(), rootFields.end());
+    auto it = fieldValueMap->begin();
+    while (it != fieldValueMap->end()) {
+        if (rootFieldSet.find(it->first) == rootFieldSet.end()) {
+            it = fieldValueMap->erase(it);
+        } else {
+            it++;
+        }
+    }
+}
+
+bool hasLeafNode(const FieldMatcher& matcher) {
+    if (!matcher.has_field()) {
+        return false;
+    }
+    for (int i = 0; i < matcher.child_size(); ++i) {
+        if (hasLeafNode(matcher.child(i))) {
+            return true;
+        }
+    }
+    return true;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/field_util.h b/cmds/statsd/src/field_util.h
new file mode 100644
index 0000000..5907e17
--- /dev/null
+++ b/cmds/statsd/src/field_util.h
@@ -0,0 +1,89 @@
+/*
+ * 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 "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+
+#include <unordered_map>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Function to sort the Field protos.
+bool CompareField(const Field& a, const Field& b);
+struct FieldCmp {
+    bool operator()(const Field& a, const Field& b) const {
+        return CompareField(a, b);
+    }
+};
+
+// Flattened dimensions value map. To save space, usually the key contains the tree structure info
+// and value field is only leaf node.
+typedef std::map<Field, DimensionsValue, FieldCmp> FieldValueMap;
+
+// Util function to print the Field proto.
+std::string FieldToString(const Field& field);
+
+// Util function to find the leaf node from the input Field proto and set it in the corresponding
+// value proto.
+bool setFieldInLeafValueProto(const Field &field, DimensionsValue* leafValue);
+
+// Returns the leaf node from the Field proto. It assume that the input has only one
+// leaf node at most.
+const Field* getSingleLeaf(const Field* field);
+Field* getSingleLeaf(Field* field);
+
+// Append a node to the current leaf. It assumes that the input "parent" has one leaf node at most.
+void appendLeaf(Field *parent, int node_field_num);
+void appendLeaf(Field *parent, int node_field_num, int position);
+
+// Given the field sorting logic, this function is to increase the "field" at the leaf node.
+void getNextField(Field* field);
+
+// Increase the position index for the node. If the "position_index" is not set, set it as 0.
+void increasePosition(Field *field);
+
+// Finds the leaf node and set the index there.
+void setPositionForLeaf(Field *field, int index);
+
+// Returns true if the matcher has specified at least one leaf node.
+bool hasLeafNode(const FieldMatcher& matcher);
+
+// The two input Field proto are describing the same tree structure. Both contain one leaf node at
+// most. This is find the position index info for the leaf node at "reference" stored in the
+// "field_with_index" tree.
+int getPositionByReferenceField(const Field& reference, const Field& field_with_index);
+
+// Utils to build the Field proto for simple atom fields.
+Field buildAtomField(const int tagId, const Field &atomField);
+Field buildSimpleAtomField(const int tagId, const int atomFieldNum);
+Field buildSimpleAtomField(const int tagId);
+
+// Find out all the fields specified by the matcher.
+void findFields(
+       const FieldValueMap& fieldValueMap,
+       const FieldMatcher& matcher,
+       std::vector<Field>* rootFields);
+
+// Filter out the fields not in the field matcher.
+void filterFields(const FieldMatcher& matcher, FieldValueMap* fieldValueMap);
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index d660b5f..49a6e33 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -17,8 +17,14 @@
 #define DEBUG true  // STOPSHIP if true
 #include "logd/LogEvent.h"
 
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+
+#include <set>
 #include <sstream>
-#include "stats_util.h"
+
+#include "field_util.h"
+#include "dimension.h"
+#include "stats_log_util.h"
 
 namespace android {
 namespace os {
@@ -30,16 +36,20 @@
 using android::util::ProtoOutputStream;
 
 LogEvent::LogEvent(log_msg& msg) {
-    mContext =
+    android_log_context context =
             create_android_log_parser(msg.msg() + sizeof(uint32_t), msg.len() - sizeof(uint32_t));
     mTimestampNs = msg.entry_v1.sec * NS_PER_SEC + msg.entry_v1.nsec;
     mLogUid = msg.entry_v4.uid;
-    init(mContext);
+    init(context);
+    if (context) {
+        android_log_destroy(&context);
+    }
 }
 
 LogEvent::LogEvent(int32_t tagId, uint64_t timestampNs) {
     mTimestampNs = timestampNs;
     mTagId = tagId;
+    mLogUid = 0;
     mContext = create_android_logger(1937006964); // the event tag shared by all stats logs
     if (mContext) {
         android_log_write_int32(mContext, tagId);
@@ -53,6 +63,14 @@
         // turns to reader mode
         mContext = create_android_log_parser(buffer, len);
         init(mContext);
+        // destroy the context to save memory.
+        android_log_destroy(&mContext);
+    }
+}
+
+LogEvent::~LogEvent() {
+    if (mContext) {
+        android_log_destroy(&mContext);
     }
 }
 
@@ -98,19 +116,72 @@
     return false;
 }
 
-LogEvent::~LogEvent() {
+bool LogEvent::write(const std::vector<AttributionNode>& nodes) {
     if (mContext) {
-        android_log_destroy(&mContext);
+         if (android_log_write_list_begin(mContext) < 0) {
+            return false;
+         }
+         for (size_t i = 0; i < nodes.size(); ++i) {
+             if (!write(nodes[i])) {
+                return false;
+             }
+         }
+         if (android_log_write_list_end(mContext) < 0) {
+            return false;
+         }
+         return true;
+    }
+    return false;
+}
+
+bool LogEvent::write(const AttributionNode& node) {
+    if (mContext) {
+         if (android_log_write_list_begin(mContext) < 0) {
+            return false;
+         }
+         if (android_log_write_int32(mContext, node.uid()) < 0) {
+            return false;
+         }
+         if (android_log_write_string8(mContext, node.tag().c_str()) < 0) {
+            return false;
+         }
+         if (android_log_write_int32(mContext, node.uid()) < 0) {
+            return false;
+         }
+         if (android_log_write_list_end(mContext) < 0) {
+            return false;
+         }
+         return true;
+    }
+    return false;
+}
+
+namespace {
+
+void increaseField(Field *field, bool is_child) {
+    if (is_child) {
+        if (field->child_size() <= 0) {
+            field->add_child();
+        }
+    } else {
+        field->clear_child();
+    }
+    Field* curr = is_child ? field->mutable_child(0) : field;
+    if (!curr->has_field()) {
+        curr->set_field(1);
+    } else {
+        curr->set_field(curr->field() + 1);
     }
 }
 
+}  // namespace
+
 /**
  * The elements of each log event are stored as a vector of android_log_list_elements.
  * The goal is to do as little preprocessing as possible, because we read a tiny fraction
  * of the elements that are written to the log.
  */
 void LogEvent::init(android_log_context context) {
-    mElements.clear();
     android_log_list_element elem;
     // TODO: The log is actually structured inside one list.  This is convenient
     // because we'll be able to use it to put the attribution (WorkSource) block first
@@ -118,24 +189,79 @@
     // list-related log elements and the order we get there is our index-keyed data
     // structure.
     int i = 0;
+
+    int seenListStart = 0;
+
+    Field field;
     do {
         elem = android_log_read_next(context);
         switch ((int)elem.type) {
             case EVENT_TYPE_INT:
-                // elem at [0] is EVENT_TYPE_LIST, [1] is the tag id. If we add WorkSource, it would
-                // be the list starting at [2].
+                // elem at [0] is EVENT_TYPE_LIST, [1] is the tag id.
                 if (i == 1) {
                     mTagId = elem.data.int32;
-                    break;
+                } else {
+                    increaseField(&field, seenListStart > 0/* is_child */);
+                    DimensionsValue dimensionsValue;
+                    dimensionsValue.set_value_int(elem.data.int32);
+                    setFieldInLeafValueProto(field, &dimensionsValue);
+                    mFieldValueMap.insert(
+                        std::make_pair(buildAtomField(mTagId, field), dimensionsValue));
                 }
+                break;
             case EVENT_TYPE_FLOAT:
+                {
+                    increaseField(&field, seenListStart > 0/* is_child */);
+                    DimensionsValue dimensionsValue;
+                    dimensionsValue.set_value_float(elem.data.float32);
+                    setFieldInLeafValueProto(field, &dimensionsValue);
+                    mFieldValueMap.insert(
+                        std::make_pair(buildAtomField(mTagId, field), dimensionsValue));
+                }
+                break;
             case EVENT_TYPE_STRING:
+                {
+                    increaseField(&field, seenListStart > 0/* is_child */);
+                    DimensionsValue dimensionsValue;
+                    dimensionsValue.set_value_str(string(elem.data.string, elem.len).c_str());
+                    setFieldInLeafValueProto(field, &dimensionsValue);
+                    mFieldValueMap.insert(
+                        std::make_pair(buildAtomField(mTagId, field), dimensionsValue));
+                }
+                break;
             case EVENT_TYPE_LONG:
-                mElements.push_back(elem);
+                {
+                    increaseField(&field, seenListStart > 0 /* is_child */);
+                    DimensionsValue dimensionsValue;
+                    dimensionsValue.set_value_long(elem.data.int64);
+                    setFieldInLeafValueProto(field, &dimensionsValue);
+                    mFieldValueMap.insert(
+                        std::make_pair(buildAtomField(mTagId, field), dimensionsValue));
+                }
                 break;
             case EVENT_TYPE_LIST:
+                if (i >= 1) {
+                    if (seenListStart > 0) {
+                       increasePosition(&field);
+                    } else {
+                        increaseField(&field, false /* is_child */);
+                    }
+                    seenListStart++;
+                    if (seenListStart >= 3) {
+                        ALOGE("Depth > 2. Not supported!");
+                        return;
+                    }
+                }
                 break;
             case EVENT_TYPE_LIST_STOP:
+                seenListStart--;
+                if (seenListStart == 0) {
+                    field.clear_position_index();
+                } else {
+                    if (field.child_size() > 0) {
+                       field.mutable_child(0)->clear_field();
+                    }
+                }
                 break;
             case EVENT_TYPE_UNKNOWN:
                 break;
@@ -147,142 +273,145 @@
 }
 
 int64_t LogEvent::GetLong(size_t key, status_t* err) const {
-    if (key < 1 || (key - 1)  >= mElements.size()) {
+    DimensionsValue value;
+    if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
         *err = BAD_INDEX;
         return 0;
     }
-    key--;
-    const android_log_list_element& elem = mElements[key];
-    if (elem.type == EVENT_TYPE_INT) {
-        return elem.data.int32;
-    } else if (elem.type == EVENT_TYPE_LONG) {
-        return elem.data.int64;
-    } else if (elem.type == EVENT_TYPE_FLOAT) {
-        return (int64_t)elem.data.float32;
-    } else {
-        *err = BAD_TYPE;
-        return 0;
+    const DimensionsValue* leafValue = getSingleLeafValue(&value);
+    switch (leafValue->value_case()) {
+        case DimensionsValue::ValueCase::kValueInt:
+            return (int64_t)leafValue->value_int();
+        case DimensionsValue::ValueCase::kValueLong:
+            return leafValue->value_long();
+        case DimensionsValue::ValueCase::kValueBool:
+            return leafValue->value_bool() ? 1 : 0;
+        case DimensionsValue::ValueCase::kValueFloat:
+            return (int64_t)leafValue->value_float();
+        case DimensionsValue::ValueCase::kValueTuple:
+        case DimensionsValue::ValueCase::kValueStr:
+        case DimensionsValue::ValueCase::VALUE_NOT_SET: {
+            *err = BAD_TYPE;
+            return 0;
+        }
     }
 }
 
 const char* LogEvent::GetString(size_t key, status_t* err) const {
-    if (key < 1 || (key - 1)  >= mElements.size()) {
+    DimensionsValue value;
+    if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
         *err = BAD_INDEX;
-        return NULL;
+        return 0;
     }
-    key--;
-    const android_log_list_element& elem = mElements[key];
-    if (elem.type != EVENT_TYPE_STRING) {
-        *err = BAD_TYPE;
-        return NULL;
+    const DimensionsValue* leafValue = getSingleLeafValue(&value);
+    switch (leafValue->value_case()) {
+        case DimensionsValue::ValueCase::kValueStr:
+            return leafValue->value_str().c_str();
+        case DimensionsValue::ValueCase::kValueInt:
+        case DimensionsValue::ValueCase::kValueLong:
+        case DimensionsValue::ValueCase::kValueBool:
+        case DimensionsValue::ValueCase::kValueFloat:
+        case DimensionsValue::ValueCase::kValueTuple:
+        case DimensionsValue::ValueCase::VALUE_NOT_SET: {
+            *err = BAD_TYPE;
+            return 0;
+        }
     }
-    // Need to add the '/0' at the end by specifying the length of the string.
-    return string(elem.data.string, elem.len).c_str();
 }
 
 bool LogEvent::GetBool(size_t key, status_t* err) const {
-    if (key < 1 || (key - 1)  >= mElements.size()) {
+    DimensionsValue value;
+    if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
         *err = BAD_INDEX;
         return 0;
     }
-    key--;
-    const android_log_list_element& elem = mElements[key];
-    if (elem.type == EVENT_TYPE_INT) {
-        return elem.data.int32 != 0;
-    } else if (elem.type == EVENT_TYPE_LONG) {
-        return elem.data.int64 != 0;
-    } else if (elem.type == EVENT_TYPE_FLOAT) {
-        return elem.data.float32 != 0;
-    } else {
-        *err = BAD_TYPE;
-        return 0;
+    const DimensionsValue* leafValue = getSingleLeafValue(&value);
+    switch (leafValue->value_case()) {
+        case DimensionsValue::ValueCase::kValueInt:
+            return leafValue->value_int() != 0;
+        case DimensionsValue::ValueCase::kValueLong:
+            return leafValue->value_long() != 0;
+        case DimensionsValue::ValueCase::kValueBool:
+            return leafValue->value_bool();
+        case DimensionsValue::ValueCase::kValueFloat:
+            return leafValue->value_float() != 0;
+        case DimensionsValue::ValueCase::kValueTuple:
+        case DimensionsValue::ValueCase::kValueStr:
+        case DimensionsValue::ValueCase::VALUE_NOT_SET: {
+            *err = BAD_TYPE;
+            return 0;
+        }
     }
 }
 
 float LogEvent::GetFloat(size_t key, status_t* err) const {
-    if (key < 1 || (key - 1)  >= mElements.size()) {
+    DimensionsValue value;
+    if (!GetSimpleAtomDimensionsValueProto(key, &value)) {
         *err = BAD_INDEX;
         return 0;
     }
-    key--;
-    const android_log_list_element& elem = mElements[key];
-    if (elem.type == EVENT_TYPE_INT) {
-        return (float)elem.data.int32;
-    } else if (elem.type == EVENT_TYPE_LONG) {
-        return (float)elem.data.int64;
-    } else if (elem.type == EVENT_TYPE_FLOAT) {
-        return elem.data.float32;
-    } else {
-        *err = BAD_TYPE;
-        return 0;
+    const DimensionsValue* leafValue = getSingleLeafValue(&value);
+    switch (leafValue->value_case()) {
+        case DimensionsValue::ValueCase::kValueInt:
+            return (float)leafValue->value_int();
+        case DimensionsValue::ValueCase::kValueLong:
+            return (float)leafValue->value_long();
+        case DimensionsValue::ValueCase::kValueBool:
+            return leafValue->value_bool() ? 1.0f : 0.0f;
+        case DimensionsValue::ValueCase::kValueFloat:
+            return leafValue->value_float();
+        case DimensionsValue::ValueCase::kValueTuple:
+        case DimensionsValue::ValueCase::kValueStr:
+        case DimensionsValue::ValueCase::VALUE_NOT_SET: {
+            *err = BAD_TYPE;
+            return 0;
+        }
     }
 }
 
-KeyValuePair LogEvent::GetKeyValueProto(size_t key) const {
-    KeyValuePair pair;
-    pair.set_key(key);
-    // If the value is not valid, return the KeyValuePair without assigning the value.
-    // Caller can detect the error by checking the enum for "one of" proto type.
-    if (key < 1 || (key - 1) >= mElements.size()) {
-        return pair;
-    }
-    key--;
+void LogEvent::GetAtomDimensionsValueProtos(const FieldMatcher& matcher,
+                                            std::vector<DimensionsValue> *dimensionsValues) const {
+    findDimensionsValues(mFieldValueMap, matcher, dimensionsValues);
+}
 
-    const android_log_list_element& elem = mElements[key];
-    if (elem.type == EVENT_TYPE_INT) {
-        pair.set_value_int(elem.data.int32);
-    } else if (elem.type == EVENT_TYPE_LONG) {
-        pair.set_value_long(elem.data.int64);
-    } else if (elem.type == EVENT_TYPE_STRING) {
-        pair.set_value_str(elem.data.string);
-    } else if (elem.type == EVENT_TYPE_FLOAT) {
-        pair.set_value_float(elem.data.float32);
+bool LogEvent::GetAtomDimensionsValueProto(const FieldMatcher& matcher,
+                                           DimensionsValue* dimensionsValue) const {
+    std::vector<DimensionsValue> rootDimensionsValues;
+    findDimensionsValues(mFieldValueMap, matcher, &rootDimensionsValues);
+    if (rootDimensionsValues.size() != 1) {
+        return false;
     }
-    return pair;
+    *dimensionsValue = rootDimensionsValues.front();
+    return true;
+}
+
+bool LogEvent::GetSimpleAtomDimensionsValueProto(size_t atomField,
+                                                 DimensionsValue* dimensionsValue) const {
+    return GetAtomDimensionsValueProto(
+        buildSimpleAtomFieldMatcher(mTagId, atomField), dimensionsValue);
+}
+
+DimensionsValue LogEvent::GetSimpleAtomDimensionsValueProto(size_t atomField)  const {
+    DimensionsValue dimensionsValue;
+    GetSimpleAtomDimensionsValueProto(atomField, &dimensionsValue);
+    return dimensionsValue;
 }
 
 string LogEvent::ToString() const {
     ostringstream result;
     result << "{ " << mTimestampNs << " (" << mTagId << ")";
-    const size_t N = mElements.size();
-    for (size_t i=0; i<N; i++) {
-        result << " ";
-        result << (i + 1);
+    for (const auto& itr : mFieldValueMap) {
+        result << FieldToString(itr.first);
         result << "->";
-        const android_log_list_element& elem = mElements[i];
-        if (elem.type == EVENT_TYPE_INT) {
-            result << elem.data.int32;
-        } else if (elem.type == EVENT_TYPE_LONG) {
-            result << elem.data.int64;
-        } else if (elem.type == EVENT_TYPE_FLOAT) {
-            result << elem.data.float32;
-        } else if (elem.type == EVENT_TYPE_STRING) {
-            // Need to add the '/0' at the end by specifying the length of the string.
-            result << string(elem.data.string, elem.len).c_str();
-        }
+        result << DimensionsValueToString(itr.second);
+        result << " ";
     }
     result << " }";
     return result.str();
 }
 
-void LogEvent::ToProto(ProtoOutputStream& proto) const {
-    long long atomToken = proto.start(FIELD_TYPE_MESSAGE | mTagId);
-    const size_t N = mElements.size();
-    for (size_t i=0; i<N; i++) {
-        const int key = i + 1;
-
-        const android_log_list_element& elem = mElements[i];
-        if (elem.type == EVENT_TYPE_INT) {
-            proto.write(FIELD_TYPE_INT32 | key, elem.data.int32);
-        } else if (elem.type == EVENT_TYPE_LONG) {
-            proto.write(FIELD_TYPE_INT64 | key, (long long)elem.data.int64);
-        } else if (elem.type == EVENT_TYPE_FLOAT) {
-            proto.write(FIELD_TYPE_FLOAT | key, elem.data.float32);
-        } else if (elem.type == EVENT_TYPE_STRING) {
-            proto.write(FIELD_TYPE_STRING | key, elem.data.string);
-        }
-    }
-    proto.end(atomToken);
+void LogEvent::ToProto(ProtoOutputStream& protoOutput) const {
+    writeFieldValueTreeToStream(getFieldValueMap(), &protoOutput);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index d3f38de..8f3dedf 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -16,6 +16,7 @@
 
 #pragma once
 
+#include "field_util.h"
 #include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 
 #include <android/util/ProtoOutputStream.h>
@@ -23,9 +24,11 @@
 #include <log/log_read.h>
 #include <private/android_logger.h>
 #include <utils/Errors.h>
+#include <utils/JenkinsHash.h>
 
 #include <memory>
 #include <string>
+#include <map>
 #include <vector>
 
 namespace android {
@@ -77,6 +80,20 @@
     bool GetBool(size_t key, status_t* err) const;
     float GetFloat(size_t key, status_t* err) const;
 
+    /*
+     * Get DimensionsValue proto objects from FieldMatcher.
+     */
+    void GetAtomDimensionsValueProtos(
+        const FieldMatcher& matcher, std::vector<DimensionsValue> *dimensionsValues) const;
+    bool GetAtomDimensionsValueProto(
+        const FieldMatcher& matcher, DimensionsValue* dimensionsValue) const;
+
+    /*
+     * Get a DimensionsValue proto objects from Field.
+     */
+    bool GetSimpleAtomDimensionsValueProto(size_t field, DimensionsValue* dimensionsValue) const;
+    DimensionsValue  GetSimpleAtomDimensionsValueProto(size_t atomField)  const;
+
     /**
      * Write test data to the LogEvent. This can only be used when the LogEvent is constructed
      * using LogEvent(tagId, timestampNs). You need to call init() before you can read from it.
@@ -87,6 +104,8 @@
     bool write(int64_t value);
     bool write(const string& value);
     bool write(float value);
+    bool write(const std::vector<AttributionNode>& nodes);
+    bool write(const AttributionNode& node);
 
     /**
      * Return a string representation of this event.
@@ -98,11 +117,6 @@
      */
     void ToProto(android::util::ProtoOutputStream& out) const;
 
-    /*
-     * Get a KeyValuePair proto object.
-     */
-    KeyValuePair GetKeyValueProto(size_t key) const;
-
     /**
      * Used with the constructor where tag is passed in. Converts the log_event_list to read mode
      * and prepares the list for reading.
@@ -114,10 +128,12 @@
      */
     void setTimestampNs(uint64_t timestampNs) {mTimestampNs = timestampNs;}
 
-    int size() const {
-        return mElements.size();
+    inline int size() const {
+        return mFieldValueMap.size();
     }
 
+    inline const FieldValueMap& getFieldValueMap() const { return mFieldValueMap; }
+
 private:
     /**
      * Don't copy, it's slower. If we really need this we can add it but let's try to
@@ -130,8 +146,11 @@
      */
     void init(android_log_context context);
 
-    vector<android_log_list_element> mElements;
+    FieldValueMap mFieldValueMap;
 
+    // This field is used when statsD wants to create log event object and write fields to it. After
+    // calling init() function, this object would be destroyed to save memory usage.
+    // When the log event is created from log msg, this field is never initiated.
     android_log_context mContext;
 
     uint64_t mTimestampNs;
diff --git a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
index 51a38b6..bd5e3cb 100644
--- a/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
+++ b/cmds/statsd/src/matchers/CombinationLogMatchingTracker.cpp
@@ -82,8 +82,8 @@
 
         mChildren.push_back(childIndex);
 
-        const set<int>& childTagIds = allTrackers[childIndex]->getTagIds();
-        mTagIds.insert(childTagIds.begin(), childTagIds.end());
+        const set<int>& childTagIds = allTrackers[childIndex]->getAtomIds();
+        mAtomIds.insert(childTagIds.begin(), childTagIds.end());
     }
 
     mInitialized = true;
@@ -100,7 +100,7 @@
         return;
     }
 
-    if (mTagIds.find(event.GetTagId()) == mTagIds.end()) {
+    if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) {
         matcherResults[mIndex] = MatchingState::kNotMatched;
         return;
     }
diff --git a/cmds/statsd/src/matchers/LogMatchingTracker.h b/cmds/statsd/src/matchers/LogMatchingTracker.h
index 8162c44..567944f 100644
--- a/cmds/statsd/src/matchers/LogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/LogMatchingTracker.h
@@ -65,8 +65,8 @@
     // Get the tagIds that this matcher cares about. The combined collection is stored
     // in MetricMananger, so that we can pass any LogEvents that are not interest of us. It uses
     // some memory but hopefully it can save us much CPU time when there is flood of events.
-    virtual const std::set<int>& getTagIds() const {
-        return mTagIds;
+    virtual const std::set<int>& getAtomIds() const {
+        return mAtomIds;
     }
 
     const std::string& getName() const {
@@ -88,7 +88,7 @@
     // useful when we have a complex CombinationLogMatcherTracker.
     // TODO: Consider use an array instead of stl set. In reality, the number of the tag ids a
     // LogMatchingTracker cares is only a few.
-    std::set<int> mTagIds;
+    std::set<int> mAtomIds;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
index ac217ab..91ef034 100644
--- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.cpp
@@ -30,12 +30,13 @@
 
 
 SimpleLogMatchingTracker::SimpleLogMatchingTracker(const string& name, const int index,
-                                                   const SimpleAtomMatcher& matcher)
-    : LogMatchingTracker(name, index), mMatcher(matcher) {
-    if (!matcher.has_tag()) {
+                                                   const SimpleAtomMatcher& matcher,
+                                                   const UidMap& uidMap)
+    : LogMatchingTracker(name, index), mMatcher(matcher), mUidMap(uidMap) {
+    if (!matcher.has_atom_id()) {
         mInitialized = false;
     } else {
-        mTagIds.insert(matcher.tag());
+        mAtomIds.insert(matcher.atom_id());
         mInitialized = true;
     }
 }
@@ -59,12 +60,12 @@
         return;
     }
 
-    if (mTagIds.find(event.GetTagId()) == mTagIds.end()) {
+    if (mAtomIds.find(event.GetTagId()) == mAtomIds.end()) {
         matcherResults[mIndex] = MatchingState::kNotMatched;
         return;
     }
 
-    bool matched = matchesSimple(mMatcher, event);
+    bool matched = matchesSimple(mUidMap, mMatcher, event);
     matcherResults[mIndex] = matched ? MatchingState::kMatched : MatchingState::kNotMatched;
     VLOG("Stats SimpleLogMatcher %s matched? %d", mName.c_str(), matched);
 }
diff --git a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
index 2c188c1..68518f8 100644
--- a/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
+++ b/cmds/statsd/src/matchers/SimpleLogMatchingTracker.h
@@ -24,6 +24,7 @@
 #include <vector>
 #include "LogMatchingTracker.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "packages/UidMap.h"
 
 namespace android {
 namespace os {
@@ -32,7 +33,8 @@
 class SimpleLogMatchingTracker : public virtual LogMatchingTracker {
 public:
     SimpleLogMatchingTracker(const std::string& name, const int index,
-                             const SimpleAtomMatcher& matcher);
+                             const SimpleAtomMatcher& matcher,
+                             const UidMap& uidMap);
 
     ~SimpleLogMatchingTracker();
 
@@ -47,6 +49,7 @@
 
 private:
     const SimpleAtomMatcher mMatcher;
+    const UidMap& mUidMap;
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/matchers/matcher_util.cpp b/cmds/statsd/src/matchers/matcher_util.cpp
index 9e88e5d0..46d9b92 100644
--- a/cmds/statsd/src/matchers/matcher_util.cpp
+++ b/cmds/statsd/src/matchers/matcher_util.cpp
@@ -19,7 +19,9 @@
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "matchers/LogMatchingTracker.h"
 #include "matchers/matcher_util.h"
+#include "dimension.h"
 #include "stats_util.h"
+#include "field_util.h"
 
 #include <log/event_tag_map.h>
 #include <log/log_event_list.h>
@@ -91,127 +93,173 @@
     return matched;
 }
 
-bool matchesSimple(const SimpleAtomMatcher& simpleMatcher, const LogEvent& event) {
-    const int tagId = event.GetTagId();
+bool IsAttributionUidField(const Field& field) {
+    return field.child_size() == 1 && field.child(0).field() == 1
+        && field.child(0).child_size() == 1 && field.child(0).child(0).field() == 1;
+}
 
-    if (simpleMatcher.tag() != tagId) {
-        return false;
-    }
-    // now see if this event is interesting to us -- matches ALL the matchers
-    // defined in the metrics.
-    bool allMatched = true;
-    for (int j = 0; allMatched && j < simpleMatcher.key_value_matcher_size(); j++) {
-        auto cur = simpleMatcher.key_value_matcher(j);
-
-        // TODO: Check if this key is a magic key (eg package name).
-        // TODO: Maybe make packages a different type in the config?
-        int key = cur.key_matcher().key();
-
-        const KeyValueMatcher::ValueMatcherCase matcherCase = cur.value_matcher_case();
-        if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqString) {
-            // String fields
-            status_t err = NO_ERROR;
-            const char* val = event.GetString(key, &err);
-            if (err == NO_ERROR && val != NULL) {
-                if (!(cur.eq_string() == val)) {
-                    allMatched = false;
-                    break;
-                }
-            } else {
-                allMatched = false;
-                break;
-            }
-        } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqInt ||
-                   matcherCase == KeyValueMatcher::ValueMatcherCase::kLtInt ||
-                   matcherCase == KeyValueMatcher::ValueMatcherCase::kGtInt ||
-                   matcherCase == KeyValueMatcher::ValueMatcherCase::kLteInt ||
-                   matcherCase == KeyValueMatcher::ValueMatcherCase::kGteInt) {
-            // Integer fields
-            status_t err = NO_ERROR;
-            int64_t val = event.GetLong(key, &err);
-            if (err == NO_ERROR) {
-                if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqInt) {
-                    if (!(val == cur.eq_int())) {
-                        allMatched = false;
-                        break;
-                    }
-                } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLtInt) {
-                    if (!(val < cur.lt_int())) {
-                        allMatched = false;
-                        break;
-                    }
-                } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kGtInt) {
-                    if (!(val > cur.gt_int())) {
-                        allMatched = false;
-                        break;
-                    }
-                } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLteInt) {
-                    if (!(val <= cur.lte_int())) {
-                        allMatched = false;
-                        break;
-                    }
-                } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kGteInt) {
-                    if (!(val >= cur.gte_int())) {
-                        allMatched = false;
-                        break;
-                    }
-                }
-            } else {
-                allMatched = false;
-                break;
-            }
-        } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kEqBool) {
-            // Boolean fields
-            status_t err = NO_ERROR;
-            bool val = event.GetBool(key, &err);
-            if (err == NO_ERROR) {
-                if (!(cur.eq_bool() == val)) {
-                    allMatched = false;
-                    break;
-                }
-            } else {
-                allMatched = false;
-                break;
-            }
-        } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLtFloat ||
-                   matcherCase == KeyValueMatcher::ValueMatcherCase::kGtFloat) {
-            // Float fields
-            status_t err = NO_ERROR;
-            float val = event.GetFloat(key, &err);
-            if (err == NO_ERROR) {
-                if (matcherCase == KeyValueMatcher::ValueMatcherCase::kLtFloat) {
-                    if (!(val < cur.lt_float())) {
-                        allMatched = false;
-                        break;
-                    }
-                } else if (matcherCase == KeyValueMatcher::ValueMatcherCase::kGtFloat) {
-                    if (!(val > cur.gt_float())) {
-                        allMatched = false;
-                        break;
-                    }
-                }
-            } else {
-                allMatched = false;
-                break;
-            }
-        } else {
-            // If value matcher is not present, assume that we match.
+bool matchesNonRepeatedField(
+       const UidMap& uidMap,
+       const FieldValueMap& fieldMap,
+       const FieldValueMatcher&matcher,
+       const Field& field) {
+    if (matcher.value_matcher_case() ==
+            FieldValueMatcher::ValueMatcherCase::VALUE_MATCHER_NOT_SET) {
+        return !fieldMap.empty() && fieldMap.begin()->first.field() == matcher.field();
+    } else if (matcher.value_matcher_case() == FieldValueMatcher::ValueMatcherCase::kMatchesTuple) {
+        bool allMatched = true;
+        for (int i = 0; allMatched && i <  matcher.matches_tuple().field_value_matcher_size(); ++i) {
+            const auto& childMatcher = matcher.matches_tuple().field_value_matcher(i);
+            Field childField = field;
+            appendLeaf(&childField, childMatcher.field());
+            allMatched &= matchFieldSimple(uidMap, fieldMap, childMatcher, childField);
         }
+        return allMatched;
+    } else {
+        auto ret = fieldMap.equal_range(field);
+        int found = 0;
+        for (auto it = ret.first; it != ret.second; ++it) {
+            found++;
+        }
+        // Not found.
+        if (found <= 0) {
+            return false;
+        }
+        if (found > 1) {
+            ALOGE("Found multiple values for optional field.");
+            return false;
+        }
+        bool matched = false;
+        switch (matcher.value_matcher_case()) {
+            case FieldValueMatcher::ValueMatcherCase::kEqBool:
+                 // Logd does not support bool, it is int instead.
+                 matched = ((ret.first->second.value_int() > 0) == matcher.eq_bool());
+                 break;
+            case FieldValueMatcher::ValueMatcherCase::kEqString:
+                 {
+                    if (IsAttributionUidField(field)) {
+                        const int uid = ret.first->second.value_int();
+                        std::set<string> packageNames =
+                            uidMap.getAppNamesFromUid(uid, true /* normalize*/);
+                        matched = packageNames.find(matcher.eq_string()) != packageNames.end();
+                    } else {
+                        matched = (ret.first->second.value_str() == matcher.eq_string());
+                    }
+                 }
+                 break;
+            case FieldValueMatcher::ValueMatcherCase::kEqInt:
+                 matched = (ret.first->second.value_int() == matcher.eq_int());
+                 break;
+            case FieldValueMatcher::ValueMatcherCase::kLtInt:
+                 matched = (ret.first->second.value_int() < matcher.lt_int());
+                 break;
+            case FieldValueMatcher::ValueMatcherCase::kGtInt:
+                 matched = (ret.first->second.value_int() > matcher.gt_int());
+                 break;
+            case FieldValueMatcher::ValueMatcherCase::kLtFloat:
+                 matched = (ret.first->second.value_float() < matcher.lt_float());
+                 break;
+            case FieldValueMatcher::ValueMatcherCase::kGtFloat:
+                 matched = (ret.first->second.value_float() > matcher.gt_float());
+                 break;
+            case FieldValueMatcher::ValueMatcherCase::kLteInt:
+                 matched = (ret.first->second.value_int() <= matcher.lte_int());
+                 break;
+            case FieldValueMatcher::ValueMatcherCase::kGteInt:
+                 matched = (ret.first->second.value_int() >= matcher.gte_int());
+                 break;
+            default:
+                break;
+        }
+        return matched;
     }
-    return allMatched;
 }
 
-vector<KeyValuePair> getDimensionKey(const LogEvent& event,
-                                     const std::vector<KeyMatcher>& dimensions) {
-    vector<KeyValuePair> key;
-    key.reserve(dimensions.size());
-    for (const KeyMatcher& dimension : dimensions) {
-        KeyValuePair k = event.GetKeyValueProto(dimension.key());
-        key.push_back(k);
+bool matchesRepeatedField(const UidMap& uidMap, const FieldValueMap& fieldMap,
+                          const FieldValueMatcher&matcher, const Field& field) {
+    if (matcher.position() == Position::FIRST) {
+        Field first_field = field;
+        setPositionForLeaf(&first_field, 0);
+        return matchesNonRepeatedField(uidMap, fieldMap, matcher, first_field);
+    } else {
+        auto itLower = fieldMap.lower_bound(field);
+        if (itLower == fieldMap.end()) {
+            return false;
+        }
+        Field next_field = field;
+        getNextField(&next_field);
+        auto itUpper = fieldMap.lower_bound(next_field);
+        switch (matcher.position()) {
+             case Position::LAST:
+                 {
+                     itUpper--;
+                     if (itUpper == fieldMap.end()) {
+                        return false;
+                     } else {
+                         Field last_field = field;
+                         int last_index = getPositionByReferenceField(field, itUpper->first);
+                         if (last_index < 0) {
+                            return false;
+                         }
+                         setPositionForLeaf(&last_field, last_index);
+                         return matchesNonRepeatedField(uidMap, fieldMap, matcher, last_field);
+                     }
+                 }
+                 break;
+             case Position::ANY:
+                 {
+                    std::set<int> indexes;
+                    for (auto it = itLower; it != itUpper; ++it) {
+                        int index = getPositionByReferenceField(field, it->first);
+                        if (index >= 0) {
+                            indexes.insert(index);
+                        }
+                    }
+                    bool matched = false;
+                    for (const int index : indexes) {
+                         Field any_field = field;
+                         setPositionForLeaf(&any_field, index);
+                         matched |= matchesNonRepeatedField(uidMap, fieldMap, matcher, any_field);
+                    }
+                    return matched;
+                 }
+             default:
+                return false;
+         }
     }
-    return key;
+
 }
 
+bool matchFieldSimple(const UidMap& uidMap, const FieldValueMap& fieldMap,
+                      const FieldValueMatcher&matcher, const Field& field) {
+    if (!matcher.has_position()) {
+        return matchesNonRepeatedField(uidMap, fieldMap, matcher, field);
+    } else {
+        return matchesRepeatedField(uidMap, fieldMap, matcher, field);
+    }
+}
+
+bool matchesSimple(const UidMap& uidMap, const SimpleAtomMatcher& simpleMatcher,
+                   const LogEvent& event) {
+    if (simpleMatcher.field_value_matcher_size() <= 0) {
+        return event.GetTagId() == simpleMatcher.atom_id();
+    }
+    Field root_field;
+    root_field.set_field(simpleMatcher.atom_id());
+    FieldValueMatcher root_field_matcher;
+    root_field_matcher.set_field(simpleMatcher.atom_id());
+    for (int i = 0; i < simpleMatcher.field_value_matcher_size(); i++) {
+        *root_field_matcher.mutable_matches_tuple()->add_field_value_matcher() =
+            simpleMatcher.field_value_matcher(i);
+    }
+    return matchFieldSimple(uidMap, event.getFieldValueMap(), root_field_matcher, root_field);
+}
+
+vector<DimensionsValue> getDimensionKeys(const LogEvent& event, const FieldMatcher& matcher) {
+    vector<DimensionsValue> values;
+    findDimensionsValues(event.getFieldValueMap(), matcher, &values);
+    return values;
+}
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/matchers/matcher_util.h b/cmds/statsd/src/matchers/matcher_util.h
index f54ab36..704cb4c 100644
--- a/cmds/statsd/src/matchers/matcher_util.h
+++ b/cmds/statsd/src/matchers/matcher_util.h
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef MATCHER_UTIL_H
-#define MATCHER_UTIL_H
+#pragma once
 
 #include "logd/LogEvent.h"
 
@@ -28,6 +27,7 @@
 #include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "stats_util.h"
+#include "packages/UidMap.h"
 
 namespace android {
 namespace os {
@@ -42,12 +42,14 @@
 bool combinationMatch(const std::vector<int>& children, const LogicalOperation& operation,
                       const std::vector<MatchingState>& matcherResults);
 
-bool matchesSimple(const SimpleAtomMatcher& simpleMatcher, const LogEvent& wrapper);
+bool matchFieldSimple(const UidMap& uidMap, const FieldValueMap& dimensionsMap,
+                      const FieldValueMatcher& matcher, const Field& field);
 
-std::vector<KeyValuePair> getDimensionKey(const LogEvent& event,
-                                          const std::vector<KeyMatcher>& dimensions);
+bool matchesSimple(const UidMap& uidMap,
+    const SimpleAtomMatcher& simpleMatcher, const LogEvent& wrapper);
+
+std::vector<DimensionsValue> getDimensionKeys(const LogEvent& event, const FieldMatcher& matcher);
 
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
-#endif  // MATCHER_UTIL_H
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.cpp b/cmds/statsd/src/metrics/CountMetricProducer.cpp
index 9031ed0..5bf3cff 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/CountMetricProducer.cpp
@@ -20,6 +20,7 @@
 #include "CountMetricProducer.h"
 #include "guardrail/StatsdStats.h"
 #include "stats_util.h"
+#include "stats_log_util.h"
 
 #include <limits.h>
 #include <stdlib.h>
@@ -51,12 +52,6 @@
 // for CountMetricData
 const int FIELD_ID_DIMENSION = 1;
 const int FIELD_ID_BUCKET_INFO = 2;
-// for KeyValuePair
-const int FIELD_ID_KEY = 1;
-const int FIELD_ID_VALUE_STR = 2;
-const int FIELD_ID_VALUE_INT = 3;
-const int FIELD_ID_VALUE_BOOL = 4;
-const int FIELD_ID_VALUE_FLOAT = 5;
 // for CountBucketInfo
 const int FIELD_ID_START_BUCKET_NANOS = 1;
 const int FIELD_ID_END_BUCKET_NANOS = 2;
@@ -75,7 +70,7 @@
     }
 
     // TODO: use UidMap if uid->pkg_name is required
-    mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
+    mDimensions = metric.dimensions();
 
     if (metric.links().size() > 0) {
         mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
@@ -95,6 +90,24 @@
     VLOG("Metric %s onSlicedConditionMayChange", mName.c_str());
 }
 
+void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
+    flushIfNeededLocked(dumpTimeNs);
+    report->set_metric_name(mName);
+    report->set_start_report_nanos(mStartTimeNs);
+
+    auto count_metrics = report->mutable_count_metrics();
+    for (const auto& counter : mPastBuckets) {
+        CountMetricData* metricData = count_metrics->add_data();
+        *metricData->mutable_dimension() = counter.first.getDimensionsValue();
+        for (const auto& bucket : counter.second) {
+            CountBucketInfo* bucketInfo = metricData->add_bucket_info();
+            bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
+            bucketInfo->set_end_bucket_nanos(bucket.mBucketEndNs);
+            bucketInfo->set_count(bucket.mCount);
+        }
+    }
+}
+
 void CountMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
                                              ProtoOutputStream* protoOutput) {
     flushIfNeededLocked(dumpTimeNs);
@@ -107,28 +120,16 @@
 
     for (const auto& counter : mPastBuckets) {
         const HashableDimensionKey& hashableKey = counter.first;
-        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 : 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());
-            if (kv.has_value_str()) {
-                protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str());
-            } else if (kv.has_value_int()) {
-                protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
-            } else if (kv.has_value_bool()) {
-                protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
-            } else if (kv.has_value_float()) {
-                protoOutput->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
-            }
-            protoOutput->end(dimensionToken);
-        }
+        // First fill dimension.
+        long long dimensionToken = protoOutput->start(
+                FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
+        writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+        protoOutput->end(dimensionToken);
 
         // Then fill bucket_info (CountBucketInfo).
         for (const auto& bucket : counter.second) {
@@ -183,7 +184,7 @@
 
 void CountMetricProducer::onMatchedLogEventInternalLocked(
         const size_t matcherIndex, const HashableDimensionKey& eventKey,
-        const map<string, HashableDimensionKey>& conditionKey, bool condition,
+        const ConditionKey& conditionKey, bool condition,
         const LogEvent& event) {
     uint64_t eventTimeNs = event.GetTimestampNs();
 
diff --git a/cmds/statsd/src/metrics/CountMetricProducer.h b/cmds/statsd/src/metrics/CountMetricProducer.h
index e32fc06..6087ae5 100644
--- a/cmds/statsd/src/metrics/CountMetricProducer.h
+++ b/cmds/statsd/src/metrics/CountMetricProducer.h
@@ -51,12 +51,13 @@
 protected:
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const HashableDimensionKey& eventKey,
-            const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
+            const ConditionKey& conditionKey, bool condition,
             const LogEvent& event) override;
 
 private:
     void onDumpReportLocked(const uint64_t dumpTimeNs,
                             android::util::ProtoOutputStream* protoOutput) override;
+    void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override;
 
     // Internal interface to handle condition change.
     void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.cpp b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
index 51ea4b5..30b105c 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.cpp
@@ -20,6 +20,7 @@
 #include "DurationMetricProducer.h"
 #include "guardrail/StatsdStats.h"
 #include "stats_util.h"
+#include "stats_log_util.h"
 
 #include <limits.h>
 #include <stdlib.h>
@@ -50,12 +51,6 @@
 // for DurationMetricData
 const int FIELD_ID_DIMENSION = 1;
 const int FIELD_ID_BUCKET_INFO = 2;
-// for KeyValuePair
-const int FIELD_ID_KEY = 1;
-const int FIELD_ID_VALUE_STR = 2;
-const int FIELD_ID_VALUE_INT = 3;
-const int FIELD_ID_VALUE_BOOL = 4;
-const int FIELD_ID_VALUE_FLOAT = 5;
 // for DurationBucketInfo
 const int FIELD_ID_START_BUCKET_NANOS = 1;
 const int FIELD_ID_END_BUCKET_NANOS = 2;
@@ -66,7 +61,7 @@
                                                const size_t stopIndex, const size_t stopAllIndex,
                                                const bool nesting,
                                                const sp<ConditionWizard>& wizard,
-                                               const vector<KeyMatcher>& internalDimension,
+                                               const FieldMatcher& internalDimensions,
                                                const uint64_t startTimeNs)
     : MetricProducer(metric.name(), key, startTimeNs, conditionIndex, wizard),
       mAggregationType(metric.aggregation_type()),
@@ -74,7 +69,7 @@
       mStopIndex(stopIndex),
       mStopAllIndex(stopAllIndex),
       mNested(nesting),
-      mInternalDimension(internalDimension) {
+      mInternalDimensions(internalDimensions) {
     // TODO: The following boiler plate code appears in all MetricProducers, but we can't abstract
     // them in the base class, because the proto generated CountMetric, and DurationMetric are
     // not related. Maybe we should add a template in the future??
@@ -85,7 +80,7 @@
     }
 
     // TODO: use UidMap if uid->pkg_name is required
-    mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
+    mDimensions = metric.dimensions();
 
     if (metric.links().size() > 0) {
         mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
@@ -151,6 +146,24 @@
     }
 }
 
+void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
+    flushIfNeededLocked(dumpTimeNs);
+    report->set_metric_name(mName);
+    report->set_start_report_nanos(mStartTimeNs);
+
+    auto duration_metrics = report->mutable_duration_metrics();
+    for (const auto& pair : mPastBuckets) {
+        DurationMetricData* metricData = duration_metrics->add_data();
+        *metricData->mutable_dimension() = pair.first.getDimensionsValue();
+        for (const auto& bucket : pair.second) {
+            auto bucketInfo = metricData->add_bucket_info();
+            bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
+            bucketInfo->set_end_bucket_nanos(bucket.mBucketEndNs);
+            bucketInfo->set_duration_nanos(bucket.mDuration);
+        }
+    }
+}
+
 void DurationMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
                                                 ProtoOutputStream* protoOutput) {
     flushIfNeededLocked(dumpTimeNs);
@@ -163,28 +176,16 @@
 
     for (const auto& pair : mPastBuckets) {
         const HashableDimensionKey& hashableKey = pair.first;
-        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 : 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());
-            if (kv.has_value_str()) {
-                protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str());
-            } else if (kv.has_value_int()) {
-                protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
-            } else if (kv.has_value_bool()) {
-                protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
-            } else if (kv.has_value_float()) {
-                protoOutput->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
-            }
-            protoOutput->end(dimensionToken);
-        }
+        // First fill dimension.
+        long long dimensionToken = protoOutput->start(
+                FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
+        writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+        protoOutput->end(dimensionToken);
 
         // Then fill bucket_info (DurationBucketInfo).
         for (const auto& bucket : pair.second) {
@@ -249,7 +250,7 @@
 
 void DurationMetricProducer::onMatchedLogEventInternalLocked(
         const size_t matcherIndex, const HashableDimensionKey& eventKey,
-        const map<string, HashableDimensionKey>& conditionKeys, bool condition,
+        const ConditionKey& conditionKeys, bool condition,
         const LogEvent& event) {
     flushIfNeededLocked(event.GetTimestampNs());
 
@@ -260,7 +261,6 @@
         return;
     }
 
-    HashableDimensionKey atomKey(getDimensionKey(event, mInternalDimension));
 
     if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
         if (hitGuardRailLocked(eventKey)) {
@@ -271,11 +271,25 @@
 
     auto it = mCurrentSlicedDuration.find(eventKey);
 
-    if (matcherIndex == mStartIndex) {
-        it->second->noteStart(atomKey, condition, event.GetTimestampNs(), conditionKeys);
-    } else if (matcherIndex == mStopIndex) {
-        it->second->noteStop(atomKey, event.GetTimestampNs(), false);
+    std::vector<DimensionsValue> values = getDimensionKeys(event, mInternalDimensions);
+    if (values.empty()) {
+        if (matcherIndex == mStartIndex) {
+            it->second->noteStart(DEFAULT_DIMENSION_KEY, condition,
+                                  event.GetTimestampNs(), conditionKeys);
+        } else if (matcherIndex == mStopIndex) {
+            it->second->noteStop(DEFAULT_DIMENSION_KEY, event.GetTimestampNs(), false);
+        }
+    } else {
+        for (const DimensionsValue& value : values) {
+            if (matcherIndex == mStartIndex) {
+                it->second->noteStart(HashableDimensionKey(value), condition,
+                                      event.GetTimestampNs(), conditionKeys);
+            } else if (matcherIndex == mStopIndex) {
+                it->second->noteStop(HashableDimensionKey(value), event.GetTimestampNs(), false);
+            }
+        }
     }
+
 }
 
 size_t DurationMetricProducer::byteSizeLocked() const {
diff --git a/cmds/statsd/src/metrics/DurationMetricProducer.h b/cmds/statsd/src/metrics/DurationMetricProducer.h
index abc89bd..e06b9a1 100644
--- a/cmds/statsd/src/metrics/DurationMetricProducer.h
+++ b/cmds/statsd/src/metrics/DurationMetricProducer.h
@@ -42,7 +42,7 @@
                            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);
+                           const FieldMatcher& internalDimensions, const uint64_t startTimeNs);
 
     virtual ~DurationMetricProducer();
 
@@ -51,12 +51,13 @@
 protected:
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const HashableDimensionKey& eventKey,
-            const std::map<std::string, HashableDimensionKey>& conditionKeys, bool condition,
+            const ConditionKey& conditionKeys, bool condition,
             const LogEvent& event) override;
 
 private:
     void onDumpReportLocked(const uint64_t dumpTimeNs,
                             android::util::ProtoOutputStream* protoOutput) override;
+    void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override;
 
     // Internal interface to handle condition change.
     void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
@@ -85,7 +86,7 @@
     const bool mNested;
 
     // The dimension from the atom predicate. e.g., uid, wakelock name.
-    const vector<KeyMatcher> mInternalDimension;
+    const FieldMatcher mInternalDimensions;
 
     // Save the past buckets and we can clear when the StatsLogReport is dumped.
     // TODO: Add a lock to mPastBuckets.
@@ -109,6 +110,7 @@
 
     FRIEND_TEST(DurationMetricTrackerTest, TestNoCondition);
     FRIEND_TEST(DurationMetricTrackerTest, TestNonSlicedCondition);
+    FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicates);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.cpp b/cmds/statsd/src/metrics/EventMetricProducer.cpp
index 6a072b0..c8138d3 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/EventMetricProducer.cpp
@@ -96,6 +96,10 @@
     return buffer;
 }
 
+void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
+
+}
+
 void EventMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
                                              ProtoOutputStream* protoOutput) {
     protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_NAME, mName);
@@ -121,7 +125,7 @@
 
 void EventMetricProducer::onMatchedLogEventInternalLocked(
         const size_t matcherIndex, const HashableDimensionKey& eventKey,
-        const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
+        const ConditionKey& conditionKey, bool condition,
         const LogEvent& event) {
     if (!condition) {
         return;
diff --git a/cmds/statsd/src/metrics/EventMetricProducer.h b/cmds/statsd/src/metrics/EventMetricProducer.h
index 6120ad8..a57b07d 100644
--- a/cmds/statsd/src/metrics/EventMetricProducer.h
+++ b/cmds/statsd/src/metrics/EventMetricProducer.h
@@ -46,11 +46,12 @@
 private:
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const HashableDimensionKey& eventKey,
-            const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
+            const ConditionKey& conditionKey, bool condition,
             const LogEvent& event) override;
 
     void onDumpReportLocked(const uint64_t dumpTimeNs,
                             android::util::ProtoOutputStream* protoOutput) override;
+    void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override;
 
     // Internal interface to handle condition change.
     void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
index 47cca0e..51fd9fc 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.cpp
@@ -19,6 +19,8 @@
 
 #include "GaugeMetricProducer.h"
 #include "guardrail/StatsdStats.h"
+#include "dimension.h"
+#include "stats_log_util.h"
 
 #include <cutils/log.h>
 
@@ -51,12 +53,6 @@
 // for GaugeMetricData
 const int FIELD_ID_DIMENSION = 1;
 const int FIELD_ID_BUCKET_INFO = 2;
-// for KeyValuePair
-const int FIELD_ID_KEY = 1;
-const int FIELD_ID_VALUE_STR = 2;
-const int FIELD_ID_VALUE_INT = 3;
-const int FIELD_ID_VALUE_BOOL = 4;
-const int FIELD_ID_VALUE_FLOAT = 5;
 // for GaugeBucketInfo
 const int FIELD_ID_START_BUCKET_NANOS = 1;
 const int FIELD_ID_END_BUCKET_NANOS = 2;
@@ -71,18 +67,18 @@
       mStatsPullerManager(statsPullerManager),
       mPullTagId(pullTagId),
       mAtomTagId(atomTagId) {
+    mCurrentSlicedBucket = std::make_shared<DimToGaugeFieldsMap>();
+    mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>();
     if (metric.has_bucket() && metric.bucket().has_bucket_size_millis()) {
         mBucketSizeNs = metric.bucket().bucket_size_millis() * 1000 * 1000;
     } else {
         mBucketSizeNs = kDefaultGaugemBucketSizeNs;
     }
 
-    for (int i = 0; i < metric.gauge_fields().field_num_size(); i++) {
-        mGaugeFields.push_back(metric.gauge_fields().field_num(i));
-    }
+    mFieldFilter = metric.gauge_fields_filter();
 
     // TODO: use UidMap if uid->pkg_name is required
-    mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
+    mDimensions = metric.dimensions();
 
     if (metric.links().size() > 0) {
         mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
@@ -93,7 +89,7 @@
     // Kicks off the puller immediately.
     if (mPullTagId != -1) {
         mStatsPullerManager->RegisterReceiver(mPullTagId, this,
-                                             metric.bucket().bucket_size_millis());
+                                              metric.bucket().bucket_size_millis());
     }
 
     VLOG("metric %s created. bucket size %lld start_time: %lld", metric.name().c_str(),
@@ -116,6 +112,10 @@
     }
 }
 
+void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
+    flushIfNeededLocked(dumpTimeNs);
+}
+
 void GaugeMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
                                              ProtoOutputStream* protoOutput) {
     VLOG("gauge metric %s dump report now...", mName.c_str());
@@ -128,28 +128,16 @@
 
     for (const auto& pair : mPastBuckets) {
         const HashableDimensionKey& hashableKey = pair.first;
-        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 : 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());
-            if (kv.has_value_str()) {
-                protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str());
-            } else if (kv.has_value_int()) {
-                protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
-            } else if (kv.has_value_bool()) {
-                protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
-            } else if (kv.has_value_float()) {
-                protoOutput->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
-            }
-            protoOutput->end(dimensionToken);
-        }
+        // First fill dimension.
+        long long dimensionToken = protoOutput->start(
+                FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
+        writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+        protoOutput->end(dimensionToken);
 
         // Then fill bucket_info (GaugeBucketInfo).
         for (const auto& bucket : pair.second) {
@@ -160,25 +148,11 @@
             protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_END_BUCKET_NANOS,
                                (long long)bucket.mBucketEndNs);
             long long atomToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_ATOM);
-            long long eventToken = protoOutput->start(FIELD_TYPE_MESSAGE | mAtomTagId);
-            for (const auto& pair : bucket.mEvent->kv) {
-                if (pair.has_value_int()) {
-                    protoOutput->write(FIELD_TYPE_INT32 | pair.key(), pair.value_int());
-                } else if (pair.has_value_long()) {
-                    protoOutput->write(FIELD_TYPE_INT64 | pair.key(), pair.value_long());
-                } else if (pair.has_value_str()) {
-                    protoOutput->write(FIELD_TYPE_STRING | pair.key(), pair.value_str());
-                } else if (pair.has_value_long()) {
-                    protoOutput->write(FIELD_TYPE_FLOAT | pair.key(), pair.value_float());
-                } else if (pair.has_value_bool()) {
-                    protoOutput->write(FIELD_TYPE_BOOL | pair.key(), pair.value_bool());
-                }
-            }
-            protoOutput->end(eventToken);
+            writeFieldValueTreeToStream(*bucket.mGaugeFields, protoOutput);
             protoOutput->end(atomToken);
             protoOutput->end(bucketInfoToken);
-            VLOG("\t bucket [%lld - %lld] content: %s", (long long)bucket.mBucketStartNs,
-                 (long long)bucket.mBucketEndNs, bucket.mEvent->ToString().c_str());
+            VLOG("\t bucket [%lld - %lld] includes %d gauge fields.", (long long)bucket.mBucketStartNs,
+                 (long long)bucket.mBucketEndNs, (int)bucket.mGaugeFields->size());
         }
         protoOutput->end(wrapperToken);
     }
@@ -223,18 +197,13 @@
     VLOG("Metric %s onSlicedConditionMayChange", mName.c_str());
 }
 
-shared_ptr<EventKV> GaugeMetricProducer::getGauge(const LogEvent& event) {
-    shared_ptr<EventKV> ret = make_shared<EventKV>();
-    if (mGaugeFields.size() == 0) {
-        for (int i = 1; i <= event.size(); i++) {
-            ret->kv.push_back(event.GetKeyValueProto(i));
-        }
-    } else {
-        for (int i = 0; i < (int)mGaugeFields.size(); i++) {
-            ret->kv.push_back(event.GetKeyValueProto(mGaugeFields[i]));
-        }
+std::shared_ptr<FieldValueMap> GaugeMetricProducer::getGaugeFields(const LogEvent& event) {
+    std::shared_ptr<FieldValueMap> gaugeFields =
+        std::make_shared<FieldValueMap>(event.getFieldValueMap());
+    if (!mFieldFilter.include_all()) {
+        filterFields(mFieldFilter.fields(), gaugeFields.get());
     }
-    return ret;
+    return gaugeFields;
 }
 
 void GaugeMetricProducer::onDataPulled(const std::vector<std::shared_ptr<LogEvent>>& allData) {
@@ -268,7 +237,7 @@
 
 void GaugeMetricProducer::onMatchedLogEventInternalLocked(
         const size_t matcherIndex, const HashableDimensionKey& eventKey,
-        const map<string, HashableDimensionKey>& conditionKey, bool condition,
+        const ConditionKey& conditionKey, bool condition,
         const LogEvent& event) {
     if (condition == false) {
         return;
@@ -285,21 +254,21 @@
     if (mCurrentSlicedBucket->find(eventKey) != mCurrentSlicedBucket->end()) {
         return;
     }
-    shared_ptr<EventKV> gauge = getGauge(event);
+    std::shared_ptr<FieldValueMap> gaugeFields = getGaugeFields(event);
     if (hitGuardRailLocked(eventKey)) {
         return;
     }
-    (*mCurrentSlicedBucket)[eventKey] = gauge;
+    (*mCurrentSlicedBucket)[eventKey] = gaugeFields;
     // Anomaly detection on gauge metric only works when there is one numeric
     // field specified.
     if (mAnomalyTrackers.size() > 0) {
-        if (gauge->kv.size() == 1) {
-            KeyValuePair pair = gauge->kv[0];
+        if (gaugeFields->size() == 1) {
+            const DimensionsValue& dimensionsValue = gaugeFields->begin()->second;
             long gaugeVal = 0;
-            if (pair.has_value_int()) {
-                gaugeVal = (long)pair.value_int();
-            } else if (pair.has_value_long()) {
-                gaugeVal = pair.value_long();
+            if (dimensionsValue.has_value_int()) {
+                gaugeVal = (long)dimensionsValue.value_int();
+            } else if (dimensionsValue.has_value_long()) {
+                gaugeVal = dimensionsValue.value_long();
             }
             for (auto& tracker : mAnomalyTrackers) {
                 tracker->detectAndDeclareAnomaly(eventTimeNs, mCurrentBucketNum, eventKey,
@@ -313,12 +282,12 @@
     mCurrentSlicedBucketForAnomaly->clear();
     status_t err = NO_ERROR;
     for (const auto& slice : *mCurrentSlicedBucket) {
-        KeyValuePair pair = slice.second->kv[0];
+        const DimensionsValue& dimensionsValue = slice.second->begin()->second;
         long gaugeVal = 0;
-        if (pair.has_value_int()) {
-            gaugeVal = (long)pair.value_int();
-        } else if (pair.has_value_long()) {
-            gaugeVal = pair.value_long();
+        if (dimensionsValue.has_value_int()) {
+            gaugeVal = (long)dimensionsValue.value_int();
+        } else if (dimensionsValue.has_value_long()) {
+            gaugeVal = dimensionsValue.value_long();
         }
         (*mCurrentSlicedBucketForAnomaly)[slice.first] = gaugeVal;
     }
@@ -342,11 +311,10 @@
     info.mBucketNum = mCurrentBucketNum;
 
     for (const auto& slice : *mCurrentSlicedBucket) {
-        info.mEvent = slice.second;
+        info.mGaugeFields = slice.second;
         auto& bucketList = mPastBuckets[slice.first];
         bucketList.push_back(info);
-        VLOG("gauge metric %s, dump key value: %s -> %s", mName.c_str(),
-             slice.first.c_str(), slice.second->ToString().c_str());
+        VLOG("gauge metric %s, dump key value: %s", mName.c_str(), slice.first.c_str());
     }
 
     // Reset counters
@@ -357,7 +325,7 @@
         }
     }
 
-    mCurrentSlicedBucket = std::make_shared<DimToEventKVMap>();
+    mCurrentSlicedBucket = std::make_shared<DimToGaugeFieldsMap>();
 
     // Adjusts the bucket start time
     int64_t numBucketsForward = (eventTimeNs - mCurrentBucketStartTimeNs) / mBucketSizeNs;
diff --git a/cmds/statsd/src/metrics/GaugeMetricProducer.h b/cmds/statsd/src/metrics/GaugeMetricProducer.h
index 19d51e8..2a6401d 100644
--- a/cmds/statsd/src/metrics/GaugeMetricProducer.h
+++ b/cmds/statsd/src/metrics/GaugeMetricProducer.h
@@ -35,10 +35,13 @@
 struct GaugeBucket {
     int64_t mBucketStartNs;
     int64_t mBucketEndNs;
-    std::shared_ptr<EventKV> mEvent;
+    std::shared_ptr<FieldValueMap> mGaugeFields;
     uint64_t mBucketNum;
 };
 
+typedef std::unordered_map<HashableDimensionKey, std::shared_ptr<FieldValueMap>>
+    DimToGaugeFieldsMap;
+
 // This gauge metric producer first register the puller to automatically pull the gauge at the
 // beginning of each bucket. If the condition is met, insert it to the bucket info. Otherwise
 // proactively pull the gauge when the condition is changed to be true. Therefore, the gauge metric
@@ -57,12 +60,13 @@
 protected:
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const HashableDimensionKey& eventKey,
-            const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
+            const ConditionKey& conditionKey, bool condition,
             const LogEvent& event) override;
 
 private:
     void onDumpReportLocked(const uint64_t dumpTimeNs,
                             android::util::ProtoOutputStream* protoOutput) override;
+    void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override;
 
     // for testing
     GaugeMetricProducer(const ConfigKey& key, const GaugeMetric& gaugeMetric,
@@ -94,10 +98,10 @@
     std::unordered_map<HashableDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
 
     // The current bucket.
-    std::shared_ptr<DimToEventKVMap> mCurrentSlicedBucket = std::make_shared<DimToEventKVMap>();
+    std::shared_ptr<DimToGaugeFieldsMap> mCurrentSlicedBucket;
 
     // The current bucket for anomaly detection.
-    std::shared_ptr<DimToValMap> mCurrentSlicedBucketForAnomaly = std::make_shared<DimToValMap>();
+    std::shared_ptr<DimToValMap> mCurrentSlicedBucketForAnomaly;
 
     // Translate Atom based bucket to single numeric value bucket for anomaly
     void updateCurrentSlicedBucketForAnomaly();
@@ -105,10 +109,10 @@
     int mAtomTagId;
 
     // Whitelist of fields to report. Empty means all are reported.
-    std::vector<int> mGaugeFields;
+    FieldFilter mFieldFilter;
 
     // apply a whitelist on the original input
-    std::shared_ptr<EventKV> getGauge(const LogEvent& event);
+    std::shared_ptr<FieldValueMap> getGaugeFields(const LogEvent& event);
 
     // Util function to check whether the specified dimension hits the guardrail.
     bool hitGuardRailLocked(const HashableDimensionKey& newKey);
diff --git a/cmds/statsd/src/metrics/MetricProducer.cpp b/cmds/statsd/src/metrics/MetricProducer.cpp
index 5286908..4ed8289 100644
--- a/cmds/statsd/src/metrics/MetricProducer.cpp
+++ b/cmds/statsd/src/metrics/MetricProducer.cpp
@@ -28,22 +28,12 @@
         return;
     }
 
-    HashableDimensionKey eventKey;
-
-    if (mDimension.size() > 0) {
-        vector<KeyValuePair> key = getDimensionKey(event, mDimension);
-        eventKey = HashableDimensionKey(key);
-    } else {
-        eventKey = DEFAULT_DIMENSION_KEY;
-    }
-
     bool condition;
-
-    map<string, HashableDimensionKey> conditionKeys;
+    map<string, std::vector<HashableDimensionKey>> conditionKeys;
     if (mConditionSliced) {
         for (const auto& link : mConditionLinks) {
-            HashableDimensionKey conditionKey = getDimensionKeyForCondition(event, link);
-            conditionKeys[link.condition()] = conditionKey;
+            conditionKeys.insert(std::make_pair(link.condition(),
+                                                getDimensionKeysForCondition(event, link)));
         }
         if (mWizard->query(mConditionTrackerIndex, conditionKeys) != ConditionState::kTrue) {
             condition = false;
@@ -53,7 +43,17 @@
     } else {
         condition = mCondition;
     }
-    onMatchedLogEventInternalLocked(matcherIndex, eventKey, conditionKeys, condition, event);
+
+    if (mDimensions.child_size() > 0) {
+        vector<DimensionsValue> dimensionValues = getDimensionKeys(event, mDimensions);
+        for (const DimensionsValue& dimensionValue : dimensionValues) {
+            onMatchedLogEventInternalLocked(
+                matcherIndex, HashableDimensionKey(dimensionValue), conditionKeys, condition, event);
+        }
+    } else {
+        onMatchedLogEventInternalLocked(
+            matcherIndex, DEFAULT_DIMENSION_KEY, conditionKeys, condition, event);
+    }
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/MetricProducer.h b/cmds/statsd/src/metrics/MetricProducer.h
index 647d8c1..fe1a53b 100644
--- a/cmds/statsd/src/metrics/MetricProducer.h
+++ b/cmds/statsd/src/metrics/MetricProducer.h
@@ -50,6 +50,7 @@
           mConditionSliced(false),
           mWizard(wizard),
           mConditionTrackerIndex(conditionIndex){};
+
     virtual ~MetricProducer(){};
 
     void notifyAppUpgrade(const string& apk, const int uid, const int64_t version) override{
@@ -90,6 +91,10 @@
         std::lock_guard<std::mutex> lock(mMutex);
         return onDumpReportLocked(dumpTimeNs, protoOutput);
     }
+    void onDumpReport(const uint64_t dumpTimeNs, StatsLogReport* report) {
+        std::lock_guard<std::mutex> lock(mMutex);
+        return onDumpReportLocked(dumpTimeNs, report);
+    }
 
     // Returns the memory in bytes currently used to store this metric's data. Does not change
     // state.
@@ -112,11 +117,16 @@
         return mBucketSizeNs;
     }
 
+    inline const string& getName() {
+        return mName;
+    }
+
 protected:
     virtual void onConditionChangedLocked(const bool condition, const uint64_t eventTime) = 0;
     virtual void onSlicedConditionMayChangeLocked(const uint64_t eventTime) = 0;
     virtual void onDumpReportLocked(const uint64_t dumpTimeNs,
                                     android::util::ProtoOutputStream* protoOutput) = 0;
+    virtual void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) = 0;
     virtual size_t byteSizeLocked() const = 0;
 
     const std::string mName;
@@ -140,7 +150,7 @@
 
     int mConditionTrackerIndex;
 
-    std::vector<KeyMatcher> mDimension;  // The dimension defined in statsd_config
+    FieldMatcher mDimensions;  // The dimension defined in statsd_config
 
     std::vector<MetricConditionLink> mConditionLinks;
 
@@ -163,7 +173,7 @@
      */
     virtual void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const HashableDimensionKey& eventKey,
-            const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
+            const ConditionKey& conditionKey, bool condition,
             const LogEvent& event) = 0;
 
     // Consume the parsed stats log entry that already matched the "what" of the metric.
diff --git a/cmds/statsd/src/metrics/MetricsManager.cpp b/cmds/statsd/src/metrics/MetricsManager.cpp
index 231bd8e..9749e0f 100644
--- a/cmds/statsd/src/metrics/MetricsManager.cpp
+++ b/cmds/statsd/src/metrics/MetricsManager.cpp
@@ -49,7 +49,7 @@
                                const long timeBaseSec, sp<UidMap> uidMap)
     : mConfigKey(key), mUidMap(uidMap) {
     mConfigValid =
-            initStatsdConfig(key, config, timeBaseSec, mTagIds, mAllAtomMatchers, mAllConditionTrackers,
+            initStatsdConfig(key, config, *uidMap, timeBaseSec, mTagIds, mAllAtomMatchers, mAllConditionTrackers,
                              mAllMetricProducers, mAllAnomalyTrackers, mConditionToMetricMap,
                              mTrackerToMetricMap, mTrackerToConditionMap);
 
@@ -142,6 +142,12 @@
     initLogSourceWhiteList();
 }
 
+void MetricsManager::onDumpReport(const uint64_t& dumpTimeStampNs, ConfigMetricsReport* report) {
+    for (auto& producer : mAllMetricProducers) {
+        producer->onDumpReport(dumpTimeStampNs, report->add_metrics());
+    }
+}
+
 void MetricsManager::onDumpReport(ProtoOutputStream* protoOutput) {
     VLOG("=========================Metric Reports Start==========================");
     uint64_t dumpTimeStampNs = time(nullptr) * NS_PER_SEC;
diff --git a/cmds/statsd/src/metrics/MetricsManager.h b/cmds/statsd/src/metrics/MetricsManager.h
index 8faa75d..51c4c90 100644
--- a/cmds/statsd/src/metrics/MetricsManager.h
+++ b/cmds/statsd/src/metrics/MetricsManager.h
@@ -45,8 +45,9 @@
 
     void onLogEvent(const LogEvent& event);
 
-    void onAnomalyAlarmFired(const uint64_t timestampNs,
-                         unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& anomalySet);
+    void onAnomalyAlarmFired(
+        const uint64_t timestampNs,
+        unordered_set<sp<const AnomalyAlarm>, SpHash<AnomalyAlarm>>& anomalySet);
 
     void setAnomalyMonitor(const sp<AnomalyMonitor>& anomalyMonitor);
 
@@ -58,6 +59,7 @@
 
     // Config source owner can call onDumpReport() to get all the metrics collected.
     virtual void onDumpReport(android::util::ProtoOutputStream* protoOutput);
+    virtual void onDumpReport(const uint64_t& dumpTimeStampNs, ConfigMetricsReport* report);
 
     // Computes the total byte size of all metrics managed by a single config source.
     // Does not change the state.
@@ -128,6 +130,9 @@
     std::unordered_map<int, std::vector<int>> mConditionToMetricMap;
 
     void initLogSourceWhiteList();
+
+    FRIEND_TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions);
+    FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
 };
 
 }  // namespace statsd
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.cpp b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
index 40aed7b..b58dc68 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.cpp
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.cpp
@@ -19,6 +19,7 @@
 
 #include "ValueMetricProducer.h"
 #include "guardrail/StatsdStats.h"
+#include "stats_log_util.h"
 
 #include <cutils/log.h>
 #include <limits.h>
@@ -54,12 +55,6 @@
 // for ValueMetricData
 const int FIELD_ID_DIMENSION = 1;
 const int FIELD_ID_BUCKET_INFO = 2;
-// for KeyValuePair
-const int FIELD_ID_KEY = 1;
-const int FIELD_ID_VALUE_STR = 2;
-const int FIELD_ID_VALUE_INT = 3;
-const int FIELD_ID_VALUE_BOOL = 4;
-const int FIELD_ID_VALUE_FLOAT = 5;
 // for ValueBucketInfo
 const int FIELD_ID_START_BUCKET_NANOS = 1;
 const int FIELD_ID_END_BUCKET_NANOS = 2;
@@ -84,7 +79,7 @@
         mBucketSizeNs = kDefaultBucketSizeMillis * 1000 * 1000;
     }
 
-    mDimension.insert(mDimension.begin(), metric.dimension().begin(), metric.dimension().end());
+    mDimensions = metric.dimensions();
 
     if (metric.links().size() > 0) {
         mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
@@ -121,6 +116,23 @@
     VLOG("Metric %s onSlicedConditionMayChange", mName.c_str());
 }
 
+void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) {
+    flushIfNeededLocked(dumpTimeNs);
+    report->set_metric_name(mName);
+    report->set_start_report_nanos(mStartTimeNs);
+    auto value_metrics = report->mutable_value_metrics();
+    for (const auto& pair : mPastBuckets) {
+        ValueMetricData* metricData = value_metrics->add_data();
+        *metricData->mutable_dimension() = pair.first.getDimensionsValue();
+        for (const auto& bucket : pair.second) {
+            ValueBucketInfo* bucketInfo = metricData->add_bucket_info();
+            bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
+            bucketInfo->set_end_bucket_nanos(bucket.mBucketEndNs);
+            bucketInfo->set_value(bucket.mValue);
+        }
+    }
+}
+
 void ValueMetricProducer::onDumpReportLocked(const uint64_t dumpTimeNs,
                                              ProtoOutputStream* protoOutput) {
     VLOG("metric %s dump report now...", mName.c_str());
@@ -132,26 +144,14 @@
     for (const auto& pair : mPastBuckets) {
         const HashableDimensionKey& hashableKey = pair.first;
         VLOG("  dimension key %s", hashableKey.c_str());
-        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 : 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());
-            if (kv.has_value_str()) {
-                protoOutput->write(FIELD_TYPE_STRING | FIELD_ID_VALUE_STR, kv.value_str());
-            } else if (kv.has_value_int()) {
-                protoOutput->write(FIELD_TYPE_INT64 | FIELD_ID_VALUE_INT, kv.value_int());
-            } else if (kv.has_value_bool()) {
-                protoOutput->write(FIELD_TYPE_BOOL | FIELD_ID_VALUE_BOOL, kv.value_bool());
-            } else if (kv.has_value_float()) {
-                protoOutput->write(FIELD_TYPE_FLOAT | FIELD_ID_VALUE_FLOAT, kv.value_float());
-            }
-            protoOutput->end(dimensionToken);
-        }
+        // First fill dimension.
+        long long dimensionToken = protoOutput->start(
+                FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DIMENSION);
+        writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+        protoOutput->end(dimensionToken);
 
         // Then fill bucket_info (ValueBucketInfo).
         for (const auto& bucket : pair.second) {
@@ -256,7 +256,7 @@
 
 void ValueMetricProducer::onMatchedLogEventInternalLocked(
         const size_t matcherIndex, const HashableDimensionKey& eventKey,
-        const map<string, HashableDimensionKey>& conditionKey, bool condition,
+        const ConditionKey& conditionKey, bool condition,
         const LogEvent& event) {
     uint64_t eventTimeNs = event.GetTimestampNs();
     if (eventTimeNs < mCurrentBucketStartTimeNs) {
diff --git a/cmds/statsd/src/metrics/ValueMetricProducer.h b/cmds/statsd/src/metrics/ValueMetricProducer.h
index 2f27e4e..6f3b1d1 100644
--- a/cmds/statsd/src/metrics/ValueMetricProducer.h
+++ b/cmds/statsd/src/metrics/ValueMetricProducer.h
@@ -50,12 +50,13 @@
 protected:
     void onMatchedLogEventInternalLocked(
             const size_t matcherIndex, const HashableDimensionKey& eventKey,
-            const std::map<std::string, HashableDimensionKey>& conditionKey, bool condition,
+            const ConditionKey& conditionKey, bool condition,
             const LogEvent& event) override;
 
 private:
     void onDumpReportLocked(const uint64_t dumpTimeNs,
                             android::util::ProtoOutputStream* protoOutput) override;
+    void onDumpReportLocked(const uint64_t dumpTimeNs, StatsLogReport* report) override;
 
     // Internal interface to handle condition change.
     void onConditionChangedLocked(const bool conditionMet, const uint64_t eventTime) override;
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 658b732..97dbf7a 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -49,7 +49,7 @@
         ALOGW("cannot find the AtomMatcher \"%s\" in config", what.c_str());
         return false;
     }
-    if (usedForDimension && allAtomMatchers[logTrackerIt->second]->getTagIds().size() > 1) {
+    if (usedForDimension && allAtomMatchers[logTrackerIt->second]->getAtomIds().size() > 1) {
         ALOGE("AtomMatcher \"%s\" has more than one tag ids. When a metric has dimension, "
               "the \"what\" can only about one atom type.",
               what.c_str());
@@ -92,7 +92,8 @@
     return true;
 }
 
-bool initLogTrackers(const StatsdConfig& config, unordered_map<string, int>& logTrackerMap,
+bool initLogTrackers(const StatsdConfig& config, const UidMap& uidMap,
+                     unordered_map<string, int>& logTrackerMap,
                      vector<sp<LogMatchingTracker>>& allAtomMatchers, set<int>& allTagIds) {
     vector<AtomMatcher> matcherConfigs;
     const int atomMatcherCount = config.atom_matcher_size();
@@ -106,7 +107,7 @@
         switch (logMatcher.contents_case()) {
             case AtomMatcher::ContentsCase::kSimpleAtomMatcher:
                 allAtomMatchers.push_back(new SimpleLogMatchingTracker(
-                        logMatcher.name(), index, logMatcher.simple_atom_matcher()));
+                        logMatcher.name(), index, logMatcher.simple_atom_matcher(), uidMap));
                 break;
             case AtomMatcher::ContentsCase::kCombination:
                 allAtomMatchers.push_back(
@@ -131,7 +132,7 @@
             return false;
         }
         // Collect all the tag ids that are interesting. TagIds exist in leaf nodes only.
-        const set<int>& tagIds = matcher->getTagIds();
+        const set<int>& tagIds = matcher->getAtomIds();
         allTagIds.insert(tagIds.begin(), tagIds.end());
     }
     return true;
@@ -205,10 +206,13 @@
     // Align all buckets to same instant in MIN_BUCKET_SIZE_SEC, so that avoid alarm
     // clock will not grow very aggressive. New metrics will be delayed up to
     // MIN_BUCKET_SIZE_SEC before starting.
-    long currentTimeSec = time(nullptr);
-    uint64_t startTimeNs = (currentTimeSec - kMinBucketSizeSec -
-                            (currentTimeSec - timeBaseSec) % kMinBucketSizeSec) *
-                           NS_PER_SEC;
+    // Why not use timeBaseSec directly?
+//    long currentTimeSec = time(nullptr);
+//    uint64_t startTimeNs = (currentTimeSec - kMinBucketSizeSec -
+//                            (currentTimeSec - timeBaseSec) % kMinBucketSizeSec) *
+//                           NS_PER_SEC;
+
+    uint64_t startTimeNs = timeBaseSec * NS_PER_SEC;
 
     // Build MetricProducers for each metric defined in config.
     // build CountMetricProducer
@@ -222,7 +226,7 @@
         int metricIndex = allMetricProducers.size();
         metricMap.insert({metric.name(), metricIndex});
         int trackerIndex;
-        if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
+        if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(),
                                          allAtomMatchers, logTrackerMap, trackerToMetricMap,
                                          trackerIndex)) {
             return false;
@@ -274,7 +278,7 @@
         int trackerIndices[3] = {-1, -1, -1};
         if (!simplePredicate.has_start() ||
             !handleMetricWithLogTrackers(simplePredicate.start(), metricIndex,
-                                         metric.dimension_size() > 0, allAtomMatchers,
+                                         metric.has_dimensions(), allAtomMatchers,
                                          logTrackerMap, trackerToMetricMap, trackerIndices[0])) {
             ALOGE("Duration metrics must specify a valid the start event matcher");
             return false;
@@ -282,21 +286,19 @@
 
         if (simplePredicate.has_stop() &&
             !handleMetricWithLogTrackers(simplePredicate.stop(), metricIndex,
-                                         metric.dimension_size() > 0, allAtomMatchers,
+                                         metric.has_dimensions(), allAtomMatchers,
                                          logTrackerMap, trackerToMetricMap, trackerIndices[1])) {
             return false;
         }
 
         if (simplePredicate.has_stop_all() &&
             !handleMetricWithLogTrackers(simplePredicate.stop_all(), metricIndex,
-                                         metric.dimension_size() > 0, allAtomMatchers,
+                                         metric.has_dimensions(), allAtomMatchers,
                                          logTrackerMap, trackerToMetricMap, trackerIndices[2])) {
             return false;
         }
 
-        vector<KeyMatcher> internalDimension;
-        internalDimension.insert(internalDimension.begin(), simplePredicate.dimension().begin(),
-                                 simplePredicate.dimension().end());
+        FieldMatcher internalDimensions = simplePredicate.dimensions();
 
         int conditionIndex = -1;
 
@@ -316,7 +318,7 @@
 
         sp<MetricProducer> durationMetric = new DurationMetricProducer(
                 key, metric, conditionIndex, trackerIndices[0], trackerIndices[1],
-                trackerIndices[2], nesting, wizard, internalDimension, startTimeNs);
+                trackerIndices[2], nesting, wizard, internalDimensions, startTimeNs);
 
         allMetricProducers.push_back(durationMetric);
     }
@@ -368,7 +370,7 @@
         int metricIndex = allMetricProducers.size();
         metricMap.insert({metric.name(), metricIndex});
         int trackerIndex;
-        if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
+        if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(),
                                          allAtomMatchers, logTrackerMap, trackerToMetricMap,
                                          trackerIndex)) {
             return false;
@@ -376,10 +378,10 @@
 
         sp<LogMatchingTracker> atomMatcher = allAtomMatchers.at(trackerIndex);
         // If it is pulled atom, it should be simple matcher with one tagId.
-        if (atomMatcher->getTagIds().size() != 1) {
+        if (atomMatcher->getAtomIds().size() != 1) {
             return false;
         }
-        int atomTagId = *(atomMatcher->getTagIds().begin());
+        int atomTagId = *(atomMatcher->getAtomIds().begin());
         int pullTagId = statsPullerManager.PullerForMatcherExists(atomTagId) ? atomTagId : -1;
 
         int conditionIndex = -1;
@@ -410,15 +412,15 @@
             return false;
         }
 
-        if ((!metric.gauge_fields().has_include_all() ||
-             (metric.gauge_fields().include_all() == false)) &&
-            metric.gauge_fields().field_num_size() == 0) {
+        if ((!metric.gauge_fields_filter().has_include_all() ||
+             (metric.gauge_fields_filter().include_all() == false)) &&
+            !hasLeafNode(metric.gauge_fields_filter().fields())) {
             ALOGW("Incorrect field filter setting in GaugeMetric %s", metric.name().c_str());
             return false;
         }
-        if ((metric.gauge_fields().has_include_all() &&
-             metric.gauge_fields().include_all() == true) &&
-            metric.gauge_fields().field_num_size() > 0) {
+        if ((metric.gauge_fields_filter().has_include_all() &&
+             metric.gauge_fields_filter().include_all() == true) &&
+            hasLeafNode(metric.gauge_fields_filter().fields())) {
             ALOGW("Incorrect field filter setting in GaugeMetric %s", metric.name().c_str());
             return false;
         }
@@ -426,7 +428,7 @@
         int metricIndex = allMetricProducers.size();
         metricMap.insert({metric.name(), metricIndex});
         int trackerIndex;
-        if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.dimension_size() > 0,
+        if (!handleMetricWithLogTrackers(metric.what(), metricIndex, metric.has_dimensions(),
                                          allAtomMatchers, logTrackerMap, trackerToMetricMap,
                                          trackerIndex)) {
             return false;
@@ -434,10 +436,10 @@
 
         sp<LogMatchingTracker> atomMatcher = allAtomMatchers.at(trackerIndex);
         // If it is pulled atom, it should be simple matcher with one tagId.
-        if (atomMatcher->getTagIds().size() != 1) {
+        if (atomMatcher->getAtomIds().size() != 1) {
             return false;
         }
-        int atomTagId = *(atomMatcher->getTagIds().begin());
+        int atomTagId = *(atomMatcher->getAtomIds().begin());
         int pullTagId = statsPullerManager.PullerForMatcherExists(atomTagId) ? atomTagId : -1;
 
         int conditionIndex = -1;
@@ -462,7 +464,8 @@
     return true;
 }
 
-bool initAlerts(const StatsdConfig& config, const unordered_map<string, int>& metricProducerMap,
+bool initAlerts(const StatsdConfig& config,
+                const unordered_map<string, int>& metricProducerMap,
                 vector<sp<MetricProducer>>& allMetricProducers,
                 vector<sp<AnomalyTracker>>& allAnomalyTrackers) {
     for (int i = 0; i < config.alert_size(); i++) {
@@ -488,7 +491,9 @@
     return true;
 }
 
-bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec, set<int>& allTagIds,
+bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config,
+                const UidMap& uidMap,
+                        const long timeBaseSec, set<int>& allTagIds,
                       vector<sp<LogMatchingTracker>>& allAtomMatchers,
                       vector<sp<ConditionTracker>>& allConditionTrackers,
                       vector<sp<MetricProducer>>& allMetricProducers,
@@ -500,7 +505,7 @@
     unordered_map<string, int> conditionTrackerMap;
     unordered_map<string, int> metricProducerMap;
 
-    if (!initLogTrackers(config, logTrackerMap, allAtomMatchers, allTagIds)) {
+    if (!initLogTrackers(config, uidMap, logTrackerMap, allAtomMatchers, allTagIds)) {
         ALOGE("initLogMatchingTrackers failed");
         return false;
     }
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index 3337332..9ad5176 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -43,6 +43,7 @@
 // [allAtomMatchers]: should store the sp to all the LogMatchingTracker
 // [allTagIds]: contains the set of all interesting tag ids to this config.
 bool initLogTrackers(const StatsdConfig& config,
+                const UidMap& uidMap,
                      std::unordered_map<std::string, int>& logTrackerMap,
                      std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
                      std::set<int>& allTagIds);
@@ -89,7 +90,10 @@
 
 // Initialize MetricsManager from StatsdConfig.
 // Parameters are the members of MetricsManager. See MetricsManager for declaration.
-bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config, const long timeBaseSec, std::set<int>& allTagIds,
+bool initStatsdConfig(const ConfigKey& key, const StatsdConfig& config,
+
+                const UidMap& uidMap,
+                const long timeBaseSec, std::set<int>& allTagIds,
                       std::vector<sp<LogMatchingTracker>>& allAtomMatchers,
                       std::vector<sp<ConditionTracker>>& allConditionTrackers,
                       std::vector<sp<MetricProducer>>& allMetricProducers,
diff --git a/cmds/statsd/src/packages/UidMap.cpp b/cmds/statsd/src/packages/UidMap.cpp
index 21a9cf3..416b87b 100644
--- a/cmds/statsd/src/packages/UidMap.cpp
+++ b/cmds/statsd/src/packages/UidMap.cpp
@@ -404,4 +404,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/packages/UidMap.h b/cmds/statsd/src/packages/UidMap.h
index a9aec94..02dea54 100644
--- a/cmds/statsd/src/packages/UidMap.h
+++ b/cmds/statsd/src/packages/UidMap.h
@@ -168,4 +168,3 @@
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
-
diff --git a/cmds/statsd/src/stats_log.proto b/cmds/statsd/src/stats_log.proto
index 3c85c57..f5282ea 100644
--- a/cmds/statsd/src/stats_log.proto
+++ b/cmds/statsd/src/stats_log.proto
@@ -24,8 +24,14 @@
 
 import "frameworks/base/cmds/statsd/src/atoms.proto";
 
-message KeyValuePair {
-  optional int32 key = 1;
+message Field {
+  optional int32 field = 1;
+  optional int32 position_index = 2 [default = -1];
+  repeated Field child = 3;
+}
+
+message DimensionsValue {
+  optional int32 field = 1;
 
   oneof value {
     string value_str = 2;
@@ -33,9 +39,14 @@
     int64 value_long = 4;
     bool value_bool = 5;
     float value_float = 6;
+    DimensionsValueTuple value_tuple = 7;
   }
 }
 
+message DimensionsValueTuple {
+  repeated DimensionsValue dimensions_value = 1;
+}
+
 message EventMetricData {
   optional int64 timestamp_nanos = 1;
 
@@ -51,7 +62,7 @@
 }
 
 message CountMetricData {
-  repeated KeyValuePair dimension = 1;
+  optional DimensionsValue dimension = 1;
 
   repeated CountBucketInfo bucket_info = 2;
 }
@@ -65,7 +76,7 @@
 }
 
 message DurationMetricData {
-  repeated KeyValuePair dimension = 1;
+  optional DimensionsValue dimension = 1;
 
   repeated DurationBucketInfo bucket_info = 2;
 }
@@ -79,7 +90,7 @@
 }
 
 message ValueMetricData {
-  repeated KeyValuePair dimension = 1;
+  optional DimensionsValue dimension = 1;
 
   repeated ValueBucketInfo bucket_info = 2;
 }
@@ -93,7 +104,7 @@
 }
 
 message GaugeMetricData {
-  repeated KeyValuePair dimension = 1;
+  optional DimensionsValue dimension = 1;
 
   repeated GaugeBucketInfo bucket_info = 2;
 }
diff --git a/cmds/statsd/src/stats_log_util.cpp b/cmds/statsd/src/stats_log_util.cpp
new file mode 100644
index 0000000..476e117
--- /dev/null
+++ b/cmds/statsd/src/stats_log_util.cpp
@@ -0,0 +1,227 @@
+/*
+ * 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_log_util.h"
+
+#include <set>
+#include <stack>
+#include <utils/Log.h>
+
+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;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// for DimensionsValue Proto
+const int DIMENSIONS_VALUE_FIELD = 1;
+const int DIMENSIONS_VALUE_VALUE_STR = 2;
+const int DIMENSIONS_VALUE_VALUE_INT = 3;
+const int DIMENSIONS_VALUE_VALUE_LONG = 4;
+const int DIMENSIONS_VALUE_VALUE_BOOL = 5;
+const int DIMENSIONS_VALUE_VALUE_FLOAT = 6;
+const int DIMENSIONS_VALUE_VALUE_TUPLE = 7;
+
+// for MessageValue Proto
+const int FIELD_ID_FIELD_VALUE_IN_MESSAGE_VALUE_PROTO = 1;
+
+void writeDimensionsValueProtoToStream(const DimensionsValue& dimensionsValue,
+                                       ProtoOutputStream* protoOutput) {
+    protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, dimensionsValue.field());
+    switch (dimensionsValue.value_case()) {
+        case DimensionsValue::ValueCase::kValueStr:
+            protoOutput->write(FIELD_TYPE_STRING | DIMENSIONS_VALUE_VALUE_STR,
+                dimensionsValue.value_str());
+            break;
+        case DimensionsValue::ValueCase::kValueInt:
+            protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_VALUE_INT,
+                dimensionsValue.value_int());
+            break;
+        case DimensionsValue::ValueCase::kValueLong:
+            protoOutput->write(FIELD_TYPE_INT64 | DIMENSIONS_VALUE_VALUE_LONG,
+                dimensionsValue.value_long());
+            break;
+        case DimensionsValue::ValueCase::kValueBool:
+            protoOutput->write(FIELD_TYPE_BOOL | DIMENSIONS_VALUE_VALUE_BOOL,
+                dimensionsValue.value_bool());
+            break;
+        case DimensionsValue::ValueCase::kValueFloat:
+            protoOutput->write(FIELD_TYPE_FLOAT | DIMENSIONS_VALUE_VALUE_FLOAT,
+                dimensionsValue.value_float());
+            break;
+        case DimensionsValue::ValueCase::kValueTuple:
+            {
+                long long tupleToken = protoOutput->start(
+                    FIELD_TYPE_MESSAGE | DIMENSIONS_VALUE_VALUE_TUPLE);
+                for (int i = 0; i < dimensionsValue.value_tuple().dimensions_value_size(); ++i) {
+                    long long token = protoOutput->start(
+                        FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED
+                        | FIELD_ID_FIELD_VALUE_IN_MESSAGE_VALUE_PROTO);
+                    writeDimensionsValueProtoToStream(
+                        dimensionsValue.value_tuple().dimensions_value(i), protoOutput);
+                    protoOutput->end(token);
+                }
+                protoOutput->end(tupleToken);
+            }
+            break;
+        default:
+            break;
+    }
+}
+
+// for Field Proto
+const int FIELD_FIELD = 1;
+const int FIELD_POSITION_INDEX = 2;
+const int FIELD_CHILD = 3;
+
+void writeFieldProtoToStream(
+    const Field& field, util::ProtoOutputStream* protoOutput) {
+    protoOutput->write(FIELD_TYPE_INT32 | FIELD_FIELD, field.field());
+    if (field.has_position_index()) {
+      protoOutput->write(FIELD_TYPE_INT32 | FIELD_POSITION_INDEX, field.position_index());
+    }
+    for (int i = 0; i < field.child_size(); ++i) {
+        long long childToken = protoOutput->start(
+            FIELD_TYPE_MESSAGE| FIELD_COUNT_REPEATED | FIELD_CHILD);
+        writeFieldProtoToStream(field.child(i), protoOutput);
+        protoOutput->end(childToken);
+    }
+}
+
+namespace {
+
+void addOrUpdateChildrenMap(
+    const Field& root,
+    const Field& node,
+    std::map<Field, std::set<Field, FieldCmp>, FieldCmp> *childrenMap) {
+    Field parentNode = root;
+    if (node.has_position_index()) {
+        appendLeaf(&parentNode, node.field(), node.position_index());
+    } else {
+        appendLeaf(&parentNode, node.field());
+    }
+    if (childrenMap->find(parentNode) == childrenMap->end()) {
+        childrenMap->insert(std::make_pair(parentNode, std::set<Field, FieldCmp>{}));
+    }
+    auto it = childrenMap->find(parentNode);
+    for (int i = 0; i < node.child_size(); ++i) {
+        auto child = node.child(i);
+        Field childNode = parentNode;
+        if (child.has_position_index()) {
+            appendLeaf(&childNode, child.field(), child.position_index());
+        } else {
+            appendLeaf(&childNode, child.field());
+        }
+        it->second.insert(childNode);
+        addOrUpdateChildrenMap(parentNode, child, childrenMap);
+    }
+}
+
+void addOrUpdateChildrenMap(
+    const Field& field,
+    std::map<Field, std::set<Field, FieldCmp>, FieldCmp> *childrenMap) {
+    Field root;
+    addOrUpdateChildrenMap(root, field, childrenMap);
+}
+
+} // namespace
+
+void writeFieldValueTreeToStream(const FieldValueMap &fieldValueMap,
+                                 util::ProtoOutputStream* protoOutput) {
+    std::map<Field, std::set<Field, FieldCmp>, FieldCmp> childrenMap;
+    // Rebuild the field tree.
+    for (auto it = fieldValueMap.begin(); it != fieldValueMap.end(); ++it) {
+        addOrUpdateChildrenMap(it->first, &childrenMap);
+    }
+    std::stack<std::pair<long long, Field>> tokenStack;
+    // Iterate over the node tree to fill the Atom proto.
+    for (auto it = childrenMap.begin(); it != childrenMap.end(); ++it) {
+        const Field* nodeLeaf = getSingleLeaf(&it->first);
+        const int fieldNum = nodeLeaf->field();
+        while (!tokenStack.empty()) {
+            auto currentMsgNode = tokenStack.top().second;
+            auto currentMsgNodeChildrenIt = childrenMap.find(currentMsgNode);
+            if (currentMsgNodeChildrenIt->second.find(it->first) ==
+                currentMsgNodeChildrenIt->second.end()) {
+                protoOutput->end(tokenStack.top().first);
+                tokenStack.pop();
+            } else {
+                break;
+            }
+        }
+        if (it->second.size() == 0) {
+            auto itValue = fieldValueMap.find(it->first);
+            if (itValue != fieldValueMap.end()) {
+                const DimensionsValue& value = itValue->second;
+                switch (value.value_case()) {
+                        case DimensionsValue::ValueCase::kValueStr:
+                            protoOutput->write(FIELD_TYPE_STRING | fieldNum,
+                                value.value_str());
+                            break;
+                        case DimensionsValue::ValueCase::kValueInt:
+                            protoOutput->write(FIELD_TYPE_INT32 | fieldNum,
+                                value.value_int());
+                            break;
+                        case DimensionsValue::ValueCase::kValueLong:
+                            protoOutput->write(FIELD_TYPE_INT64 | fieldNum,
+                                value.value_long());
+                            break;
+                        case DimensionsValue::ValueCase::kValueBool:
+                            protoOutput->write(FIELD_TYPE_BOOL | fieldNum,
+                                value.value_bool());
+                            break;
+                        case DimensionsValue::ValueCase::kValueFloat:
+                            protoOutput->write(FIELD_TYPE_FLOAT | fieldNum,
+                                value.value_float());
+                            break;
+                        // This would not happen as the node has no child.
+                        case DimensionsValue::ValueCase::kValueTuple:
+                            break;
+                        default:
+                            break;
+                }
+            } else {
+                ALOGE("Leaf node value not found. This should never happen.");
+            }
+        } else {
+            long long token;
+            if (nodeLeaf->has_position_index()) {
+                token = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | fieldNum);
+            } else {
+                token = protoOutput->start(FIELD_TYPE_MESSAGE | fieldNum);
+            }
+            tokenStack.push(std::make_pair(token, it->first));
+        }
+    }
+
+    while (!tokenStack.empty()) {
+        protoOutput->end(tokenStack.top().first);
+        tokenStack.pop();
+    }
+
+
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/stats_log_util.h b/cmds/statsd/src/stats_log_util.h
new file mode 100644
index 0000000..1f81860
--- /dev/null
+++ b/cmds/statsd/src/stats_log_util.h
@@ -0,0 +1,41 @@
+/*
+ * 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 <android/util/ProtoOutputStream.h>
+#include "frameworks/base/cmds/statsd/src/stats_log.pb.h"
+#include "field_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Helper function to write DimensionsValue proto to ProtoOutputStream.
+void writeDimensionsValueProtoToStream(
+    const DimensionsValue& fieldValue, util::ProtoOutputStream* protoOutput);
+
+// Helper function to write Field proto to ProtoOutputStream.
+void writeFieldProtoToStream(
+    const Field& field, util::ProtoOutputStream* protoOutput);
+
+// Helper function to construct the field value tree and write to ProtoOutputStream
+void writeFieldValueTreeToStream(const FieldValueMap &fieldValueMap,
+    util::ProtoOutputStream* protoOutput);
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/stats_util.h b/cmds/statsd/src/stats_util.h
index 1cdf031..e46b8eb 100644
--- a/cmds/statsd/src/stats_util.h
+++ b/cmds/statsd/src/stats_util.h
@@ -27,45 +27,15 @@
 namespace os {
 namespace statsd {
 
-const HashableDimensionKey DEFAULT_DIMENSION_KEY = HashableDimensionKey(vector<KeyValuePair>());
+const HashableDimensionKey DEFAULT_DIMENSION_KEY = HashableDimensionKey();
 
 // Minimum bucket size in seconds
 const long kMinBucketSizeSec = 5 * 60;
 
-typedef std::map<std::string, HashableDimensionKey> ConditionKey;
+typedef std::map<std::string, std::vector<HashableDimensionKey>> ConditionKey;
 
 typedef std::unordered_map<HashableDimensionKey, int64_t> DimToValMap;
 
-/*
- * In memory rep for LogEvent. Uses much less memory than LogEvent
- */
-typedef struct EventKV {
-    std::vector<KeyValuePair> kv;
-    string ToString() const {
-        std::ostringstream result;
-        result << "{ ";
-        const size_t N = kv.size();
-        for (size_t i = 0; i < N; i++) {
-            result << " ";
-            result << (i + 1);
-            result << "->";
-            const auto& pair = kv[i];
-            if (pair.has_value_int()) {
-                result << pair.value_int();
-            } else if (pair.has_value_long()) {
-                result << pair.value_long();
-            } else if (pair.has_value_float()) {
-                result << pair.value_float();
-            } else if (pair.has_value_str()) {
-                result << pair.value_str().c_str();
-            }
-        }
-        result << " }";
-        return result.str();
-    }
-} EventKV;
-
-typedef std::unordered_map<HashableDimensionKey, std::shared_ptr<EventKV>> DimToEventKVMap;
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index 4729f6a..1ed1e05 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -22,30 +22,52 @@
 option java_package = "com.android.internal.os";
 option java_outer_classname = "StatsdConfigProto";
 
-message KeyMatcher {
-    optional int32 key = 1;
-
-    optional bool as_package_name = 2 [default = false];
+enum Position {
+    POSITION_UNKNOWN = 0;
+    FIRST = 1;
+    LAST = 2;
+    ANY = 3;
 }
 
-message KeyValueMatcher {
-    optional KeyMatcher key_matcher = 1;
+message FieldMatcher {
+    optional int32 field = 1;
+
+    optional Position position = 2;
+
+    repeated FieldMatcher child = 3;
+}
+
+message FieldValueMatcher {
+    // Field id, as specified in the atom proto message.
+    optional int32 field = 1;
+
+    // For repeated fields, specifies the position in the array.
+    // FIRST and LAST mean that if the values are found at the first
+    // or last position, it's a match. ANY means that if the values are found
+    // anywhere in the array, then it's a match.
+    optional Position position = 2;
 
     oneof value_matcher {
-        bool eq_bool = 2;
-        string eq_string = 3;
-        int32 eq_int = 4;
+        bool eq_bool = 3;
+        string eq_string = 4;
+        int32 eq_int = 5;
 
-        int64 lt_int = 5;
-        int64 gt_int = 6;
-        float lt_float = 7;
-        float gt_float = 8;
+        int64 lt_int = 6;
+        int64 gt_int = 7;
+        float lt_float = 8;
+        float gt_float = 9;
 
-        int64 lte_int = 9;
-        int64 gte_int = 10;
+        int64 lte_int = 10;
+        int64 gte_int = 11;
+
+        MessageMatcher matches_tuple = 12;
     }
 }
 
+message MessageMatcher {
+    repeated FieldValueMatcher field_value_matcher = 1;
+}
+
 enum LogicalOperation {
     LOGICAL_OPERATION_UNSPECIFIED = 0;
     AND = 1;
@@ -56,9 +78,9 @@
 }
 
 message SimpleAtomMatcher {
-    optional int32 tag = 1;
+    optional int32 atom_id = 1;
 
-    repeated KeyValueMatcher key_value_matcher = 2;
+    repeated FieldValueMatcher field_value_matcher = 2;
 }
 
 message AtomMatcher {
@@ -90,7 +112,7 @@
     }
     optional InitialValue initial_value = 5 [default = FALSE];
 
-    repeated KeyMatcher dimension = 6;
+    optional FieldMatcher dimensions = 6;
 }
 
 message Predicate {
@@ -115,14 +137,14 @@
 message MetricConditionLink {
     optional string condition = 1;
 
-    repeated KeyMatcher key_in_what = 2;
+    optional FieldMatcher dimensions_in_what = 2;
 
-    repeated KeyMatcher key_in_condition = 3;
+    optional FieldMatcher dimensions_in_condition = 3;
 }
 
 message FieldFilter {
-    optional bool include_all = 1;
-    repeated int32 field_num = 2;
+    optional bool include_all = 1 [default = false];
+    optional FieldMatcher fields = 2;
 }
 
 message EventMetric {
@@ -142,7 +164,7 @@
 
     optional string condition = 3;
 
-    repeated KeyMatcher dimension = 4;
+    optional FieldMatcher dimensions = 4;
 
     optional Bucket bucket = 5;
 
@@ -165,7 +187,7 @@
     }
     optional AggregationType aggregation_type = 5 [default = SUM];
 
-    repeated KeyMatcher dimension = 6;
+    optional FieldMatcher dimensions = 6;
 
     optional Bucket bucket = 7;
 }
@@ -175,11 +197,11 @@
 
     optional string what = 2;
 
-    optional FieldFilter gauge_fields = 3;
+    optional FieldFilter gauge_fields_filter = 3;
 
     optional string condition = 4;
 
-    repeated KeyMatcher dimension = 5;
+    optional FieldMatcher dimensions = 5;
 
     optional Bucket bucket = 6;
 
@@ -195,7 +217,7 @@
 
     optional string condition = 4;
 
-    repeated KeyMatcher dimension = 5;
+    optional FieldMatcher dimensions = 5;
 
     optional Bucket bucket = 6;
 
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
index 1ec9155..111b4ba 100644
--- a/cmds/statsd/tests/LogEntryMatcher_test.cpp
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -32,65 +32,313 @@
 const int FIELD_ID_2 = 2;
 const int FIELD_ID_3 = 2;
 
+const int ATTRIBUTION_UID_FIELD_ID = 1;
+const int ATTRIBUTION_TAG_FIELD_ID = 2;
+
 // Private API from liblog.
 extern "C" void android_log_rewind(android_log_context ctx);
 
 #ifdef __ANDROID__
 TEST(AtomMatcherTest, TestSimpleMatcher) {
+    UidMap uidMap;
+
     // Set up the matcher
     AtomMatcher matcher;
     auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-    simpleMatcher->set_tag(TAG_ID);
+    simpleMatcher->set_atom_id(TAG_ID);
 
     LogEvent event(TAG_ID, 0);
+    EXPECT_TRUE(event.write(11));
     event.init();
 
     // Test
-    EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // Wrong tag id.
+    simpleMatcher->set_atom_id(TAG_ID + 1);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
 }
 
-TEST(AtomMatcherTest, TestBoolMatcher) {
-    // Set up the matcher
-    AtomMatcher matcher;
-    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-    simpleMatcher->set_tag(TAG_ID);
-    auto keyValue1 = simpleMatcher->add_key_value_matcher();
-    keyValue1->mutable_key_matcher()->set_key(FIELD_ID_1);
-    auto keyValue2 = simpleMatcher->add_key_value_matcher();
-    keyValue2->mutable_key_matcher()->set_key(FIELD_ID_2);
+TEST(AtomMatcherTest, TestAttributionMatcher) {
+    UidMap uidMap;
+    AttributionNode attribution_node1;
+    attribution_node1.set_uid(1111);
+    attribution_node1.set_tag("location1");
+
+    AttributionNode attribution_node2;
+    attribution_node2.set_uid(2222);
+    attribution_node2.set_tag("location2");
+
+    AttributionNode attribution_node3;
+    attribution_node3.set_uid(3333);
+    attribution_node3.set_tag("location3");
+    std::vector<AttributionNode> attribution_nodes =
+        { attribution_node1, attribution_node2, attribution_node3 };
 
     // Set up the event
     LogEvent event(TAG_ID, 0);
-    event.write(true);
-    event.write(false);
+    event.write(attribution_nodes);
+    event.write("some value");
+    // Convert to a LogEvent
+    event.init();
+
+    // Set up the matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+    simpleMatcher->set_atom_id(TAG_ID);
+
+    // Match first node.
+    auto attributionMatcher = simpleMatcher->add_field_value_matcher();
+    attributionMatcher->set_field(FIELD_ID_1);
+    attributionMatcher->set_position(Position::FIRST);
+    attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
+        ATTRIBUTION_TAG_FIELD_ID);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string("tag");
+
+    auto fieldMatcher = simpleMatcher->add_field_value_matcher();
+    fieldMatcher->set_field(FIELD_ID_2);
+    fieldMatcher->set_eq_string("some value");
+
+    // Tag not matched.
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("location3");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("location1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // Match last node.
+    attributionMatcher->set_position(Position::LAST);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("location3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // Match any node.
+    attributionMatcher->set_position(Position::ANY);
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("location1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("location2");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("location3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("location4");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // Attribution match but primitive field not match.
+    attributionMatcher->set_position(Position::ANY);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("location2");
+    fieldMatcher->set_eq_string("wrong value");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    fieldMatcher->set_eq_string("some value");
+
+    // Uid match.
+    attributionMatcher->set_position(Position::ANY);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_field(
+        ATTRIBUTION_UID_FIELD_ID);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)->set_eq_string("pkg0");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    uidMap.updateMap({1111, 1111, 2222, 3333, 3333} /* uid list */,
+        {1, 1, 2, 1, 2} /* version list */,
+        {android::String16("pkg0"), android::String16("pkg1"),
+         android::String16("pkg1"), android::String16("Pkg2"),
+         android::String16("PkG3")} /* package name list */);
+
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg2");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg0");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    attributionMatcher->set_position(Position::FIRST);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg0");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg3");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg2");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    attributionMatcher->set_position(Position::LAST);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg0");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg2");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg1");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    // Uid + tag.
+    attributionMatcher->set_position(Position::ANY);
+    attributionMatcher->mutable_matches_tuple()->add_field_value_matcher()->set_field(
+        ATTRIBUTION_TAG_FIELD_ID);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg0");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+        ->set_eq_string("location1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg1");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+        ->set_eq_string("location1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg1");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+        ->set_eq_string("location2");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg2");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+        ->set_eq_string("location3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg3");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+        ->set_eq_string("location3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg3");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+        ->set_eq_string("location1");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    attributionMatcher->set_position(Position::FIRST);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg0");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+        ->set_eq_string("location1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg1");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+        ->set_eq_string("location1");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg1");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+        ->set_eq_string("location2");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg2");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+        ->set_eq_string("location3");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg3");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+        ->set_eq_string("location3");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg3");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+        ->set_eq_string("location1");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+
+    attributionMatcher->set_position(Position::LAST);
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg0");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+        ->set_eq_string("location1");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg1");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+        ->set_eq_string("location1");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg1");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+        ->set_eq_string("location2");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg2");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+        ->set_eq_string("location3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg3");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+        ->set_eq_string("location3");
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(0)
+        ->set_eq_string("pkg3");
+    attributionMatcher->mutable_matches_tuple()->mutable_field_value_matcher(1)
+        ->set_eq_string("location1");
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
+}
+
+TEST(AtomMatcherTest, TestBoolMatcher) {
+    UidMap uidMap;
+    // Set up the matcher
+    AtomMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_atom_matcher();
+    simpleMatcher->set_atom_id(TAG_ID);
+    auto keyValue1 = simpleMatcher->add_field_value_matcher();
+    keyValue1->set_field(FIELD_ID_1);
+    auto keyValue2 = simpleMatcher->add_field_value_matcher();
+    keyValue2->set_field(FIELD_ID_2);
+
+    // Set up the event
+    LogEvent event(TAG_ID, 0);
+    EXPECT_TRUE(event.write(true));
+    EXPECT_TRUE(event.write(false));
     // Convert to a LogEvent
     event.init();
 
     // Test
     keyValue1->set_eq_bool(true);
     keyValue2->set_eq_bool(false);
-    EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
 
     keyValue1->set_eq_bool(false);
     keyValue2->set_eq_bool(false);
-    EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
 
-    keyValue1->set_eq_bool(true);
-    keyValue2->set_eq_bool(false);
-    EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+    keyValue1->set_eq_bool(false);
+    keyValue2->set_eq_bool(true);
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
 
     keyValue1->set_eq_bool(true);
     keyValue2->set_eq_bool(true);
-    EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
 }
 
 TEST(AtomMatcherTest, TestStringMatcher) {
+    UidMap uidMap;
     // Set up the matcher
     AtomMatcher matcher;
     auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-    simpleMatcher->set_tag(TAG_ID);
-    auto keyValue = simpleMatcher->add_key_value_matcher();
-    keyValue->mutable_key_matcher()->set_key(FIELD_ID_1);
+    simpleMatcher->set_atom_id(TAG_ID);
+    auto keyValue = simpleMatcher->add_field_value_matcher();
+    keyValue->set_field(FIELD_ID_1);
     keyValue->set_eq_string("some value");
 
     // Set up the event
@@ -100,18 +348,19 @@
     event.init();
 
     // Test
-    EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
 }
 
 TEST(AtomMatcherTest, TestMultiFieldsMatcher) {
+    UidMap uidMap;
     // Set up the matcher
     AtomMatcher matcher;
     auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-    simpleMatcher->set_tag(TAG_ID);
-    auto keyValue1 = simpleMatcher->add_key_value_matcher();
-    keyValue1->mutable_key_matcher()->set_key(FIELD_ID_1);
-    auto keyValue2 = simpleMatcher->add_key_value_matcher();
-    keyValue2->mutable_key_matcher()->set_key(FIELD_ID_2);
+    simpleMatcher->set_atom_id(TAG_ID);
+    auto keyValue1 = simpleMatcher->add_field_value_matcher();
+    keyValue1->set_field(FIELD_ID_1);
+    auto keyValue2 = simpleMatcher->add_field_value_matcher();
+    keyValue2->set_field(FIELD_ID_2);
 
     // Set up the event
     LogEvent event(TAG_ID, 0);
@@ -124,25 +373,26 @@
     // Test
     keyValue1->set_eq_int(2);
     keyValue2->set_eq_int(3);
-    EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
 
     keyValue1->set_eq_int(2);
     keyValue2->set_eq_int(4);
-    EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
 
     keyValue1->set_eq_int(4);
     keyValue2->set_eq_int(3);
-    EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
 }
 
 TEST(AtomMatcherTest, TestIntComparisonMatcher) {
+    UidMap uidMap;
     // Set up the matcher
     AtomMatcher matcher;
     auto simpleMatcher = matcher.mutable_simple_atom_matcher();
 
-    simpleMatcher->set_tag(TAG_ID);
-    auto keyValue = simpleMatcher->add_key_value_matcher();
-    keyValue->mutable_key_matcher()->set_key(FIELD_ID_1);
+    simpleMatcher->set_atom_id(TAG_ID);
+    auto keyValue = simpleMatcher->add_field_value_matcher();
+    keyValue->set_field(FIELD_ID_1);
 
     // Set up the event
     LogEvent event(TAG_ID, 0);
@@ -153,82 +403,83 @@
 
     // eq_int
     keyValue->set_eq_int(10);
-    EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
     keyValue->set_eq_int(11);
-    EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
     keyValue->set_eq_int(12);
-    EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
 
     // lt_int
     keyValue->set_lt_int(10);
-    EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
     keyValue->set_lt_int(11);
-    EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
     keyValue->set_lt_int(12);
-    EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
 
     // lte_int
     keyValue->set_lte_int(10);
-    EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
     keyValue->set_lte_int(11);
-    EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
     keyValue->set_lte_int(12);
-    EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
 
     // gt_int
     keyValue->set_gt_int(10);
-    EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
     keyValue->set_gt_int(11);
-    EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
     keyValue->set_gt_int(12);
-    EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
 
     // gte_int
     keyValue->set_gte_int(10);
-    EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
     keyValue->set_gte_int(11);
-    EXPECT_TRUE(matchesSimple(*simpleMatcher, event));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event));
     keyValue->set_gte_int(12);
-    EXPECT_FALSE(matchesSimple(*simpleMatcher, event));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event));
 }
 
 TEST(AtomMatcherTest, TestFloatComparisonMatcher) {
+    UidMap uidMap;
     // Set up the matcher
     AtomMatcher matcher;
     auto simpleMatcher = matcher.mutable_simple_atom_matcher();
-    simpleMatcher->set_tag(TAG_ID);
+    simpleMatcher->set_atom_id(TAG_ID);
 
-    auto keyValue = simpleMatcher->add_key_value_matcher();
-    keyValue->mutable_key_matcher()->set_key(FIELD_ID_1);
+    auto keyValue = simpleMatcher->add_field_value_matcher();
+    keyValue->set_field(FIELD_ID_1);
 
     LogEvent event1(TAG_ID, 0);
     keyValue->set_lt_float(10.0);
     event1.write(10.1f);
     event1.init();
-    EXPECT_FALSE(matchesSimple(*simpleMatcher, event1));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event1));
 
     LogEvent event2(TAG_ID, 0);
     event2.write(9.9f);
     event2.init();
-    EXPECT_TRUE(matchesSimple(*simpleMatcher, event2));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event2));
 
     LogEvent event3(TAG_ID, 0);
     event3.write(10.1f);
     event3.init();
     keyValue->set_gt_float(10.0);
-    EXPECT_TRUE(matchesSimple(*simpleMatcher, event3));
+    EXPECT_TRUE(matchesSimple(uidMap, *simpleMatcher, event3));
 
     LogEvent event4(TAG_ID, 0);
     event4.write(9.9f);
     event4.init();
-    EXPECT_FALSE(matchesSimple(*simpleMatcher, event4));
+    EXPECT_FALSE(matchesSimple(uidMap, *simpleMatcher, event4));
 }
 
 // Helper for the composite matchers.
 void addSimpleMatcher(SimpleAtomMatcher* simpleMatcher, int tag, int key, int val) {
-    simpleMatcher->set_tag(tag);
-    auto keyValue = simpleMatcher->add_key_value_matcher();
-    keyValue->mutable_key_matcher()->set_key(key);
+    simpleMatcher->set_atom_id(tag);
+    auto keyValue = simpleMatcher->add_field_value_matcher();
+    keyValue->set_field(key);
     keyValue->set_eq_int(val);
 }
 
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
new file mode 100644
index 0000000..fd28460
--- /dev/null
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -0,0 +1,573 @@
+// 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 <gtest/gtest.h>
+#include <log/log_event_list.h>
+#include "src/logd/LogEvent.h"
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+TEST(LogEventTest, testEmptyEvent) {
+    const int32_t TAG_ID = 123;
+    LogEvent event(TAG_ID, 0);
+    event.init();
+
+    DimensionsValue dimensionsValue;
+    EXPECT_FALSE(event.GetSimpleAtomDimensionsValueProto(234, &dimensionsValue));
+    FieldMatcher dimensions;
+    dimensions.set_field(event.GetTagId());
+    EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue));
+
+    dimensions.add_child()->set_field(3);
+    dimensions.mutable_child(0)->set_position(Position::FIRST);
+    EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue));
+
+    dimensions.mutable_child(0)->set_position(Position::ANY);
+    EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue));
+
+    dimensions.mutable_child(0)->set_position(Position::LAST);
+    EXPECT_FALSE(event.GetAtomDimensionsValueProto(dimensions, &dimensionsValue));
+}
+
+TEST(LogEventTest, testRepeatedAttributionNode) {
+    const int32_t TAG_ID = 123;
+    LogEvent event(TAG_ID, 0);
+    AttributionNode attribution_node1;
+    attribution_node1.set_uid(1111);
+    attribution_node1.set_tag("locationService");
+
+    AttributionNode attribution_node2;
+    attribution_node2.set_uid(2222);
+    attribution_node2.set_tag("locationService2");
+
+    AttributionNode attribution_node3;
+    attribution_node3.set_uid(3333);
+    attribution_node3.set_tag("locationService3");
+    std::vector<AttributionNode> attribution_nodes =
+        {attribution_node1, attribution_node2, attribution_node3};
+
+    // 1nd field: int32.
+    EXPECT_TRUE(event.write(int32_t(11)));
+    // 2rd field: float.
+    EXPECT_TRUE(event.write(3.45f));
+    // Here it assume that the atom proto contains a repeated AttributionNode field.
+    // 3rd field: attribution node. This is repeated field.
+    EXPECT_TRUE(event.write(attribution_nodes));
+    // 4th field: bool.
+    EXPECT_TRUE(event.write(true));
+    // 5th field: long.
+    EXPECT_TRUE(event.write(uint64_t(1234)));
+
+    event.init();
+
+    DimensionsValue dimensionsValue;
+    // Query single primitive fields.
+    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(1, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), 11);
+
+    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(2, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 2);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_float(), 3.45f);
+
+    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(4, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4);
+    // The bool value is stored in value_int field as logD does not support bool.
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), true);
+
+    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(5, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 5);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_long(), long(1234));
+
+    // First attribution.
+    FieldMatcher first_uid_dimensions;
+    first_uid_dimensions.set_field(event.GetTagId());
+    first_uid_dimensions.add_child()->set_field(3);
+    first_uid_dimensions.mutable_child(0)->set_position(Position::FIRST);
+    first_uid_dimensions.mutable_child(0)->add_child()->set_field(1);
+    EXPECT_TRUE(event.GetAtomDimensionsValueProto(first_uid_dimensions, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).field(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).value_int(), 1111);
+
+    FieldMatcher first_tag_dimensions = first_uid_dimensions;
+    first_tag_dimensions.mutable_child(0)->mutable_child(0)->set_field(2);
+    EXPECT_TRUE(event.GetAtomDimensionsValueProto(first_tag_dimensions, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).field(), 2);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).value_str(), "locationService");
+
+    FieldMatcher first_attribution_dimensions = first_uid_dimensions;
+    first_attribution_dimensions.mutable_child(0)->add_child()->set_field(2);
+    EXPECT_TRUE(event.GetAtomDimensionsValueProto(first_attribution_dimensions, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value_size(), 2);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).field(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).value_int(), 1111);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(1).field(), 2);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(1).value_str(), "locationService");
+
+    FieldMatcher last_attribution_dimensions = first_attribution_dimensions;
+    last_attribution_dimensions.mutable_child(0)->set_position(Position::LAST);
+    EXPECT_TRUE(event.GetAtomDimensionsValueProto(last_attribution_dimensions, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value_size(), 2);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).field(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).value_int(), 3333);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(1).field(), 2);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(1).value_str(), "locationService3");
+
+    FieldMatcher any_attribution_dimensions = first_attribution_dimensions;
+    any_attribution_dimensions.mutable_child(0)->set_position(Position::ANY);
+    std::vector<DimensionsValue> dimensionsValues;
+    event.GetAtomDimensionsValueProtos(any_attribution_dimensions, &dimensionsValues);
+    EXPECT_EQ(dimensionsValues.size(), 3u);
+    EXPECT_EQ(dimensionsValues[0].field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).field(), 3);
+    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value_size(), 2);
+    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).field(), 1);
+    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).value_int(), 1111);
+    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(1).field(), 2);
+    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(1).value_str(), "locationService");
+    EXPECT_EQ(dimensionsValues[1].field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).field(), 3);
+    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value_size(), 2);
+    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).field(), 1);
+    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).value_int(), 2222);
+    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(1).field(), 2);
+    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(1).value_str(), "locationService2");
+    EXPECT_EQ(dimensionsValues[2].field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).field(), 3);
+    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value_size(), 2);
+    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).field(), 1);
+    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).value_int(), 3333);
+    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(1).field(), 2);
+    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(1).value_str(), "locationService3");
+
+    FieldMatcher mixed_dimensions = any_attribution_dimensions;
+    mixed_dimensions.add_child()->set_field(1000);
+    mixed_dimensions.add_child()->set_field(6); // missing field.
+    mixed_dimensions.add_child()->set_field(3); // position not set.
+    mixed_dimensions.add_child()->set_field(5);
+    mixed_dimensions.add_child()->set_field(1);
+    dimensionsValues.clear();
+    event.GetAtomDimensionsValueProtos(mixed_dimensions, &dimensionsValues);
+    EXPECT_EQ(dimensionsValues.size(), 3u);
+    EXPECT_EQ(dimensionsValues[0].field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value_size(), 3);
+    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).field(), 3);
+    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 2);
+    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(),
+              1111);
+    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).field(), 2);
+    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).value_str(),
+              "locationService");
+    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(1).field(), 5);
+    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(1).value_long(), long(1234));
+    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(2).field(), 1);
+    EXPECT_EQ(dimensionsValues[0].value_tuple().dimensions_value(2).value_int(), 11);
+
+    EXPECT_EQ(dimensionsValues[1].field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value_size(), 3);
+    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).field(), 3);
+    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 2);
+    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(),
+              2222);
+    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).field(), 2);
+    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).value_str(),
+              "locationService2");
+    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(1).field(), 5);
+    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(1).value_long(), long(1234));
+    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(2).field(), 1);
+    EXPECT_EQ(dimensionsValues[1].value_tuple().dimensions_value(2).value_int(), 11);
+
+    EXPECT_EQ(dimensionsValues[2].field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value_size(), 3);
+    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).field(), 3);
+    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value_size(), 2);
+    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(0).value_int(),
+              3333);
+    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).field(), 2);
+    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(0).value_tuple().dimensions_value(1).value_str(),
+              "locationService3");
+    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(1).field(), 5);
+    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(1).value_long(), long(1234));
+    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(2).field(), 1);
+    EXPECT_EQ(dimensionsValues[2].value_tuple().dimensions_value(2).value_int(), 11);
+
+    FieldMatcher wrong_dimensions = mixed_dimensions;
+    // Wrong tagId.
+    wrong_dimensions.set_field(event.GetTagId() + 100);
+    dimensionsValues.clear();
+    event.GetAtomDimensionsValueProtos(wrong_dimensions, &dimensionsValues);
+    EXPECT_TRUE(dimensionsValues.empty());
+}
+
+TEST(LogEventTest, testMessageField) {
+    const int32_t TAG_ID = 123;
+    LogEvent event(TAG_ID, 0);
+    AttributionNode attribution_node1;
+    attribution_node1.set_uid(1111);
+    attribution_node1.set_tag("locationService");
+
+    AttributionNode attribution_node2;
+    attribution_node2.set_uid(2222);
+    attribution_node2.set_tag("locationService2");
+
+    // 1nd field: int32.
+    EXPECT_TRUE(event.write(int32_t(11)));
+    // 2rd field: float.
+    EXPECT_TRUE(event.write(3.45f));
+    // Here it assume that the atom proto contains two optional AttributionNode fields.
+    // 3rd field: attribution node. This is not repeated field.
+    EXPECT_TRUE(event.write(attribution_node1));
+    // 4th field: another attribution field. This is not repeated field.
+    EXPECT_TRUE(event.write(attribution_node2));
+    // 5th field: bool.
+    EXPECT_TRUE(event.write(true));
+    // 6th field: long.
+    EXPECT_TRUE(event.write(uint64_t(1234)));
+
+    event.init();
+
+    FieldMatcher uid_dimensions1;
+    uid_dimensions1.set_field(event.GetTagId());
+    uid_dimensions1.add_child()->set_field(3);
+    uid_dimensions1.mutable_child(0)->add_child()->set_field(1);
+
+    FieldMatcher tag_dimensions1;
+    tag_dimensions1.set_field(event.GetTagId());
+    tag_dimensions1.add_child()->set_field(3);
+    tag_dimensions1.mutable_child(0)->add_child()->set_field(2);
+
+    FieldMatcher attribution_dimensions1;
+    attribution_dimensions1.set_field(event.GetTagId());
+    attribution_dimensions1.add_child()->set_field(3);
+    attribution_dimensions1.mutable_child(0)->add_child()->set_field(1);
+    attribution_dimensions1.mutable_child(0)->add_child()->set_field(2);
+
+    FieldMatcher uid_dimensions2 = uid_dimensions1;
+    uid_dimensions2.mutable_child(0)->set_field(4);
+
+    FieldMatcher tag_dimensions2 = tag_dimensions1;
+    tag_dimensions2.mutable_child(0)->set_field(4);
+
+    FieldMatcher attribution_dimensions2 = attribution_dimensions1;
+    attribution_dimensions2.mutable_child(0)->set_field(4);
+
+    FieldMatcher mixed_dimensions = attribution_dimensions1;
+    mixed_dimensions.add_child()->set_field(4);
+    mixed_dimensions.mutable_child(1)->add_child()->set_field(1);
+    mixed_dimensions.add_child()->set_field(1000);
+    mixed_dimensions.add_child()->set_field(5);
+    mixed_dimensions.add_child()->set_field(1);
+
+    DimensionsValue dimensionsValue;
+
+    // Query single primitive fields.
+    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(1, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), 11);
+
+    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(2, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 2);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_float(), 3.45f);
+
+    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(5, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 5);
+    // The bool value is stored in value_int field as logD does not support bool.
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), true);
+
+    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(6, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 6);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_long(), long(1234));
+
+    // Query atom field 3: attribution node uid field only.
+    EXPECT_TRUE(event.GetAtomDimensionsValueProto(uid_dimensions1, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).field(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).value_int(), 1111);
+
+    // Query atom field 3: attribution node tag field only.
+    EXPECT_TRUE(event.GetAtomDimensionsValueProto(tag_dimensions1, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).field(), 2);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).value_str(), "locationService");
+
+    // Query atom field 3: attribution node uid + tag fields.
+    EXPECT_TRUE(event.GetAtomDimensionsValueProto(attribution_dimensions1, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value_size(), 2);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).field(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).value_int(), 1111);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(1).field(), 2);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(1).value_str(), "locationService");
+
+    // Query atom field 4: attribution node uid field only.
+    EXPECT_TRUE(event.GetAtomDimensionsValueProto(uid_dimensions2, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).field(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).value_int(), 2222);
+
+    // Query atom field 4: attribution node tag field only.
+    EXPECT_TRUE(event.GetAtomDimensionsValueProto(tag_dimensions2, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).field(), 2);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).value_str(), "locationService2");
+
+    // Query atom field 4: attribution node uid + tag fields.
+    EXPECT_TRUE(event.GetAtomDimensionsValueProto(attribution_dimensions2, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value_size(), 2);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).field(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).value_int(), 2222);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(1).field(), 2);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(1).value_str(), "locationService2");
+
+    // Query multiple fields:
+    // 1/ Field 3: attribution uid + tag.
+    // 2/ Field 4: attribution uid only.
+    // 3/ Field not exist.
+    // 4/ Primitive fields #5
+    // 5/ Primitive fields #1
+    EXPECT_TRUE(event.GetAtomDimensionsValueProto(mixed_dimensions, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 4);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value_size(), 2);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).field(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(0).value_int(), 1111);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(1).field(), 2);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_tuple()
+        .dimensions_value(1).value_str(), "locationService");
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).field(), 4);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).value_tuple()
+        .dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).value_tuple()
+        .dimensions_value(0).field(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(1).value_tuple()
+        .dimensions_value(0).value_int(), 2222);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(2).field(), 5);
+    // The bool value is stored in value_int field as logD does not support bool.
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(2).value_int(), true);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(3).field(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(3).value_int(), 11);
+}
+
+TEST(LogEventTest, testAllPrimitiveFields) {
+    const int32_t TAG_ID = 123;
+    LogEvent event(TAG_ID, 0);
+
+    // 1nd field: int32.
+    EXPECT_TRUE(event.write(int32_t(11)));
+    // 2rd field: float.
+    EXPECT_TRUE(event.write(3.45f));
+    // 3th field: string.
+    EXPECT_TRUE(event.write("test"));
+    // 4th field: bool.
+    EXPECT_TRUE(event.write(true));
+    // 5th field: bool.
+    EXPECT_TRUE(event.write(false));
+    // 6th field: long.
+    EXPECT_TRUE(event.write(uint64_t(1234)));
+
+    event.init();
+
+    DimensionsValue dimensionsValue;
+    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(1, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), 11);
+
+    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(2, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 2);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_float(), 3.45f);
+
+    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(3, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 3);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_str(), "test");
+
+    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(4, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 4);
+    // The bool value is stored in value_int field as logD does not support bool.
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), true);
+
+    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(5, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 5);
+    // The bool value is stored in value_int field as logD does not support bool.
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_int(), false);
+
+    EXPECT_TRUE(event.GetSimpleAtomDimensionsValueProto(6, &dimensionsValue));
+    EXPECT_EQ(dimensionsValue.field(), event.GetTagId());
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).field(), 6);
+    EXPECT_EQ(dimensionsValue.value_tuple().dimensions_value(0).value_long(), long(1234));
+
+    // Field not exist.
+    EXPECT_FALSE(event.GetSimpleAtomDimensionsValueProto(7, &dimensionsValue));
+}
+
+TEST(LogEventTest, testWriteAtomProtoToStream) {
+    AttributionNode attribution_node1;
+    attribution_node1.set_uid(1111);
+    attribution_node1.set_tag("locationService");
+
+    AttributionNode attribution_node2;
+    attribution_node2.set_uid(2222);
+    attribution_node2.set_tag("locationService2");
+
+    AttributionNode attribution_node3;
+    attribution_node3.set_uid(3333);
+    attribution_node3.set_tag("locationService3");
+    std::vector<AttributionNode> attribution_nodes =
+        {attribution_node1, attribution_node2, attribution_node3};
+
+    LogEvent event(1, 0);
+    EXPECT_TRUE(event.write("222"));
+    EXPECT_TRUE(event.write(attribution_nodes));
+    EXPECT_TRUE(event.write(345));
+    EXPECT_TRUE(event.write(attribution_node3));
+    EXPECT_TRUE(event.write("hello"));
+    event.init();
+
+    util::ProtoOutputStream protoOutput;
+    // For now only see whether it will crash.
+    // TODO(yanglu): test parsing from stream.
+    event.ToProto(protoOutput);
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
\ No newline at end of file
diff --git a/cmds/statsd/tests/MetricsManager_test.cpp b/cmds/statsd/tests/MetricsManager_test.cpp
index 3c8ccab..6cd31dd 100644
--- a/cmds/statsd/tests/MetricsManager_test.cpp
+++ b/cmds/statsd/tests/MetricsManager_test.cpp
@@ -52,20 +52,20 @@
     eventMatcher->set_name("SCREEN_IS_ON");
 
     SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
-    simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_field_value_matcher()->set_field(
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int(
+    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
             2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
 
     eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("SCREEN_IS_OFF");
 
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
-    simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_field_value_matcher()->set_field(
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int(
+    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
 
     eventMatcher = config.add_atom_matcher();
@@ -80,8 +80,8 @@
     metric->set_name("3");
     metric->set_what("SCREEN_IS_ON");
     metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
-    KeyMatcher* keyMatcher = metric->add_dimension();
-    keyMatcher->set_key(1);
+    metric->mutable_dimensions()->set_field(2 /*SCREEN_STATE_CHANGE*/);
+    metric->mutable_dimensions()->add_child()->set_field(1);
 
     auto alert = config.add_alert();
     alert->set_name("3");
@@ -100,10 +100,10 @@
     eventMatcher->set_name("SCREEN_IS_ON");
 
     SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
-    simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_field_value_matcher()->set_field(
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int(
+    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
             2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
 
     eventMatcher = config.add_atom_matcher();
@@ -129,8 +129,8 @@
     metric->set_name("3");
     metric->set_what("SCREEN_IS_ON");
     metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
-    KeyMatcher* keyMatcher = metric->add_dimension();
-    keyMatcher->set_key(1);
+    metric->mutable_dimensions()->set_field(2 /*SCREEN_STATE_CHANGE*/);
+    metric->mutable_dimensions()->add_child()->set_field(1);
 
     auto alert = config.add_alert();
     alert->set_name("3");
@@ -149,10 +149,10 @@
     eventMatcher->set_name("SCREEN_IS_ON");
 
     SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
-    simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_field_value_matcher()->set_field(
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int(
+    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
             2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
 
     eventMatcher = config.add_atom_matcher();
@@ -181,7 +181,7 @@
     eventMatcher->set_name("SCREEN_EVENT");
 
     SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_tag(2);
+    simpleAtomMatcher->set_atom_id(2);
 
     return config;
 }
@@ -193,12 +193,12 @@
     AtomMatcher* eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("BATTERY_VERY_LOW");
     SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_tag(2);
+    simpleAtomMatcher->set_atom_id(2);
 
     eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("BATTERY_VERY_VERY_LOW");
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_tag(3);
+    simpleAtomMatcher->set_atom_id(3);
 
     eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("BATTERY_LOW");
@@ -213,8 +213,8 @@
     metric->set_name("3");
     metric->set_what("BATTERY_LOW");
     metric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
-    KeyMatcher* keyMatcher = metric->add_dimension();
-    keyMatcher->set_key(1);
+    // This case is interesting. We want to dimension across two atoms.
+    metric->mutable_dimensions()->add_child()->set_field(1);
 
     auto alert = config.add_alert();
     alert->set_name("3");
@@ -233,20 +233,20 @@
     eventMatcher->set_name("SCREEN_IS_ON");
 
     SimpleAtomMatcher* simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
-    simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_field_value_matcher()->set_field(
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int(
+    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
             2 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_ON*/);
 
     eventMatcher = config.add_atom_matcher();
     eventMatcher->set_name("SCREEN_IS_OFF");
 
     simpleAtomMatcher = eventMatcher->mutable_simple_atom_matcher();
-    simpleAtomMatcher->set_tag(2 /*SCREEN_STATE_CHANGE*/);
-    simpleAtomMatcher->add_key_value_matcher()->mutable_key_matcher()->set_key(
+    simpleAtomMatcher->set_atom_id(2 /*SCREEN_STATE_CHANGE*/);
+    simpleAtomMatcher->add_field_value_matcher()->set_field(
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE*/);
-    simpleAtomMatcher->mutable_key_value_matcher(0)->set_eq_int(
+    simpleAtomMatcher->mutable_field_value_matcher(0)->set_eq_int(
             1 /*SCREEN_STATE_CHANGE__DISPLAY_STATE__STATE_OFF*/);
 
     auto condition = config.add_predicate();
@@ -267,6 +267,7 @@
 }
 
 TEST(MetricsManagerTest, TestGoodConfig) {
+    UidMap uidMap;
     StatsdConfig config = buildGoodConfig();
     set<int> allTagIds;
     vector<sp<LogMatchingTracker>> allAtomMatchers;
@@ -277,7 +278,7 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
-    EXPECT_TRUE(initStatsdConfig(kConfigKey, config, timeBaseSec,  allTagIds, allAtomMatchers,
+    EXPECT_TRUE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
                                  allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                  conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
     EXPECT_EQ(1u, allMetricProducers.size());
@@ -285,6 +286,7 @@
 }
 
 TEST(MetricsManagerTest, TestDimensionMetricsWithMultiTags) {
+    UidMap uidMap;
     StatsdConfig config = buildDimensionMetricsWithMultiTags();
     set<int> allTagIds;
     vector<sp<LogMatchingTracker>> allAtomMatchers;
@@ -295,12 +297,13 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers,
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
 
 TEST(MetricsManagerTest, TestCircleLogMatcherDependency) {
+    UidMap uidMap;
     StatsdConfig config = buildCircleMatchers();
     set<int> allTagIds;
     vector<sp<LogMatchingTracker>> allAtomMatchers;
@@ -311,12 +314,13 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers,
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
 
 TEST(MetricsManagerTest, TestMissingMatchers) {
+    UidMap uidMap;
     StatsdConfig config = buildMissingMatchers();
     set<int> allTagIds;
     vector<sp<LogMatchingTracker>> allAtomMatchers;
@@ -326,12 +330,13 @@
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers,
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
 
 TEST(MetricsManagerTest, TestMissingPredicate) {
+    UidMap uidMap;
     StatsdConfig config = buildMissingPredicate();
     set<int> allTagIds;
     vector<sp<LogMatchingTracker>> allAtomMatchers;
@@ -341,12 +346,13 @@
     unordered_map<int, std::vector<int>> conditionToMetricMap;
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers,
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
 
 TEST(MetricsManagerTest, TestCirclePredicateDependency) {
+    UidMap uidMap;
     StatsdConfig config = buildCirclePredicates();
     set<int> allTagIds;
     vector<sp<LogMatchingTracker>> allAtomMatchers;
@@ -357,12 +363,13 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers,
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
 
 TEST(MetricsManagerTest, testAlertWithUnknownMetric) {
+    UidMap uidMap;
     StatsdConfig config = buildAlertWithUnknownMetric();
     set<int> allTagIds;
     vector<sp<LogMatchingTracker>> allAtomMatchers;
@@ -373,7 +380,7 @@
     unordered_map<int, std::vector<int>> trackerToMetricMap;
     unordered_map<int, std::vector<int>> trackerToConditionMap;
 
-    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, timeBaseSec, allTagIds, allAtomMatchers,
+    EXPECT_FALSE(initStatsdConfig(kConfigKey, config, uidMap, timeBaseSec, allTagIds, allAtomMatchers,
                                   allConditionTrackers, allMetricProducers, allAnomalyTrackers,
                                   conditionToMetricMap, trackerToMetricMap, trackerToConditionMap));
 }
diff --git a/cmds/statsd/tests/StatsLogProcessor_test.cpp b/cmds/statsd/tests/StatsLogProcessor_test.cpp
index 9b96bb7..a27bed5 100644
--- a/cmds/statsd/tests/StatsLogProcessor_test.cpp
+++ b/cmds/statsd/tests/StatsLogProcessor_test.cpp
@@ -52,7 +52,7 @@
     sp<UidMap> m = new UidMap();
     sp<AnomalyMonitor> anomalyMonitor;
     // Construct the processor with a dummy sendBroadcast function that does nothing.
-    StatsLogProcessor p(m, anomalyMonitor, [](const ConfigKey& key) {});
+    StatsLogProcessor p(m, anomalyMonitor, 0, [](const ConfigKey& key) {});
 
     MockMetricsManager mockMetricsManager;
 
@@ -69,7 +69,7 @@
     sp<UidMap> m = new UidMap();
     sp<AnomalyMonitor> anomalyMonitor;
     int broadcastCount = 0;
-    StatsLogProcessor p(m, anomalyMonitor,
+    StatsLogProcessor p(m, anomalyMonitor, 0,
                         [&broadcastCount](const ConfigKey& key) { broadcastCount++; });
 
     MockMetricsManager mockMetricsManager;
@@ -93,7 +93,7 @@
     sp<UidMap> m = new UidMap();
     sp<AnomalyMonitor> anomalyMonitor;
     int broadcastCount = 0;
-    StatsLogProcessor p(m, anomalyMonitor,
+    StatsLogProcessor p(m, anomalyMonitor, 0,
                         [&broadcastCount](const ConfigKey& key) { broadcastCount++; });
 
     MockMetricsManager mockMetricsManager;
diff --git a/cmds/statsd/tests/UidMap_test.cpp b/cmds/statsd/tests/UidMap_test.cpp
index 3fa96d3..8a394f7 100644
--- a/cmds/statsd/tests/UidMap_test.cpp
+++ b/cmds/statsd/tests/UidMap_test.cpp
@@ -37,7 +37,7 @@
     sp<UidMap> m = new UidMap();
     sp<AnomalyMonitor> anomalyMonitor;
     // Construct the processor with a dummy sendBroadcast function that does nothing.
-    StatsLogProcessor p(m, anomalyMonitor, [](const ConfigKey& key) {});
+    StatsLogProcessor p(m, anomalyMonitor, 0, [](const ConfigKey& key) {});
     LogEvent addEvent(android::util::ISOLATED_UID_CHANGED, 1);
     addEvent.write(100);  // parent UID
     addEvent.write(101);  // isolated UID
@@ -273,4 +273,4 @@
 
 }  // namespace statsd
 }  // namespace os
-}  // namespace android
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
index a016054..da872ad 100644
--- a/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
+++ b/cmds/statsd/tests/anomaly/AnomalyTracker_test.cpp
@@ -34,14 +34,10 @@
 const ConfigKey kConfigKey(0, "test");
 
 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);
+    DimensionsValue dimensionsValue;
+    dimensionsValue.set_field(key);
+    dimensionsValue.set_value_str(value);
+    return HashableDimensionKey(dimensionsValue);
 }
 
 void AddValueToBucket(const std::vector<std::pair<HashableDimensionKey, long>>& key_value_pair_list,
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index eb0fafe..705804f 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -17,6 +17,7 @@
 #include <gtest/gtest.h>
 #include <stdio.h>
 #include <vector>
+#include <numeric>
 
 using std::map;
 using std::unordered_map;
@@ -30,15 +31,22 @@
 
 const ConfigKey kConfigKey(0, "test");
 
+const int ATTRIBUTION_NODE_FIELD_ID = 1;
+const int ATTRIBUTION_UID_FIELD_ID = 1;
+const int TAG_ID = 1;
+
 SimplePredicate getWakeLockHeldCondition(bool countNesting, bool defaultFalse,
-                                         bool outputSlicedUid) {
+                                         bool outputSlicedUid, Position position) {
     SimplePredicate simplePredicate;
     simplePredicate.set_start("WAKE_LOCK_ACQUIRE");
     simplePredicate.set_stop("WAKE_LOCK_RELEASE");
     simplePredicate.set_stop_all("RELEASE_ALL");
     if (outputSlicedUid) {
-        KeyMatcher* keyMatcher = simplePredicate.add_dimension();
-        keyMatcher->set_key(1);
+        simplePredicate.mutable_dimensions()->set_field(TAG_ID);
+        simplePredicate.mutable_dimensions()->add_child()->set_field(ATTRIBUTION_NODE_FIELD_ID);
+        simplePredicate.mutable_dimensions()->mutable_child(0)->set_position(position);
+        simplePredicate.mutable_dimensions()->mutable_child(0)->add_child()->set_field(
+            ATTRIBUTION_UID_FIELD_ID);
     }
 
     simplePredicate.set_count_nesting(countNesting);
@@ -47,24 +55,55 @@
     return simplePredicate;
 }
 
-void makeWakeLockEvent(LogEvent* event, int uid, const string& wl, int acquire) {
-    event->write(uid);  // uid
+void writeAttributionNodesToEvent(LogEvent* event, const std::vector<int> &uids) {
+    std::vector<AttributionNode> nodes;
+    for (size_t i = 0; i < uids.size(); ++i) {
+        AttributionNode node;
+        node.set_uid(uids[i]);
+        nodes.push_back(node);
+    }
+    event->write(nodes);  // attribution chain.
+}
+
+void makeWakeLockEvent(
+        LogEvent* event, const std::vector<int> &uids, const string& wl, int acquire) {
+    writeAttributionNodesToEvent(event, uids);
     event->write(wl);
     event->write(acquire);
     event->init();
 }
 
-map<string, HashableDimensionKey> getWakeLockQueryKey(int key, int uid,
-                                                      const string& conditionName) {
-    // test query
-    KeyValuePair kv1;
-    kv1.set_key(key);
-    kv1.set_value_int(uid);
-    vector<KeyValuePair> kv_list;
-    kv_list.push_back(kv1);
-    map<string, HashableDimensionKey> queryKey;
-    queryKey[conditionName] = HashableDimensionKey(kv_list);
-    return queryKey;
+std::map<string, std::vector<HashableDimensionKey>> getWakeLockQueryKey(
+    const Position position,
+    const std::vector<int> &uids, const string& conditionName) {
+    std::map<string, std::vector<HashableDimensionKey>>  outputKeyMap;
+    std::vector<int> uid_indexes;
+    switch(position) {
+        case Position::FIRST:
+            uid_indexes.push_back(0);
+            break;
+        case Position::LAST:
+            uid_indexes.push_back(uids.size() - 1);
+            break;
+        case Position::ANY:
+            uid_indexes.resize(uids.size());
+            std::iota(uid_indexes.begin(), uid_indexes.end(), 0);
+            break;
+        default:
+            break;
+    }
+
+    for (const int idx : uid_indexes) {
+        DimensionsValue dimensionsValue;
+        dimensionsValue.set_field(TAG_ID);
+        dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(ATTRIBUTION_NODE_FIELD_ID);
+        dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)
+            ->mutable_value_tuple()->add_dimensions_value()->set_field(ATTRIBUTION_NODE_FIELD_ID);
+        dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)
+            ->mutable_value_tuple()->mutable_dimensions_value(0)->set_value_int(uids[idx]);
+        outputKeyMap[conditionName].push_back(HashableDimensionKey(dimensionsValue));
+    }
+    return outputKeyMap;
 }
 
 TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) {
@@ -221,91 +260,115 @@
 }
 
 TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
-    SimplePredicate simplePredicate = getWakeLockHeldCondition(
-            true /*nesting*/, true /*default to false*/, true /*output slice by uid*/);
-    string conditionName = "WL_HELD_BY_UID2";
+    for (Position position :
+            { Position::ANY, Position::FIRST, Position::LAST}) {
+        SimplePredicate simplePredicate = getWakeLockHeldCondition(
+                true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
+                position);
+        string conditionName = "WL_HELD_BY_UID2";
 
-    unordered_map<string, int> trackerNameIndexMap;
-    trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
-    trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
-    trackerNameIndexMap["RELEASE_ALL"] = 2;
+        unordered_map<string, int> trackerNameIndexMap;
+        trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
+        trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
+        trackerNameIndexMap["RELEASE_ALL"] = 2;
 
-    SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
-                                            0 /*condition tracker index*/, simplePredicate,
-                                            trackerNameIndexMap);
-    int uid = 111;
+        SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
+                                                0 /*condition tracker index*/, simplePredicate,
+                                                trackerNameIndexMap);
+        std::vector<int> uids = {111, 222, 333};
 
-    LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
-    makeWakeLockEvent(&event, uid, "wl1", 1);
+        LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+        makeWakeLockEvent(&event, uids, "wl1", 1);
 
-    // one matched start
-    vector<MatchingState> matcherState;
-    matcherState.push_back(MatchingState::kMatched);
-    matcherState.push_back(MatchingState::kNotMatched);
-    matcherState.push_back(MatchingState::kNotMatched);
-    vector<sp<ConditionTracker>> allPredicates;
-    vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
-    vector<bool> changedCache(1, false);
+        // one matched start
+        vector<MatchingState> matcherState;
+        matcherState.push_back(MatchingState::kMatched);
+        matcherState.push_back(MatchingState::kNotMatched);
+        matcherState.push_back(MatchingState::kNotMatched);
+        vector<sp<ConditionTracker>> allPredicates;
+        vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+        vector<bool> changedCache(1, false);
 
-    conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
-                                       changedCache);
+        conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
+                                           changedCache);
 
-    EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
-    EXPECT_TRUE(changedCache[0]);
+        if (position == Position::FIRST ||
+            position == Position::LAST) {
+            EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+        } else {
+            EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
+        }
+        EXPECT_TRUE(changedCache[0]);
 
-    // Now test query
-    const auto queryKey = getWakeLockQueryKey(1, uid, conditionName);
-    conditionCache[0] = ConditionState::kNotEvaluated;
+        // Now test query
+        const auto queryKey = getWakeLockQueryKey(position, uids, conditionName);
+        conditionCache[0] = ConditionState::kNotEvaluated;
 
-    conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
-    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+        conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+        EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
 
-    // another wake lock acquired by this uid
-    LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
-    makeWakeLockEvent(&event2, uid, "wl2", 1);
-    matcherState.clear();
-    matcherState.push_back(MatchingState::kMatched);
-    matcherState.push_back(MatchingState::kNotMatched);
-    conditionCache[0] = ConditionState::kNotEvaluated;
-    changedCache[0] = false;
-    conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
-                                       changedCache);
-    EXPECT_FALSE(changedCache[0]);
+        // another wake lock acquired by this uid
+        LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
+        makeWakeLockEvent(&event2, uids, "wl2", 1);
+        matcherState.clear();
+        matcherState.push_back(MatchingState::kMatched);
+        matcherState.push_back(MatchingState::kNotMatched);
+        conditionCache[0] = ConditionState::kNotEvaluated;
+        changedCache[0] = false;
+        conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
+                                           changedCache);
+        EXPECT_FALSE(changedCache[0]);
+        if (position == Position::FIRST ||
+            position == Position::LAST) {
+            EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+        } else {
+            EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
+        }
 
-    // wake lock 1 release
-    LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
-    makeWakeLockEvent(&event3, uid, "wl1", 0);  // now release it.
-    matcherState.clear();
-    matcherState.push_back(MatchingState::kNotMatched);
-    matcherState.push_back(MatchingState::kMatched);
-    conditionCache[0] = ConditionState::kNotEvaluated;
-    changedCache[0] = false;
-    conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
-                                       changedCache);
-    // nothing changes, because wake lock 2 is still held for this uid
-    EXPECT_FALSE(changedCache[0]);
+        // wake lock 1 release
+        LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
+        makeWakeLockEvent(&event3, uids, "wl1", 0);  // now release it.
+        matcherState.clear();
+        matcherState.push_back(MatchingState::kNotMatched);
+        matcherState.push_back(MatchingState::kMatched);
+        conditionCache[0] = ConditionState::kNotEvaluated;
+        changedCache[0] = false;
+        conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
+                                           changedCache);
+        // nothing changes, because wake lock 2 is still held for this uid
+        EXPECT_FALSE(changedCache[0]);
+        if (position == Position::FIRST ||
+            position == Position::LAST) {
+            EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+        } else {
+            EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
+        }
 
-    LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
-    makeWakeLockEvent(&event4, uid, "wl2", 0);  // now release it.
-    matcherState.clear();
-    matcherState.push_back(MatchingState::kNotMatched);
-    matcherState.push_back(MatchingState::kMatched);
-    conditionCache[0] = ConditionState::kNotEvaluated;
-    changedCache[0] = false;
-    conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache,
-                                       changedCache);
-    EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
-    EXPECT_TRUE(changedCache[0]);
+        LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
+        makeWakeLockEvent(&event4, uids, "wl2", 0);  // now release it.
+        matcherState.clear();
+        matcherState.push_back(MatchingState::kNotMatched);
+        matcherState.push_back(MatchingState::kMatched);
+        conditionCache[0] = ConditionState::kNotEvaluated;
+        changedCache[0] = false;
+        conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache,
+                                           changedCache);
+        EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+        EXPECT_TRUE(changedCache[0]);
 
-    // query again
-    conditionCache[0] = ConditionState::kNotEvaluated;
-    conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
-    EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+        // query again
+        conditionCache[0] = ConditionState::kNotEvaluated;
+        conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+        EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+
+    }
+
 }
 
 TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
     SimplePredicate simplePredicate = getWakeLockHeldCondition(
-            true /*nesting*/, true /*default to false*/, false /*slice output by uid*/);
+            true /*nesting*/, true /*default to false*/, false /*slice output by uid*/,
+            Position::ANY /* position */);
     string conditionName = "WL_HELD";
 
     unordered_map<string, int> trackerNameIndexMap;
@@ -316,13 +379,14 @@
     SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
                                             0 /*condition tracker index*/, simplePredicate,
                                             trackerNameIndexMap);
-    int uid1 = 111;
+
+    std::vector<int> uid_list1 = {111, 1111, 11111};
     string uid1_wl1 = "wl1_1";
-    int uid2 = 222;
+    std::vector<int> uid_list2 = {222, 2222, 22222};
     string uid2_wl1 = "wl2_1";
 
     LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
-    makeWakeLockEvent(&event, uid1, uid1_wl1, 1);
+    makeWakeLockEvent(&event, uid_list1, uid1_wl1, 1);
 
     // one matched start for uid1
     vector<MatchingState> matcherState;
@@ -340,7 +404,7 @@
     EXPECT_TRUE(changedCache[0]);
 
     // Now test query
-    map<string, HashableDimensionKey> queryKey;
+    ConditionKey queryKey;
     conditionCache[0] = ConditionState::kNotEvaluated;
 
     conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
@@ -348,7 +412,7 @@
 
     // another wake lock acquired by this uid
     LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
-    makeWakeLockEvent(&event2, uid2, uid2_wl1, 1);
+    makeWakeLockEvent(&event2, uid_list2, uid2_wl1, 1);
     matcherState.clear();
     matcherState.push_back(MatchingState::kMatched);
     matcherState.push_back(MatchingState::kNotMatched);
@@ -360,7 +424,7 @@
 
     // uid1 wake lock 1 release
     LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
-    makeWakeLockEvent(&event3, uid1, uid1_wl1, 0);  // now release it.
+    makeWakeLockEvent(&event3, uid_list1, uid1_wl1, 0);  // now release it.
     matcherState.clear();
     matcherState.push_back(MatchingState::kNotMatched);
     matcherState.push_back(MatchingState::kMatched);
@@ -372,7 +436,7 @@
     EXPECT_FALSE(changedCache[0]);
 
     LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
-    makeWakeLockEvent(&event4, uid2, uid2_wl1, 0);  // now release it.
+    makeWakeLockEvent(&event4, uid_list2, uid2_wl1, 0);  // now release it.
     matcherState.clear();
     matcherState.push_back(MatchingState::kNotMatched);
     matcherState.push_back(MatchingState::kMatched);
@@ -390,95 +454,111 @@
 }
 
 TEST(SimpleConditionTrackerTest, TestStopAll) {
-    SimplePredicate simplePredicate = getWakeLockHeldCondition(
-            true /*nesting*/, true /*default to false*/, true /*output slice by uid*/);
-    string conditionName = "WL_HELD_BY_UID3";
+    for (Position position :
+            {Position::ANY, Position::FIRST, Position::LAST}) {
+        SimplePredicate simplePredicate = getWakeLockHeldCondition(
+                true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
+                position);
+        string conditionName = "WL_HELD_BY_UID3";
 
-    unordered_map<string, int> trackerNameIndexMap;
-    trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
-    trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
-    trackerNameIndexMap["RELEASE_ALL"] = 2;
+        unordered_map<string, int> trackerNameIndexMap;
+        trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
+        trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
+        trackerNameIndexMap["RELEASE_ALL"] = 2;
 
-    SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
-                                            0 /*condition tracker index*/, simplePredicate,
-                                            trackerNameIndexMap);
-    int uid1 = 111;
-    int uid2 = 222;
+        SimpleConditionTracker conditionTracker(kConfigKey, conditionName,
+                                                0 /*condition tracker index*/, simplePredicate,
+                                                trackerNameIndexMap);
 
-    LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
-    makeWakeLockEvent(&event, uid1, "wl1", 1);
+        std::vector<int> uid_list1 = {111, 1111, 11111};
+        std::vector<int> uid_list2 = {222, 2222, 22222};
 
-    // one matched start
-    vector<MatchingState> matcherState;
-    matcherState.push_back(MatchingState::kMatched);
-    matcherState.push_back(MatchingState::kNotMatched);
-    matcherState.push_back(MatchingState::kNotMatched);
-    vector<sp<ConditionTracker>> allPredicates;
-    vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
-    vector<bool> changedCache(1, false);
+        LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+        makeWakeLockEvent(&event, uid_list1, "wl1", 1);
 
-    conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
-                                       changedCache);
+        // one matched start
+        vector<MatchingState> matcherState;
+        matcherState.push_back(MatchingState::kMatched);
+        matcherState.push_back(MatchingState::kNotMatched);
+        matcherState.push_back(MatchingState::kNotMatched);
+        vector<sp<ConditionTracker>> allPredicates;
+        vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+        vector<bool> changedCache(1, false);
 
-    EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
-    EXPECT_TRUE(changedCache[0]);
+        conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
+                                           changedCache);
+        if (position == Position::FIRST ||
+            position == Position::LAST) {
+            EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+        } else {
+            EXPECT_EQ(uid_list1.size(), conditionTracker.mSlicedConditionState.size());
+        }
+        EXPECT_TRUE(changedCache[0]);
 
-    // Now test query
-    const auto queryKey = getWakeLockQueryKey(1, uid1, conditionName);
-    conditionCache[0] = ConditionState::kNotEvaluated;
+        // Now test query
+        const auto queryKey = getWakeLockQueryKey(position, uid_list1, conditionName);
+        conditionCache[0] = ConditionState::kNotEvaluated;
 
-    conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
-    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+        conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+        EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
 
-    // another wake lock acquired by uid2
-    LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
-    makeWakeLockEvent(&event2, uid2, "wl2", 1);
-    matcherState.clear();
-    matcherState.push_back(MatchingState::kMatched);
-    matcherState.push_back(MatchingState::kNotMatched);
-    matcherState.push_back(MatchingState::kNotMatched);
-    conditionCache[0] = ConditionState::kNotEvaluated;
-    changedCache[0] = false;
-    conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
-                                       changedCache);
-    EXPECT_EQ(2UL, conditionTracker.mSlicedConditionState.size());
-    EXPECT_TRUE(changedCache[0]);
+        // another wake lock acquired by uid2
+        LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
+        makeWakeLockEvent(&event2, uid_list2, "wl2", 1);
+        matcherState.clear();
+        matcherState.push_back(MatchingState::kMatched);
+        matcherState.push_back(MatchingState::kNotMatched);
+        matcherState.push_back(MatchingState::kNotMatched);
+        conditionCache[0] = ConditionState::kNotEvaluated;
+        changedCache[0] = false;
+        conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
+                                           changedCache);
+        if (position == Position::FIRST ||
+            position == Position::LAST) {
+            EXPECT_EQ(2UL, conditionTracker.mSlicedConditionState.size());
+        } else {
+            EXPECT_EQ(uid_list1.size() + uid_list2.size(),
+                      conditionTracker.mSlicedConditionState.size());
+        }
+        EXPECT_TRUE(changedCache[0]);
 
-    // TEST QUERY
-    const auto queryKey2 = getWakeLockQueryKey(1, uid2, conditionName);
-    conditionCache[0] = ConditionState::kNotEvaluated;
+        // TEST QUERY
+        const auto queryKey2 = getWakeLockQueryKey(position, uid_list2, conditionName);
+        conditionCache[0] = ConditionState::kNotEvaluated;
 
-    conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
-    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+        conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+        EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
 
 
-    // stop all event
-    LogEvent event3(2 /*tagId*/, 0 /*timestamp*/);
-    matcherState.clear();
-    matcherState.push_back(MatchingState::kNotMatched);
-    matcherState.push_back(MatchingState::kNotMatched);
-    matcherState.push_back(MatchingState::kMatched);
+        // stop all event
+        LogEvent event3(2 /*tagId*/, 0 /*timestamp*/);
+        matcherState.clear();
+        matcherState.push_back(MatchingState::kNotMatched);
+        matcherState.push_back(MatchingState::kNotMatched);
+        matcherState.push_back(MatchingState::kMatched);
 
-    conditionCache[0] = ConditionState::kNotEvaluated;
-    changedCache[0] = false;
-    conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
-                                       changedCache);
-    EXPECT_TRUE(changedCache[0]);
-    EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+        conditionCache[0] = ConditionState::kNotEvaluated;
+        changedCache[0] = false;
+        conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
+                                           changedCache);
+        EXPECT_TRUE(changedCache[0]);
+        EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
 
-    // TEST QUERY
-    const auto queryKey3 = getWakeLockQueryKey(1, uid1, conditionName);
-    conditionCache[0] = ConditionState::kNotEvaluated;
+        // TEST QUERY
+        const auto queryKey3 = getWakeLockQueryKey(position, uid_list1, conditionName);
+        conditionCache[0] = ConditionState::kNotEvaluated;
 
-    conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
-    EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+        conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+        EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
 
-    // TEST QUERY
-    const auto queryKey4 = getWakeLockQueryKey(1, uid2, conditionName);
-    conditionCache[0] = ConditionState::kNotEvaluated;
+        // TEST QUERY
+        const auto queryKey4 = getWakeLockQueryKey(position, uid_list2, conditionName);
+        conditionCache[0] = ConditionState::kNotEvaluated;
 
-    conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
-    EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+        conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+        EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+    }
+
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
new file mode 100644
index 0000000..c747016
--- /dev/null
+++ b/cmds/statsd/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -0,0 +1,228 @@
+// 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 <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+StatsdConfig CreateStatsdConfig() {
+    StatsdConfig config;
+    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+
+    *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
+    *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
+
+    *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
+    *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
+
+    auto appCrashMatcher = CreateProcessCrashAtomMatcher();
+    *config.add_atom_matcher() = appCrashMatcher;
+
+    auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+
+    auto isSyncingPredicate = CreateIsSyncingPredicate();
+    *isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions() =
+        CreateDimensions(
+            android::util::SYNC_STATE_CHANGED, {1 /* uid field */});
+
+    auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
+    *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
+        CreateDimensions(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
+
+    *config.add_predicate() = screenIsOffPredicate;
+    *config.add_predicate() = isSyncingPredicate;
+    *config.add_predicate() = isInBackgroundPredicate;
+
+    auto combinationPredicate = config.add_predicate();
+    combinationPredicate->set_name("combinationPredicate");
+    combinationPredicate->mutable_combination()->set_operation(LogicalOperation::AND);
+    addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+    addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
+    addPredicateToPredicateCombination(isInBackgroundPredicate, combinationPredicate);
+
+    auto countMetric = config.add_count_metric();
+    countMetric->set_name("AppCrashes");
+    countMetric->set_what(appCrashMatcher.name());
+    countMetric->set_condition(combinationPredicate->name());
+    // The metric is dimensioning by uid only.
+    *countMetric->mutable_dimensions() =
+        CreateDimensions(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1});
+    countMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000LL);
+
+    // Links between crash atom and condition of app is in syncing.
+    auto links = countMetric->add_links();
+    links->set_condition(isSyncingPredicate.name());
+    auto dimensionWhat = links->mutable_dimensions_in_what();
+    dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    dimensionWhat->add_child()->set_field(1);  // uid field.
+    auto dimensionCondition = links->mutable_dimensions_in_condition();
+    dimensionCondition->set_field(android::util::SYNC_STATE_CHANGED);
+    dimensionCondition->add_child()->set_field(1);  // uid field.
+
+    // Links between crash atom and condition of app is in background.
+    links = countMetric->add_links();
+    links->set_condition(isInBackgroundPredicate.name());
+    dimensionWhat = links->mutable_dimensions_in_what();
+    dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    dimensionWhat->add_child()->set_field(1);  // uid field.
+    dimensionCondition = links->mutable_dimensions_in_condition();
+    dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+    dimensionCondition->add_child()->set_field(1);  // uid field.
+    return config;
+}
+
+TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks) {
+    auto config = CreateStatsdConfig();
+    uint64_t bucketStartTimeNs = 10000000000;
+    uint64_t bucketSizeNs = config.count_metric(0).bucket().bucket_size_millis() * 1000 * 1000;
+
+    ConfigKey cfgKey;
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+    int appUid = 123;
+    auto crashEvent1 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 1);
+    auto crashEvent2 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 201);
+    auto crashEvent3= CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 101);
+
+    auto crashEvent4 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 51);
+    auto crashEvent5 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 299);
+    auto crashEvent6 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 2001);
+
+    auto crashEvent7 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 16);
+    auto crashEvent8 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 249);
+
+    auto crashEvent9 = CreateAppCrashEvent(appUid, bucketStartTimeNs + bucketSizeNs + 351);
+    auto crashEvent10 = CreateAppCrashEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 2);
+
+    auto screenTurnedOnEvent =
+        CreateScreenStateChangedEvent(ScreenStateChanged::STATE_ON, bucketStartTimeNs + 2);
+    auto screenTurnedOffEvent =
+        CreateScreenStateChangedEvent(ScreenStateChanged::STATE_OFF, bucketStartTimeNs + 200);
+    auto screenTurnedOnEvent2 =
+        CreateScreenStateChangedEvent(ScreenStateChanged::STATE_ON,
+                                      bucketStartTimeNs + 2 * bucketSizeNs - 100);
+
+    auto syncOnEvent1 =
+        CreateSyncStartEvent(appUid, "ReadEmail", bucketStartTimeNs + 50);
+    auto syncOffEvent1 =
+        CreateSyncEndEvent(appUid, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300);
+    auto syncOnEvent2 =
+        CreateSyncStartEvent(appUid, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000);
+
+    auto moveToBackgroundEvent1 =
+        CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15);
+    auto moveToForegroundEvent1 =
+        CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 250);
+
+    auto moveToBackgroundEvent2 =
+        CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + bucketSizeNs + 350);
+    auto moveToForegroundEvent2 =
+        CreateMoveToForegroundEvent(appUid, bucketStartTimeNs + 2 * bucketSizeNs - 1);
+
+    /*
+                    bucket #1                               bucket #2
+
+
+       |      |   |  |                      |   |          |        |   |   |     (crashEvents)
+    |-------------------------------------|-----------------------------------|---------
+
+             |                                           |                        (MoveToBkground)
+
+                                             |                               |    (MoveToForeground)
+
+                |                                                 |                (SyncIsOn)
+                                                  |                                (SyncIsOff)
+          |                                                               |        (ScreenIsOn)
+                   |                                                               (ScreenIsOff)
+    */
+    std::vector<std::unique_ptr<LogEvent>> events;
+    events.push_back(std::move(crashEvent1));
+    events.push_back(std::move(crashEvent2));
+    events.push_back(std::move(crashEvent3));
+    events.push_back(std::move(crashEvent4));
+    events.push_back(std::move(crashEvent5));
+    events.push_back(std::move(crashEvent6));
+    events.push_back(std::move(crashEvent7));
+    events.push_back(std::move(crashEvent8));
+    events.push_back(std::move(crashEvent9));
+    events.push_back(std::move(crashEvent10));
+    events.push_back(std::move(screenTurnedOnEvent));
+    events.push_back(std::move(screenTurnedOffEvent));
+    events.push_back(std::move(screenTurnedOnEvent2));
+    events.push_back(std::move(syncOnEvent1));
+    events.push_back(std::move(syncOffEvent1));
+    events.push_back(std::move(syncOnEvent2));
+    events.push_back(std::move(moveToBackgroundEvent1));
+    events.push_back(std::move(moveToForegroundEvent1));
+    events.push_back(std::move(moveToBackgroundEvent2));
+    events.push_back(std::move(moveToForegroundEvent2));
+
+    sortLogEventsByTimestamp(&events);
+
+    for (const auto& event : events) {
+        processor->OnLogEvent(*event);
+    }
+    ConfigMetricsReportList reports;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &reports);
+    EXPECT_EQ(reports.reports_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1);
+    auto data = reports.reports(0).metrics(0).count_metrics().data(0);
+    // Validate dimension value.
+    EXPECT_EQ(data.dimension().field(),
+              android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1);
+    // Uid field.
+    EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).value_int(), appUid);
+
+    reports.Clear();
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+    EXPECT_EQ(reports.reports_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info_size(), 2);
+    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(0).count(), 1);
+    EXPECT_EQ(reports.reports(0).metrics(0).count_metrics().data(0).bucket_info(1).count(), 3);
+    data = reports.reports(0).metrics(0).count_metrics().data(0);
+    // Validate dimension value.
+    EXPECT_EQ(data.dimension().field(),
+              android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1);
+    // Uid field.
+    EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).value_int(), appUid);
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
new file mode 100644
index 0000000..8d7b2d5
--- /dev/null
+++ b/cmds/statsd/tests/e2e/WakelockDuration_e2e_test.cpp
@@ -0,0 +1,178 @@
+// 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 <gtest/gtest.h>
+
+#include "src/StatsLogProcessor.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+StatsdConfig CreateStatsdConfig(DurationMetric::AggregationType aggregationType) {
+    StatsdConfig config;
+    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = CreateAcquireWakelockAtomMatcher();
+    *config.add_atom_matcher() = CreateReleaseWakelockAtomMatcher();
+
+    auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+    *config.add_predicate() = screenIsOffPredicate;
+
+    auto holdingWakelockPredicate = CreateHoldingWakelockPredicate();
+    // The predicate is dimensioning by any attribution node and both by uid and tag.
+    *holdingWakelockPredicate.mutable_simple_predicate()->mutable_dimensions() =
+        CreateAttributionUidAndTagDimensions(
+            android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST, Position::LAST});
+    *config.add_predicate() = holdingWakelockPredicate;
+
+    auto durationMetric = config.add_duration_metric();
+    durationMetric->set_name("WakelockDuration");
+    durationMetric->set_what(holdingWakelockPredicate.name());
+    durationMetric->set_condition(screenIsOffPredicate.name());
+    durationMetric->set_aggregation_type(aggregationType);
+    // The metric is dimensioning by first attribution node and only by uid.
+    *durationMetric->mutable_dimensions() =
+        CreateAttributionUidDimensions(
+            android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000LL);
+    return config;
+}
+
+TEST(WakelockDurationE2eTest, TestAggregatedPredicateDimensions) {
+    ConfigKey cfgKey;
+    for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE }) {
+        auto config = CreateStatsdConfig(aggregationType);
+        uint64_t bucketStartTimeNs = 10000000000;
+        uint64_t bucketSizeNs =
+            config.duration_metric(0).bucket().bucket_size_millis() * 1000 * 1000;
+
+        auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+        EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+        EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+        auto screenTurnedOnEvent =
+            CreateScreenStateChangedEvent(ScreenStateChanged::STATE_ON, bucketStartTimeNs + 1);
+        auto screenTurnedOffEvent =
+            CreateScreenStateChangedEvent(ScreenStateChanged::STATE_OFF, bucketStartTimeNs + 200);
+        auto screenTurnedOnEvent2 =
+            CreateScreenStateChangedEvent(ScreenStateChanged::STATE_ON,
+                                          bucketStartTimeNs + bucketSizeNs + 500);
+
+        std::vector<AttributionNode> attributions1 =
+            {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+             CreateAttribution(222, "GMSCoreModule2")};
+
+        std::vector<AttributionNode> attributions2 =
+            {CreateAttribution(111, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+             CreateAttribution(222, "GMSCoreModule2")};
+
+        auto acquireEvent1 = CreateAcquireWakelockEvent(
+            attributions1, "wl1", bucketStartTimeNs + 2);
+        auto acquireEvent2 = CreateAcquireWakelockEvent(
+            attributions2, "wl2", bucketStartTimeNs + bucketSizeNs - 10);
+
+        auto releaseEvent1 = CreateReleaseWakelockEvent(
+            attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 2);
+        auto releaseEvent2 = CreateReleaseWakelockEvent(
+            attributions2, "wl2", bucketStartTimeNs + 2 * bucketSizeNs - 15);
+
+        std::vector<std::unique_ptr<LogEvent>> events;
+
+        events.push_back(std::move(screenTurnedOnEvent));
+        events.push_back(std::move(screenTurnedOffEvent));
+        events.push_back(std::move(screenTurnedOnEvent2));
+        events.push_back(std::move(acquireEvent1));
+        events.push_back(std::move(acquireEvent2));
+        events.push_back(std::move(releaseEvent1));
+        events.push_back(std::move(releaseEvent2));
+
+        sortLogEventsByTimestamp(&events);
+
+        for (const auto& event : events) {
+            processor->OnLogEvent(*event);
+        }
+
+        ConfigMetricsReportList reports;
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs - 1, &reports);
+        EXPECT_EQ(reports.reports_size(), 1);
+        EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+        // Only 1 dimension output. The tag dimension in the predicate has been aggregated.
+        EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+
+        auto data = reports.reports(0).metrics(0).duration_metrics().data(0);
+        // Validate dimension value.
+        EXPECT_EQ(data.dimension().field(),
+                  android::util::WAKELOCK_STATE_CHANGED);
+        EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1);
+        // Attribution field.
+        EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1);
+        // Uid only.
+        EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
+            .value_tuple().dimensions_value_size(), 1);
+        EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
+            .value_tuple().dimensions_value(0).field(), 1);
+        EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
+            .value_tuple().dimensions_value(0).value_int(), 111);
+        // Validate bucket info.
+        EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 1);
+        data = reports.reports(0).metrics(0).duration_metrics().data(0);
+        // The wakelock holding interval starts from the screen off event and to the end of the 1st
+        // bucket.
+        EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(), bucketSizeNs - 200);
+
+        reports.Clear();
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+        EXPECT_EQ(reports.reports_size(), 1);
+        EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+        EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data_size(), 1);
+        // Dump the report after the end of 2nd bucket.
+        EXPECT_EQ(reports.reports(0).metrics(0).duration_metrics().data(0).bucket_info_size(), 2);
+        data = reports.reports(0).metrics(0).duration_metrics().data(0);
+        // Validate dimension value.
+        EXPECT_EQ(data.dimension().field(),
+                  android::util::WAKELOCK_STATE_CHANGED);
+        EXPECT_EQ(data.dimension().value_tuple().dimensions_value_size(), 1);
+        // Attribution field.
+        EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0).field(), 1);
+        // Uid only.
+        EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
+            .value_tuple().dimensions_value_size(), 1);
+        EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
+            .value_tuple().dimensions_value(0).field(), 1);
+        EXPECT_EQ(data.dimension().value_tuple().dimensions_value(0)
+            .value_tuple().dimensions_value(0).value_int(), 111);
+        // Two output buckets.
+        // The wakelock holding interval in the 1st bucket starts from the screen off event and to
+        // the end of the 1st bucket.
+        EXPECT_EQ((unsigned long long)data.bucket_info(0).duration_nanos(),
+            bucketStartTimeNs + bucketSizeNs - (bucketStartTimeNs + 200));
+        // The wakelock holding interval in the 2nd bucket starts at the beginning of the bucket and
+        // ends at the second screen on event.
+        EXPECT_EQ((unsigned long long)data.bucket_info(1).duration_nanos(), 500UL);
+    }
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
index d3269ed..6e114a6 100644
--- a/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/CountMetricProducer_test.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "src/metrics/CountMetricProducer.h"
+#include "src/dimension.h"
 #include "metrics_test_helper.h"
 
 #include <gmock/gmock.h>
@@ -137,26 +138,29 @@
     int64_t bucketStartTimeNs = 10000000000;
     int64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
+    int tagId = 1;
+    int conditionTagId = 2;
+
     CountMetric metric;
     metric.set_name("1");
     metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
     metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON");
     MetricConditionLink* link = metric.add_links();
     link->set_condition("APP_IN_BACKGROUND_PER_UID");
-    link->add_key_in_what()->set_key(1);
-    link->add_key_in_condition()->set_key(2);
+    *link->mutable_dimensions_in_what() = buildSimpleAtomFieldMatcher(tagId, 1);
+    *link->mutable_dimensions_in_condition() = buildSimpleAtomFieldMatcher(conditionTagId, 2);
 
-    LogEvent event1(1, bucketStartTimeNs + 1);
+    LogEvent event1(tagId, bucketStartTimeNs + 1);
     event1.write("111");  // uid
     event1.init();
     ConditionKey key1;
-    key1["APP_IN_BACKGROUND_PER_UID"] = getMockedDimensionKey(2, "111");
+    key1["APP_IN_BACKGROUND_PER_UID"] = {getMockedDimensionKey(conditionTagId, 2, "111")};
 
-    LogEvent event2(1, bucketStartTimeNs + 10);
+    LogEvent event2(tagId, bucketStartTimeNs + 10);
     event2.write("222");  // uid
     event2.init();
     ConditionKey key2;
-    key2["APP_IN_BACKGROUND_PER_UID"] = getMockedDimensionKey(2, "222");
+    key2["APP_IN_BACKGROUND_PER_UID"] = {getMockedDimensionKey(conditionTagId, 2, "222")};
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse));
diff --git a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
index 58a4ac6..8ee94c7 100644
--- a/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/DurationMetricProducer_test.cpp
@@ -52,9 +52,10 @@
     LogEvent event1(tagId, bucketStartTimeNs + 1);
     LogEvent event2(tagId, bucketStartTimeNs + bucketSizeNs + 2);
 
+    FieldMatcher dimensions;
     DurationMetricProducer durationProducer(
             kConfigKey, metric, -1 /*no condition*/, 1 /* start index */, 2 /* stop index */,
-            3 /* stop_all index */, false /*nesting*/, wizard, {}, bucketStartTimeNs);
+            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
 
     durationProducer.onMatchedLogEvent(1 /* start index*/, event1);
     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
@@ -88,9 +89,10 @@
     LogEvent event3(tagId, bucketStartTimeNs + bucketSizeNs + 1);
     LogEvent event4(tagId, bucketStartTimeNs + bucketSizeNs + 3);
 
+    FieldMatcher dimensions;
     DurationMetricProducer durationProducer(
             kConfigKey, metric, 0 /* condition index */, 1 /* start index */, 2 /* stop index */,
-            3 /* stop_all index */, false /*nesting*/, wizard, {}, bucketStartTimeNs);
+            3 /* stop_all index */, false /*nesting*/, wizard, dimensions, bucketStartTimeNs);
     EXPECT_FALSE(durationProducer.mCondition);
     EXPECT_FALSE(durationProducer.isConditionSliced());
 
diff --git a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
index baaac67..0ba1c2f 100644
--- a/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/EventMetricProducer_test.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "src/metrics/EventMetricProducer.h"
+#include "src/dimension.h"
 #include "metrics_test_helper.h"
 
 #include <gmock/gmock.h>
@@ -88,25 +89,28 @@
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
+    int tagId = 1;
+    int conditionTagId = 2;
+
     EventMetric metric;
     metric.set_name("1");
     metric.set_condition("APP_IN_BACKGROUND_PER_UID_AND_SCREEN_ON");
     MetricConditionLink* link = metric.add_links();
     link->set_condition("APP_IN_BACKGROUND_PER_UID");
-    link->add_key_in_what()->set_key(1);
-    link->add_key_in_condition()->set_key(2);
+    *link->mutable_dimensions_in_what() = buildSimpleAtomFieldMatcher(tagId, 1);
+    *link->mutable_dimensions_in_condition() = buildSimpleAtomFieldMatcher(conditionTagId, 2);
 
-    LogEvent event1(1, bucketStartTimeNs + 1);
-    event1.write("111");  // uid
+    LogEvent event1(tagId, bucketStartTimeNs + 1);
+    EXPECT_TRUE(event1.write("111"));
     event1.init();
     ConditionKey key1;
-    key1["APP_IN_BACKGROUND_PER_UID"] = getMockedDimensionKey(2, "111");
+    key1["APP_IN_BACKGROUND_PER_UID"] = {getMockedDimensionKey(conditionTagId, 2, "111")};
 
-    LogEvent event2(1, bucketStartTimeNs + 10);
-    event2.write("222");  // uid
+    LogEvent event2(tagId, bucketStartTimeNs + 10);
+    EXPECT_TRUE(event2.write("222"));
     event2.init();
     ConditionKey key2;
-    key2["APP_IN_BACKGROUND_PER_UID"] = getMockedDimensionKey(2, "222");
+    key2["APP_IN_BACKGROUND_PER_UID"] = {getMockedDimensionKey(conditionTagId, 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 584a6d3..359851f 100644
--- a/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/cmds/statsd/tests/metrics/GaugeMetricProducer_test.cpp
@@ -47,7 +47,11 @@
     GaugeMetric metric;
     metric.set_name(metricName);
     metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
-    metric.mutable_gauge_fields()->add_field_num(2);
+    metric.mutable_gauge_fields_filter()->set_include_all(false);
+    auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
+    gaugeFieldMatcher->set_field(tagId);
+    gaugeFieldMatcher->add_child()->set_field(1);
+    gaugeFieldMatcher->add_child()->set_field(3);
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
@@ -64,30 +68,41 @@
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
     shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-    event->write(tagId);
+    event->write(10);
+    event->write("some value");
     event->write(11);
     event->init();
     allData.push_back(event);
 
     gaugeProducer.onDataPulled(allData);
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-    EXPECT_EQ(11, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
+    auto it = gaugeProducer.mCurrentSlicedBucket->begin()->second->begin();
+    EXPECT_EQ(10, it->second.value_int());
+    it++;
+    EXPECT_EQ(11, it->second.value_int());
     EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
 
     allData.clear();
     std::shared_ptr<LogEvent> event2 =
             std::make_shared<LogEvent>(tagId, bucket3StartTimeNs + 10);
-    event2->write(tagId);
+    event2->write(24);
+    event2->write("some value");
     event2->write(25);
     event2->init();
     allData.push_back(event2);
     gaugeProducer.onDataPulled(allData);
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-    EXPECT_EQ(25, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
+    it = gaugeProducer.mCurrentSlicedBucket->begin()->second->begin();
+    EXPECT_EQ(24, it->second.value_int());
+    it++;
+    EXPECT_EQ(25, it->second.value_int());
     // One dimension.
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(11L, gaugeProducer.mPastBuckets.begin()->second.back().mEvent->kv[0].value_int());
+    it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeFields->begin();
+    EXPECT_EQ(10L, it->second.value_int());
+    it++;
+    EXPECT_EQ(11L, it->second.value_int());
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum);
 
     gaugeProducer.flushIfNeededLocked(bucket4StartTimeNs);
@@ -95,7 +110,10 @@
     // One dimension.
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
     EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(25L, gaugeProducer.mPastBuckets.begin()->second.back().mEvent->kv[0].value_int());
+    it = gaugeProducer.mPastBuckets.begin()->second.back().mGaugeFields->begin();
+    EXPECT_EQ(24L, it->second.value_int());
+    it++;
+    EXPECT_EQ(25L, it->second.value_int());
     EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum);
 }
 
@@ -103,7 +121,9 @@
     GaugeMetric metric;
     metric.set_name(metricName);
     metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
-    metric.mutable_gauge_fields()->add_field_num(2);
+    auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
+    gaugeFieldMatcher->set_field(tagId);
+    gaugeFieldMatcher->add_child()->set_field(2);
     metric.set_condition("SCREEN_ON");
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
@@ -116,7 +136,7 @@
             .WillOnce(Invoke([](int tagId, vector<std::shared_ptr<LogEvent>>* data) {
                 data->clear();
                 shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucketStartTimeNs + 10);
-                event->write(tagId);
+                event->write("some value");
                 event->write(100);
                 event->init();
                 data->push_back(event);
@@ -128,28 +148,32 @@
 
     gaugeProducer.onConditionChanged(true, bucketStartTimeNs + 8);
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-    EXPECT_EQ(100, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
+    EXPECT_EQ(100,
+        gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int());
     EXPECT_EQ(0UL, gaugeProducer.mPastBuckets.size());
 
     vector<shared_ptr<LogEvent>> allData;
     allData.clear();
     shared_ptr<LogEvent> event = make_shared<LogEvent>(tagId, bucket2StartTimeNs + 1);
-    event->write(1);
+    event->write("some value");
     event->write(110);
     event->init();
     allData.push_back(event);
     gaugeProducer.onDataPulled(allData);
 
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-    EXPECT_EQ(110, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
+    EXPECT_EQ(110,
+        gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int());
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
-    EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin()->second.back().mEvent->kv[0].value_int());
+    EXPECT_EQ(100, gaugeProducer.mPastBuckets.begin()->second.back()
+        .mGaugeFields->begin()->second.value_int());
 
     gaugeProducer.onConditionChanged(false, bucket2StartTimeNs + 10);
     gaugeProducer.flushIfNeededLocked(bucket3StartTimeNs + 10);
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.size());
     EXPECT_EQ(2UL, gaugeProducer.mPastBuckets.begin()->second.size());
-    EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin()->second.back().mEvent->kv[0].value_int());
+    EXPECT_EQ(110L, gaugeProducer.mPastBuckets.begin()->second.back()
+        .mGaugeFields->begin()->second.value_int());
     EXPECT_EQ(1UL, gaugeProducer.mPastBuckets.begin()->second.back().mBucketNum);
 }
 
@@ -164,7 +188,9 @@
     GaugeMetric metric;
     metric.set_name(metricName);
     metric.mutable_bucket()->set_bucket_size_millis(bucketSizeNs / 1000000);
-    metric.mutable_gauge_fields()->add_field_num(2);
+    auto gaugeFieldMatcher = metric.mutable_gauge_fields_filter()->mutable_fields();
+    gaugeFieldMatcher->set_field(tagId);
+    gaugeFieldMatcher->add_child()->set_field(2);
     GaugeMetricProducer gaugeProducer(kConfigKey, metric, -1 /*-1 meaning no condition*/, wizard,
                                       tagId, tagId, bucketStartTimeNs, pullerManager);
 
@@ -175,46 +201,50 @@
     alert.set_number_of_buckets(2);
     sp<AnomalyTracker> anomalyTracker = gaugeProducer.addAnomalyTracker(alert);
 
-    std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(1, bucketStartTimeNs + 1);
-    event1->write(1);
+    int tagId = 1;
+    std::shared_ptr<LogEvent> event1 = std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 1);
+    event1->write("some value");
     event1->write(13);
     event1->init();
 
     gaugeProducer.onDataPulled({event1});
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-    EXPECT_EQ(13L, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
+    EXPECT_EQ(13L,
+        gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int());
     EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), -1LL);
 
     std::shared_ptr<LogEvent> event2 =
-            std::make_shared<LogEvent>(1, bucketStartTimeNs + bucketSizeNs + 10);
-    event2->write(1);
+            std::make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 10);
+    event2->write("some value");
     event2->write(15);
     event2->init();
 
     gaugeProducer.onDataPulled({event2});
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-    EXPECT_EQ(15L, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
+    EXPECT_EQ(15L,
+        gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int());
     EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), (long long)event2->GetTimestampNs());
 
     std::shared_ptr<LogEvent> event3 =
-            std::make_shared<LogEvent>(1, bucketStartTimeNs + 2 * bucketSizeNs + 10);
-    event3->write(1);
+            std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 2 * bucketSizeNs + 10);
+    event3->write("some value");
     event3->write(24);
     event3->init();
 
     gaugeProducer.onDataPulled({event3});
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-    EXPECT_EQ(24L, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
+    EXPECT_EQ(24L,
+        gaugeProducer.mCurrentSlicedBucket->begin()->second->begin()->second.value_int());
     EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), (long long)event3->GetTimestampNs());
 
     // The event4 does not have the gauge field. Thus the current bucket value is 0.
     std::shared_ptr<LogEvent> event4 =
-            std::make_shared<LogEvent>(1, bucketStartTimeNs + 3 * bucketSizeNs + 10);
-    event4->write(1);
+            std::make_shared<LogEvent>(tagId, bucketStartTimeNs + 3 * bucketSizeNs + 10);
+    event4->write("some value");
     event4->init();
     gaugeProducer.onDataPulled({event4});
     EXPECT_EQ(1UL, gaugeProducer.mCurrentSlicedBucket->size());
-    EXPECT_EQ(0, gaugeProducer.mCurrentSlicedBucket->begin()->second->kv[0].value_int());
+    EXPECT_TRUE(gaugeProducer.mCurrentSlicedBucket->begin()->second->empty());
     EXPECT_EQ(anomalyTracker->getLastAnomalyTimestampNs(), (long long)event3->GetTimestampNs());
 }
 
diff --git a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
index 4ad1db1..7843ca0 100644
--- a/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/MaxDurationTracker_test.cpp
@@ -38,10 +38,12 @@
 
 const ConfigKey kConfigKey(0, "test");
 
-const HashableDimensionKey eventKey = getMockedDimensionKey(0, "1");
-const HashableDimensionKey conditionKey = getMockedDimensionKey(4, "1");
-const HashableDimensionKey key1 = getMockedDimensionKey(1, "1");
-const HashableDimensionKey key2 = getMockedDimensionKey(1, "2");
+const int TagId = 1;
+
+const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "1");
+const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
 
 TEST(MaxDurationTrackerTest, TestSimpleMaxDuration) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
@@ -174,7 +176,7 @@
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey conditionKey1;
-    HashableDimensionKey eventKey = getMockedDimensionKey(2, "maps");
+    HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 2, "maps");
     conditionKey1["APP_BACKGROUND"] = conditionKey;
 
     EXPECT_CALL(*wizard, query(_, conditionKey1))  // #4
diff --git a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
index e0f554d..550b059 100644
--- a/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
+++ b/cmds/statsd/tests/metrics/OringDurationTracker_test.cpp
@@ -35,11 +35,12 @@
 namespace statsd {
 
 const ConfigKey kConfigKey(0, "test");
-const HashableDimensionKey eventKey = getMockedDimensionKey(0, "event");
+const int TagId = 1;
+const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "event");
 
-const HashableDimensionKey kConditionKey1 = getMockedDimensionKey(1, "maps");
-const HashableDimensionKey kEventKey1 = getMockedDimensionKey(2, "maps");
-const HashableDimensionKey kEventKey2 = getMockedDimensionKey(3, "maps");
+const std::vector<HashableDimensionKey> kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")};
+const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
 
 TEST(OringDurationTrackerTest, TestDurationOverlap) {
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.cpp b/cmds/statsd/tests/metrics/metrics_test_helper.cpp
index a0a854a..fc7245c 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.cpp
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.cpp
@@ -18,15 +18,12 @@
 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);
+HashableDimensionKey getMockedDimensionKey(int tagId, int key, string value) {
+    DimensionsValue dimensionsValue;
+    dimensionsValue.set_field(tagId);
+    dimensionsValue.mutable_value_tuple()->add_dimensions_value()->set_field(key);
+    dimensionsValue.mutable_value_tuple()->mutable_dimensions_value(0)->set_value_str(value);
+    return HashableDimensionKey(dimensionsValue);
 }
 
 }  // namespace statsd
diff --git a/cmds/statsd/tests/metrics/metrics_test_helper.h b/cmds/statsd/tests/metrics/metrics_test_helper.h
index 7cb3329..23e86f9 100644
--- a/cmds/statsd/tests/metrics/metrics_test_helper.h
+++ b/cmds/statsd/tests/metrics/metrics_test_helper.h
@@ -28,7 +28,7 @@
     MOCK_METHOD2(
             query,
             ConditionState(const int conditionIndex,
-                           const std::map<std::string, HashableDimensionKey>& conditionParameters));
+                           const ConditionKey& conditionParameters));
 };
 
 class MockStatsPullerManager : public StatsPullerManager {
@@ -38,7 +38,7 @@
     MOCK_METHOD2(Pull, bool(const int pullCode, vector<std::shared_ptr<LogEvent>>* data));
 };
 
-HashableDimensionKey getMockedDimensionKey(int key, std::string value);
+HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
new file mode 100644
index 0000000..39e366f
--- /dev/null
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -0,0 +1,321 @@
+// 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 <gtest/gtest.h>
+#include "statsd_test_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name,
+                                                  WakelockStateChanged::State state) {
+    AtomMatcher atom_matcher;
+    atom_matcher.set_name(name);
+    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+    simple_atom_matcher->set_atom_id(android::util::WAKELOCK_STATE_CHANGED);
+    auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
+    field_value_matcher->set_field(4);  // State field.
+    field_value_matcher->set_eq_int(state);
+    return atom_matcher;
+}
+
+AtomMatcher CreateAcquireWakelockAtomMatcher() {
+    return CreateWakelockStateChangedAtomMatcher("AcquireWakelock", WakelockStateChanged::ACQUIRE);
+}
+
+AtomMatcher CreateReleaseWakelockAtomMatcher() {
+    return CreateWakelockStateChangedAtomMatcher("ReleaseWakelock", WakelockStateChanged::RELEASE);
+}
+
+AtomMatcher CreateScreenStateChangedAtomMatcher(
+    const string& name, ScreenStateChanged::State state) {
+    AtomMatcher atom_matcher;
+    atom_matcher.set_name(name);
+    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+    simple_atom_matcher->set_atom_id(android::util::SCREEN_STATE_CHANGED);
+    auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
+    field_value_matcher->set_field(1);  // State field.
+    field_value_matcher->set_eq_int(state);
+    return atom_matcher;
+}
+
+AtomMatcher CreateScreenTurnedOnAtomMatcher() {
+    return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn", ScreenStateChanged::STATE_ON);
+}
+
+AtomMatcher CreateScreenTurnedOffAtomMatcher() {
+    return CreateScreenStateChangedAtomMatcher("ScreenTurnedOff", ScreenStateChanged::STATE_OFF);
+}
+
+AtomMatcher CreateSyncStateChangedAtomMatcher(
+    const string& name, SyncStateChanged::State state) {
+    AtomMatcher atom_matcher;
+    atom_matcher.set_name(name);
+    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+    simple_atom_matcher->set_atom_id(android::util::SYNC_STATE_CHANGED);
+    auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
+    field_value_matcher->set_field(3);  // State field.
+    field_value_matcher->set_eq_int(state);
+    return atom_matcher;
+}
+
+AtomMatcher CreateSyncStartAtomMatcher() {
+    return CreateSyncStateChangedAtomMatcher("SyncStart", SyncStateChanged::ON);
+}
+
+AtomMatcher CreateSyncEndAtomMatcher() {
+    return CreateSyncStateChangedAtomMatcher("SyncEnd", SyncStateChanged::OFF);
+}
+
+AtomMatcher CreateActivityForegroundStateChangedAtomMatcher(
+    const string& name, ActivityForegroundStateChanged::Activity activity) {
+    AtomMatcher atom_matcher;
+    atom_matcher.set_name(name);
+    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+    simple_atom_matcher->set_atom_id(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+    auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
+    field_value_matcher->set_field(4);  // Activity field.
+    field_value_matcher->set_eq_int(activity);
+    return atom_matcher;
+}
+
+AtomMatcher CreateMoveToBackgroundAtomMatcher() {
+    return CreateActivityForegroundStateChangedAtomMatcher(
+        "MoveToBackground", ActivityForegroundStateChanged::MOVE_TO_BACKGROUND);
+}
+
+AtomMatcher CreateMoveToForegroundAtomMatcher() {
+    return CreateActivityForegroundStateChangedAtomMatcher(
+        "MoveToForeground", ActivityForegroundStateChanged::MOVE_TO_FOREGROUND);
+}
+
+AtomMatcher CreateProcessLifeCycleStateChangedAtomMatcher(
+    const string& name, ProcessLifeCycleStateChanged::Event event) {
+    AtomMatcher atom_matcher;
+    atom_matcher.set_name(name);
+    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+    simple_atom_matcher->set_atom_id(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    auto field_value_matcher = simple_atom_matcher->add_field_value_matcher();
+    field_value_matcher->set_field(3);  // Process state field.
+    field_value_matcher->set_eq_int(event);
+    return atom_matcher;
+}
+
+AtomMatcher CreateProcessCrashAtomMatcher() {
+    return CreateProcessLifeCycleStateChangedAtomMatcher(
+        "ProcessCrashed", ProcessLifeCycleStateChanged::PROCESS_CRASHED);
+}
+
+
+Predicate CreateScreenIsOnPredicate() {
+    Predicate predicate;
+    predicate.set_name("ScreenIsOn");
+    predicate.mutable_simple_predicate()->set_start("ScreenTurnedOn");
+    predicate.mutable_simple_predicate()->set_stop("ScreenTurnedOff");
+    return predicate;
+}
+
+Predicate CreateScreenIsOffPredicate() {
+    Predicate predicate;
+    predicate.set_name("ScreenIsOff");
+    predicate.mutable_simple_predicate()->set_start("ScreenTurnedOff");
+    predicate.mutable_simple_predicate()->set_stop("ScreenTurnedOn");
+    return predicate;
+}
+
+Predicate CreateHoldingWakelockPredicate() {
+    Predicate predicate;
+    predicate.set_name("HoldingWakelock");
+    predicate.mutable_simple_predicate()->set_start("AcquireWakelock");
+    predicate.mutable_simple_predicate()->set_stop("ReleaseWakelock");
+    return predicate;
+}
+
+Predicate CreateIsSyncingPredicate() {
+    Predicate predicate;
+    predicate.set_name("IsSyncing");
+    predicate.mutable_simple_predicate()->set_start("SyncStart");
+    predicate.mutable_simple_predicate()->set_stop("SyncEnd");
+    return predicate;
+}
+
+Predicate CreateIsInBackgroundPredicate() {
+    Predicate predicate;
+    predicate.set_name("IsInBackground");
+    predicate.mutable_simple_predicate()->set_start("MoveToBackground");
+    predicate.mutable_simple_predicate()->set_stop("MoveToForeground");
+    return predicate;
+}
+
+void addPredicateToPredicateCombination(const Predicate& predicate,
+                                        Predicate* combinationPredicate) {
+    combinationPredicate->mutable_combination()->add_predicate(predicate.name());
+}
+
+FieldMatcher CreateAttributionUidDimensions(const int atomId,
+                                            const std::vector<Position>& positions) {
+    FieldMatcher dimensions;
+    dimensions.set_field(atomId);
+    for (const auto position : positions) {
+        auto child = dimensions.add_child();
+        child->set_field(1);
+        child->set_position(position);
+        child->add_child()->set_field(1);
+    }
+    return dimensions;
+}
+
+FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId,
+                                                 const std::vector<Position>& positions) {
+    FieldMatcher dimensions;
+    dimensions.set_field(atomId);
+    for (const auto position : positions) {
+        auto child = dimensions.add_child();
+        child->set_field(1);
+        child->set_position(position);
+        child->add_child()->set_field(1);
+        child->add_child()->set_field(2);
+    }
+    return dimensions;
+}
+
+FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields) {
+    FieldMatcher dimensions;
+    dimensions.set_field(atomId);
+    for (const int field : fields) {
+        dimensions.add_child()->set_field(field);
+    }
+    return dimensions;
+}
+
+std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
+    const ScreenStateChanged::State state, uint64_t timestampNs) {
+    auto event = std::make_unique<LogEvent>(android::util::SCREEN_STATE_CHANGED, timestampNs);
+    EXPECT_TRUE(event->write(state));
+    event->init();
+    return event;
+}
+
+std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(
+    const std::vector<AttributionNode>& attributions, const string& wakelockName,
+    const WakelockStateChanged::State state, uint64_t timestampNs) {
+    auto event = std::make_unique<LogEvent>(android::util::WAKELOCK_STATE_CHANGED, timestampNs);
+    event->write(attributions);
+    event->write(WakelockStateChanged::PARTIAL);
+    event->write(wakelockName);
+    event->write(state);
+    event->init();
+    return event;
+}
+
+std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(
+    const std::vector<AttributionNode>& attributions,
+    const string& wakelockName, uint64_t timestampNs) {
+    return CreateWakelockStateChangedEvent(
+        attributions, wakelockName, WakelockStateChanged::ACQUIRE, timestampNs);
+}
+
+std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(
+    const std::vector<AttributionNode>& attributions,
+    const string& wakelockName, uint64_t timestampNs) {
+    return CreateWakelockStateChangedEvent(
+        attributions, wakelockName, WakelockStateChanged::RELEASE, timestampNs);
+}
+
+std::unique_ptr<LogEvent> CreateActivityForegroundStateChangedEvent(
+    const int uid, const ActivityForegroundStateChanged::Activity activity, uint64_t timestampNs) {
+    auto event = std::make_unique<LogEvent>(
+        android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, timestampNs);
+    event->write(uid);
+    event->write("pkg_name");
+    event->write("class_name");
+    event->write(activity);
+    event->init();
+    return event;
+}
+
+std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs) {
+    return CreateActivityForegroundStateChangedEvent(
+        uid, ActivityForegroundStateChanged::MOVE_TO_BACKGROUND, timestampNs);
+}
+
+std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs) {
+    return CreateActivityForegroundStateChangedEvent(
+        uid, ActivityForegroundStateChanged::MOVE_TO_FOREGROUND, timestampNs);
+}
+
+std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(
+    const int uid, const string& name, const SyncStateChanged::State state, uint64_t timestampNs) {
+    auto event = std::make_unique<LogEvent>(android::util::SYNC_STATE_CHANGED, timestampNs);
+    event->write(uid);
+    event->write(name);
+    event->write(state);
+    event->init();
+    return event;
+}
+
+std::unique_ptr<LogEvent> CreateSyncStartEvent(
+    const int uid, const string& name, uint64_t timestampNs){
+    return CreateSyncStateChangedEvent(uid, name, SyncStateChanged::ON, timestampNs);
+}
+
+std::unique_ptr<LogEvent> CreateSyncEndEvent(
+    const int uid, const string& name, uint64_t timestampNs) {
+    return CreateSyncStateChangedEvent(uid, name, SyncStateChanged::OFF, timestampNs);
+}
+
+std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent(
+    const int uid, const ProcessLifeCycleStateChanged::Event event, uint64_t timestampNs) {
+    auto logEvent = std::make_unique<LogEvent>(
+        android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, timestampNs);
+    logEvent->write(uid);
+    logEvent->write("");
+    logEvent->write(event);
+    logEvent->init();
+    return logEvent;
+}
+
+std::unique_ptr<LogEvent> CreateAppCrashEvent(const int uid, uint64_t timestampNs) {
+    return CreateProcessLifeCycleStateChangedEvent(
+        uid, ProcessLifeCycleStateChanged::PROCESS_CRASHED, timestampNs);
+}
+
+sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
+                                              const ConfigKey& key) {
+    sp<UidMap> uidMap = new UidMap();
+    sp<AnomalyMonitor> anomalyMonitor = new AnomalyMonitor(10); // 10 seconds
+    sp<StatsLogProcessor> processor = new StatsLogProcessor(
+        uidMap, anomalyMonitor, timeBaseSec, [](const ConfigKey&){});
+    processor->OnConfigUpdated(key, config);
+    return processor;
+}
+
+AttributionNode CreateAttribution(const int& uid, const string& tag) {
+    AttributionNode attribution;
+    attribution.set_uid(uid);
+    attribution.set_tag(tag);
+    return attribution;
+}
+
+void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events) {
+  std::sort(events->begin(), events->end(),
+            [](const std::unique_ptr<LogEvent>& a, const std::unique_ptr<LogEvent>& b) {
+              return a->GetTimestampNs() < b->GetTimestampNs();
+            });
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tests/statsd_test_util.h b/cmds/statsd/tests/statsd_test_util.h
new file mode 100644
index 0000000..282e1b2
--- /dev/null
+++ b/cmds/statsd/tests/statsd_test_util.h
@@ -0,0 +1,126 @@
+// 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 "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "statslog.h"
+#include "src/logd/LogEvent.h"
+#include "src/StatsLogProcessor.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+// Create AtomMatcher proto for acquiring wakelock.
+AtomMatcher CreateAcquireWakelockAtomMatcher();
+
+// Create AtomMatcher proto for releasing wakelock.
+AtomMatcher CreateReleaseWakelockAtomMatcher() ;
+
+// Create AtomMatcher proto for screen turned on.
+AtomMatcher CreateScreenTurnedOnAtomMatcher();
+
+// Create AtomMatcher proto for screen turned off.
+AtomMatcher CreateScreenTurnedOffAtomMatcher();
+
+// Create AtomMatcher proto for app sync turned on.
+AtomMatcher CreateSyncStartAtomMatcher();
+
+// Create AtomMatcher proto for app sync turned off.
+AtomMatcher CreateSyncEndAtomMatcher();
+
+// Create AtomMatcher proto for app sync moves to background.
+AtomMatcher CreateMoveToBackgroundAtomMatcher();
+
+// Create AtomMatcher proto for app sync moves to foreground.
+AtomMatcher CreateMoveToForegroundAtomMatcher();
+
+// Create AtomMatcher proto for process crashes
+AtomMatcher CreateProcessCrashAtomMatcher() ;
+
+// Create Predicate proto for screen is on.
+Predicate CreateScreenIsOnPredicate();
+
+// Create Predicate proto for screen is off.
+Predicate CreateScreenIsOffPredicate();
+
+// Create Predicate proto for holding wakelock.
+Predicate CreateHoldingWakelockPredicate();
+
+// Create a Predicate proto for app syncing.
+Predicate CreateIsSyncingPredicate();
+
+// Create a Predicate proto for app is in background.
+Predicate CreateIsInBackgroundPredicate();
+
+// Add a predicate to the predicate combination.
+void addPredicateToPredicateCombination(const Predicate& predicate, Predicate* combination);
+
+// Create dimensions from primitive fields.
+FieldMatcher CreateDimensions(const int atomId, const std::vector<int>& fields);
+
+// Create dimensions by attribution uid and tag.
+FieldMatcher CreateAttributionUidAndTagDimensions(const int atomId,
+                                                  const std::vector<Position>& positions);
+
+// Create dimensions by attribution uid only.
+FieldMatcher CreateAttributionUidDimensions(const int atomId,
+                                            const std::vector<Position>& positions);
+
+// Create log event for screen state changed.
+std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
+    const ScreenStateChanged::State state, uint64_t timestampNs);
+
+// Create log event for app moving to background.
+std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs);
+
+// Create log event for app moving to foreground.
+std::unique_ptr<LogEvent> CreateMoveToForegroundEvent(const int uid, uint64_t timestampNs);
+
+// Create log event when the app sync starts.
+std::unique_ptr<LogEvent> CreateSyncStartEvent(
+    const int uid, const string& name, uint64_t timestampNs);
+
+// Create log event when the app sync ends.
+std::unique_ptr<LogEvent> CreateSyncEndEvent(
+    const int uid, const string& name, uint64_t timestampNs);
+
+// Create log event when the app sync ends.
+std::unique_ptr<LogEvent> CreateAppCrashEvent(
+    const int uid, uint64_t timestampNs);
+
+// Create log event for acquiring wakelock.
+std::unique_ptr<LogEvent> CreateAcquireWakelockEvent(
+    const std::vector<AttributionNode>& attributions,
+    const string& wakelockName, uint64_t timestampNs);
+
+// Create log event for releasing wakelock.
+std::unique_ptr<LogEvent> CreateReleaseWakelockEvent(
+    const std::vector<AttributionNode>& attributions,
+    const string& wakelockName, uint64_t timestampNs);
+
+// Helper function to create an AttributionNode proto.
+AttributionNode CreateAttribution(const int& uid, const string& tag);
+
+// Create a statsd log event processor upon the start time in seconds, config and key.
+sp<StatsLogProcessor> CreateStatsLogProcessor(const long timeBaseSec, const StatsdConfig& config,
+                                              const ConfigKey& key);
+
+// Util function to sort the log events by timestamp.
+void sortLogEventsByTimestamp(std::vector<std::unique_ptr<LogEvent>> *events);
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
index fe3d86d..9294681 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/DisplayProtoUtils.java
@@ -71,20 +71,25 @@
         return DateFormat.format("dd/MM hh:mm:ss", nanoSec/1000000).toString();
     }
 
-    private static void displayDimension(StringBuilder sb, List<StatsLog.KeyValuePair> pairs) {
-        for (com.android.os.StatsLog.KeyValuePair kv : pairs) {
-            sb.append(kv.getKey()).append(":");
-            if (kv.hasValueBool()) {
-                sb.append(kv.getValueBool());
-            } else if (kv.hasValueFloat()) {
-                sb.append(kv.getValueFloat());
-            } else if (kv.hasValueInt()) {
-                sb.append(kv.getValueInt());
-            } else if (kv.hasValueStr()) {
-                sb.append(kv.getValueStr());
+    private static void displayDimension(StringBuilder sb, StatsLog.DimensionsValue dimensionValue) {
+        sb.append(dimensionValue.getField()).append(":");
+        if (dimensionValue.hasValueBool()) {
+            sb.append(dimensionValue.getValueBool());
+        } else if (dimensionValue.hasValueFloat()) {
+            sb.append(dimensionValue.getValueFloat());
+        } else if (dimensionValue.hasValueInt()) {
+            sb.append(dimensionValue.getValueInt());
+        } else if (dimensionValue.hasValueStr()) {
+            sb.append(dimensionValue.getValueStr());
+        } else if (dimensionValue.hasValueTuple()) {
+            sb.append("{");
+            for (StatsLog.DimensionsValue child :
+                    dimensionValue.getValueTuple().getDimensionsValueList()) {
+                displayDimension(sb, child);
             }
-            sb.append(" ");
+            sb.append("}");
         }
+        sb.append(" ");
     }
 
     public static void displayDurationMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
@@ -93,7 +98,7 @@
         sb.append("Dimension size: ").append(durationMetricDataWrapper.getDataCount()).append("\n");
         for (StatsLog.DurationMetricData duration : durationMetricDataWrapper.getDataList()) {
             sb.append("dimension: ");
-            displayDimension(sb, duration.getDimensionList());
+            displayDimension(sb, duration.getDimension());
             sb.append("\n");
 
             for (StatsLog.DurationBucketInfo info : duration.getBucketInfoList())  {
@@ -120,7 +125,7 @@
         sb.append("Dimension size: ").append(countMetricDataWrapper.getDataCount()).append("\n");
         for (StatsLog.CountMetricData count : countMetricDataWrapper.getDataList()) {
             sb.append("dimension: ");
-            displayDimension(sb, count.getDimensionList());
+            displayDimension(sb, count.getDimension());
             sb.append("\n");
 
             for (StatsLog.CountBucketInfo info : count.getBucketInfoList())  {
diff --git a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
index 70dd634..137fd4d 100644
--- a/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
+++ b/cmds/statsd/tools/dogfood/src/com/android/statsd/dogfood/MainActivity.java
@@ -252,7 +252,9 @@
             Log.d(TAG, "invalid pkg id");
             return;
         }
-        StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, mUids[id], 0, name, 1);
+        int[] uids = new int[] {mUids[id]};
+        String[] tags  = new String[] {"acquire"};
+        StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uids, tags, 0, name, 1);
         StringBuilder sb = new StringBuilder();
         sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0)
                 .append(", ").append(name).append(", 1);");
@@ -264,7 +266,9 @@
             Log.d(TAG, "invalid pkg id");
             return;
         }
-        StatsLog.write(10, mUids[id], 0, name, 0);
+        int[] uids = new int[] {mUids[id]};
+        String[] tags  = new String[] {"release"};
+        StatsLog.write(10, uids, tags, 0, name, 0);
         StringBuilder sb = new StringBuilder();
         sb.append("StagsLog.write(10, ").append(mUids[id]).append(", ").append(0)
                 .append(", ").append(name).append(", 0);");
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
index 5fc4cf5..f516477 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/ConfigFactory.java
@@ -27,8 +27,7 @@
 import com.android.internal.os.StatsdConfigProto.EventMetric;
 import com.android.internal.os.StatsdConfigProto.GaugeMetric;
 import com.android.internal.os.StatsdConfigProto.ValueMetric;
-import com.android.internal.os.StatsdConfigProto.KeyMatcher;
-import com.android.internal.os.StatsdConfigProto.KeyValueMatcher;
+import com.android.internal.os.StatsdConfigProto.FieldValueMatcher;
 import com.android.internal.os.StatsdConfigProto.AtomMatcher;
 import com.android.internal.os.StatsdConfigProto.SimplePredicate;
 import com.android.internal.os.StatsdConfigProto.StatsdConfig;
diff --git a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
index 735a327..ba43d57 100644
--- a/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
+++ b/cmds/statsd/tools/loadtest/src/com/android/statsd/loadtest/DisplayProtoUtils.java
@@ -71,20 +71,25 @@
         return DateFormat.format("dd/MM hh:mm:ss", nanoSec/1000000).toString();
     }
 
-    private static void displayDimension(StringBuilder sb, List<StatsLog.KeyValuePair> pairs) {
-        for (com.android.os.StatsLog.KeyValuePair kv : pairs) {
-            sb.append(kv.getKey()).append(":");
-            if (kv.hasValueBool()) {
-                sb.append(kv.getValueBool());
-            } else if (kv.hasValueFloat()) {
-                sb.append(kv.getValueFloat());
-            } else if (kv.hasValueInt()) {
-                sb.append(kv.getValueInt());
-            } else if (kv.hasValueStr()) {
-                sb.append(kv.getValueStr());
+    private static void displayDimension(StringBuilder sb, StatsLog.DimensionsValue dimensionValue) {
+        sb.append(dimensionValue.getField()).append(":");
+        if (dimensionValue.hasValueBool()) {
+            sb.append(dimensionValue.getValueBool());
+        } else if (dimensionValue.hasValueFloat()) {
+            sb.append(dimensionValue.getValueFloat());
+        } else if (dimensionValue.hasValueInt()) {
+            sb.append(dimensionValue.getValueInt());
+        } else if (dimensionValue.hasValueStr()) {
+            sb.append(dimensionValue.getValueStr());
+        } else if (dimensionValue.hasValueTuple()) {
+            sb.append("{");
+            for (StatsLog.DimensionsValue child :
+                    dimensionValue.getValueTuple().getDimensionsValueList()) {
+                displayDimension(sb, child);
             }
-            sb.append(" ");
+            sb.append("}");
         }
+        sb.append(" ");
     }
 
     public static void displayDurationMetricData(StringBuilder sb, StatsLog.StatsLogReport log) {
@@ -93,7 +98,7 @@
         sb.append("Dimension size: ").append(durationMetricDataWrapper.getDataCount()).append("\n");
         for (StatsLog.DurationMetricData duration : durationMetricDataWrapper.getDataList()) {
             sb.append("dimension: ");
-            displayDimension(sb, duration.getDimensionList());
+            displayDimension(sb, duration.getDimension());
             sb.append("\n");
 
             for (StatsLog.DurationBucketInfo info : duration.getBucketInfoList())  {
@@ -120,7 +125,7 @@
         sb.append("Dimension size: ").append(countMetricDataWrapper.getDataCount()).append("\n");
         for (StatsLog.CountMetricData count : countMetricDataWrapper.getDataList()) {
             sb.append("dimension: ");
-            displayDimension(sb, count.getDimensionList());
+            displayDimension(sb, count.getDimension());
             sb.append("\n");
 
             for (StatsLog.CountBucketInfo info : count.getBucketInfoList())  {
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 8966585..72f07b7 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -4118,13 +4118,10 @@
 
             getUidStatsLocked(uid).noteStartWakeLocked(pid, name, type, elapsedRealtime);
 
-            // TODO(statsd): Use the attribution chain specified in WorkChain instead of uid.
-            // The debug logging here can be deleted once statsd is wired up.
-            if (DEBUG) {
-                Slog.w(TAG, "StatsLog [start]: uid=" + uid + ", type=" + type + ", name=" + name
-                + ", wc=" + wc);
+            if (wc != null) {
+                StatsLog.write(
+                        StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(), type, name, 1);
             }
-            StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uid, type, name, 1);
         }
     }
 
@@ -4161,14 +4158,10 @@
             }
 
             getUidStatsLocked(uid).noteStopWakeLocked(pid, name, type, elapsedRealtime);
-
-            // TODO(statsd): Use the attribution chain specified in WorkChain instead of uid.
-            // The debug logging here can be deleted once statsd is wired up.
-            if (DEBUG) {
-                Slog.w(TAG, "StatsLog [stop]: uid=" + uid + ", type=" + type + ", name=" + name
-                    + ", wc=" + wc);
+            if (wc != null) {
+                StatsLog.write(
+                        StatsLog.WAKELOCK_STATE_CHANGED, wc.getUids(), wc.getTags(), type, name, 0);
             }
-            StatsLog.write(StatsLog.WAKELOCK_STATE_CHANGED, uid, type, name, 0);
         }
     }
 
diff --git a/tools/stats_log_api_gen/Collation.cpp b/tools/stats_log_api_gen/Collation.cpp
index a25784d..80853b1 100644
--- a/tools/stats_log_api_gen/Collation.cpp
+++ b/tools/stats_log_api_gen/Collation.cpp
@@ -112,7 +112,7 @@
         case FieldDescriptor::TYPE_MESSAGE:
             // TODO: not the final package name
             if (field->message_type()->full_name() ==
-                "android.os.statsd.AttributionChain") {
+                "android.os.statsd.AttributionNode") {
               return JAVA_TYPE_ATTRIBUTION_CHAIN;
             } else {
                 return JAVA_TYPE_OBJECT;
diff --git a/tools/stats_log_api_gen/main.cpp b/tools/stats_log_api_gen/main.cpp
index 5e93c08..89749fb 100644
--- a/tools/stats_log_api_gen/main.cpp
+++ b/tools/stats_log_api_gen/main.cpp
@@ -769,7 +769,7 @@
 
     AtomDecl attributionDecl;
     vector<java_type_t> attributionSignature;
-    collate_atom(android::os::statsd::Attribution::descriptor(),
+    collate_atom(android::os::statsd::AttributionNode::descriptor(),
                  &attributionDecl, &attributionSignature);
 
     // Write the .cpp file
diff --git a/tools/stats_log_api_gen/test.proto b/tools/stats_log_api_gen/test.proto
index 84b22cda..66cbee8 100644
--- a/tools/stats_log_api_gen/test.proto
+++ b/tools/stats_log_api_gen/test.proto
@@ -39,7 +39,7 @@
 }
 
 message AllTypesAtom {
-  optional android.os.statsd.AttributionChain attribution_chain = 1;
+  repeated android.os.statsd.AttributionNode attribution_chain = 1;
   optional double double_field = 2;
   optional float float_field = 3;
   optional int64 int64_field = 4;
@@ -99,12 +99,12 @@
     }
 }
 
-message BadAttributionChainPositionAtom {
+message BadAttributionNodePositionAtom {
   optional int32 field1 = 1;
-  optional android.os.statsd.AttributionChain attribution = 2;
+  repeated android.os.statsd.AttributionNode attribution = 2;
 }
 
-message BadAttributionChainPosition {
-  oneof event { BadAttributionChainPositionAtom bad = 1; }
+message BadAttributionNodePosition {
+  oneof event { BadAttributionNodePositionAtom bad = 1; }
 }
 
diff --git a/tools/stats_log_api_gen/test_collation.cpp b/tools/stats_log_api_gen/test_collation.cpp
index b2b7298..9e22cd9 100644
--- a/tools/stats_log_api_gen/test_collation.cpp
+++ b/tools/stats_log_api_gen/test_collation.cpp
@@ -191,10 +191,10 @@
  * Test that atoms that have an attribution chain not in the first position are
  * rejected.
  */
-TEST(CollationTest, FailBadAttributionChainPosition) {
+TEST(CollationTest, FailBadAttributionNodePosition) {
   Atoms atoms;
   int errorCount =
-      collate_atoms(BadAttributionChainPosition::descriptor(), &atoms);
+      collate_atoms(BadAttributionNodePosition::descriptor(), &atoms);
 
   EXPECT_EQ(1, errorCount);
 }