Support dimension in condition in metric producers.

Test: added e2e tests for count/duration metrics sliced by fields in condition and with/without links.

Change-Id: Ie34deba68e6780abdde458be3f0ce5284e76a1a2
diff --git a/bin/Android.mk b/bin/Android.mk
index eabbb96..76dbd6a 100644
--- a/bin/Android.mk
+++ b/bin/Android.mk
@@ -165,6 +165,7 @@
 
 LOCAL_SRC_FILES := \
     $(statsd_common_src) \
+    tests/dimension_test.cpp \
     tests/AnomalyMonitor_test.cpp \
     tests/anomaly/AnomalyTracker_test.cpp \
     tests/ConfigManager_test.cpp \
@@ -190,7 +191,8 @@
     tests/e2e/WakelockDuration_e2e_test.cpp \
     tests/e2e/MetricConditionLink_e2e_test.cpp \
     tests/e2e/Attribution_e2e_test.cpp \
-    tests/e2e/GaugeMetric_e2e_test.cpp
+    tests/e2e/GaugeMetric_e2e_test.cpp \
+    tests/e2e/DimensionInCondition_e2e_test.cpp
 
 LOCAL_STATIC_LIBRARIES := \
     $(statsd_common_static_libraries) \
diff --git a/bin/src/HashableDimensionKey.cpp b/bin/src/HashableDimensionKey.cpp
index 857a6dd..f0eaeff 100644
--- a/bin/src/HashableDimensionKey.cpp
+++ b/bin/src/HashableDimensionKey.cpp
@@ -67,12 +67,16 @@
     return hashDimensionsValue(0, value);
 }
 
+android::hash_t hashMetricDimensionKey(int64_t seed, const MetricDimensionKey& dimensionKey) {
+    android::hash_t hash = seed;
+    hash = android::JenkinsHashMix(hash, std::hash<MetricDimensionKey>{}(dimensionKey));
+    return JenkinsHashWhiten(hash);
+}
+
 using std::string;
 
 string HashableDimensionKey::toString() const {
-    string flattened;
-    DimensionsValueToString(getDimensionsValue(), &flattened);
-    return flattened;
+    return DimensionsValueToString(getDimensionsValue());
 }
 
 bool EqualsTo(const DimensionsValue& s1, const DimensionsValue& s2) {
@@ -162,6 +166,22 @@
     return LessThan(getDimensionsValue(), that.getDimensionsValue());
 };
 
+string MetricDimensionKey::toString() const {
+    string flattened = mDimensionKeyInWhat.toString();
+    flattened += mDimensionKeyInCondition.toString();
+    return flattened;
+}
+
+bool MetricDimensionKey::operator==(const MetricDimensionKey& that) const {
+    return mDimensionKeyInWhat == that.getDimensionKeyInWhat() &&
+        mDimensionKeyInCondition == that.getDimensionKeyInCondition();
+};
+
+bool MetricDimensionKey::operator<(const MetricDimensionKey& that) const {
+    return toString().compare(that.toString()) < 0;
+};
+
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
\ No newline at end of file
diff --git a/bin/src/HashableDimensionKey.h b/bin/src/HashableDimensionKey.h
index 85c317f..a31d7a6 100644
--- a/bin/src/HashableDimensionKey.h
+++ b/bin/src/HashableDimensionKey.h
@@ -41,6 +41,10 @@
         return mDimensionsValue;
     }
 
+    inline DimensionsValue* getMutableDimensionsValue() {
+        return &mDimensionsValue;
+    }
+
     bool operator==(const HashableDimensionKey& that) const;
 
     bool operator<(const HashableDimensionKey& that) const;
@@ -53,8 +57,52 @@
     DimensionsValue mDimensionsValue;
 };
 
+class MetricDimensionKey {
+ public:
+    explicit MetricDimensionKey(const HashableDimensionKey& dimensionKeyInWhat,
+                                const HashableDimensionKey& dimensionKeyInCondition)
+        : mDimensionKeyInWhat(dimensionKeyInWhat),
+          mDimensionKeyInCondition(dimensionKeyInCondition) {};
+
+    MetricDimensionKey(){};
+
+    MetricDimensionKey(const MetricDimensionKey& that)
+        : mDimensionKeyInWhat(that.getDimensionKeyInWhat()),
+          mDimensionKeyInCondition(that.getDimensionKeyInCondition()) {};
+
+    MetricDimensionKey& operator=(const MetricDimensionKey& from) = default;
+
+    std::string toString() const;
+
+    inline const HashableDimensionKey& getDimensionKeyInWhat() const {
+        return mDimensionKeyInWhat;
+    }
+
+    inline const HashableDimensionKey& getDimensionKeyInCondition() const {
+        return mDimensionKeyInCondition;
+    }
+
+    bool hasDimensionKeyInCondition() const {
+        return mDimensionKeyInCondition.getDimensionsValue().has_field();
+    }
+
+    bool operator==(const MetricDimensionKey& that) const;
+
+    bool operator<(const MetricDimensionKey& that) const;
+
+    inline const char* c_str() const {
+        return toString().c_str();
+    }
+  private:
+      HashableDimensionKey mDimensionKeyInWhat;
+      HashableDimensionKey mDimensionKeyInCondition;
+};
+
+bool compareDimensionsValue(const DimensionsValue& s1, const DimensionsValue& s2);
+
 android::hash_t hashDimensionsValue(int64_t seed, const DimensionsValue& value);
 android::hash_t hashDimensionsValue(const DimensionsValue& value);
+android::hash_t hashMetricDimensionKey(int64_t see, const MetricDimensionKey& dimensionKey);
 
 }  // namespace statsd
 }  // namespace os
@@ -63,6 +111,7 @@
 namespace std {
 
 using android::os::statsd::HashableDimensionKey;
+using android::os::statsd::MetricDimensionKey;
 
 template <>
 struct hash<HashableDimensionKey> {
@@ -71,4 +120,14 @@
     }
 };
 
-}  // namespace std
+template <>
+struct hash<MetricDimensionKey> {
+    std::size_t operator()(const MetricDimensionKey& key) const {
+        android::hash_t hash = hashDimensionsValue(
+            key.getDimensionKeyInWhat().getDimensionsValue());
+        hash = android::JenkinsHashMix(hash,
+                    hashDimensionsValue(key.getDimensionKeyInCondition().getDimensionsValue()));
+        return android::JenkinsHashWhiten(hash);
+    }
+};
+}  // namespace std
\ No newline at end of file
diff --git a/bin/src/StatsLogProcessor.h b/bin/src/StatsLogProcessor.h
index c19ff63..7642aaf 100644
--- a/bin/src/StatsLogProcessor.h
+++ b/bin/src/StatsLogProcessor.h
@@ -104,7 +104,10 @@
     FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
     FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice);
     FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
-
+    FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricNoLink);
+    FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricWithLink);
+    FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink);
+    FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink);
 };
 
 }  // namespace statsd
diff --git a/bin/src/anomaly/AnomalyTracker.cpp b/bin/src/anomaly/AnomalyTracker.cpp
index ded6c4c..c84a5b4 100644
--- a/bin/src/anomaly/AnomalyTracker.cpp
+++ b/bin/src/anomaly/AnomalyTracker.cpp
@@ -96,7 +96,7 @@
     }
 }
 
-void AnomalyTracker::addPastBucket(const HashableDimensionKey& key, const int64_t& bucketValue,
+void AnomalyTracker::addPastBucket(const MetricDimensionKey& key, const int64_t& bucketValue,
                                    const int64_t& bucketNum) {
     flushPastBuckets(bucketNum);
 
@@ -147,7 +147,7 @@
     }
 }
 
-int64_t AnomalyTracker::getPastBucketValue(const HashableDimensionKey& key,
+int64_t AnomalyTracker::getPastBucketValue(const MetricDimensionKey& key,
                                            const int64_t& bucketNum) const {
     const auto& bucket = mPastBuckets[index(bucketNum)];
     if (bucket == nullptr) {
@@ -157,7 +157,7 @@
     return itr == bucket->end() ? 0 : itr->second;
 }
 
-int64_t AnomalyTracker::getSumOverPastBuckets(const HashableDimensionKey& key) const {
+int64_t AnomalyTracker::getSumOverPastBuckets(const MetricDimensionKey& key) const {
     const auto& itr = mSumOverPastBuckets.find(key);
     if (itr != mSumOverPastBuckets.end()) {
         return itr->second;
@@ -165,7 +165,7 @@
     return 0;
 }
 
-bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const HashableDimensionKey& key,
+bool AnomalyTracker::detectAnomaly(const int64_t& currentBucketNum, const MetricDimensionKey& key,
                                    const int64_t& currentBucketValue) {
     if (currentBucketNum > mMostRecentBucketNum + 1) {
         // TODO: This creates a needless 0 entry in mSumOverPastBuckets. Fix this.
@@ -175,7 +175,7 @@
             && getSumOverPastBuckets(key) + currentBucketValue > mAlert.trigger_if_sum_gt();
 }
 
-void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const HashableDimensionKey& key) {
+void AnomalyTracker::declareAnomaly(const uint64_t& timestampNs, const MetricDimensionKey& key) {
     // TODO: Why receive timestamp? RefractoryPeriod should always be based on real time right now.
     if (isInRefractoryPeriod(timestampNs, key)) {
         VLOG("Skipping anomaly declaration since within refractory period");
@@ -199,14 +199,14 @@
 
     StatsdStats::getInstance().noteAnomalyDeclared(mConfigKey, mAlert.id());
 
-    // TODO: This should also take in the const HashableDimensionKey& key?
+    // TODO: This should also take in the const MetricDimensionKey& key?
     android::util::stats_write(android::util::ANOMALY_DETECTED, mConfigKey.GetUid(),
                                mConfigKey.GetId(), mAlert.id());
 }
 
 void AnomalyTracker::detectAndDeclareAnomaly(const uint64_t& timestampNs,
                                              const int64_t& currBucketNum,
-                                             const HashableDimensionKey& key,
+                                             const MetricDimensionKey& key,
                                              const int64_t& currentBucketValue) {
     if (detectAnomaly(currBucketNum, key, currentBucketValue)) {
         declareAnomaly(timestampNs, key);
@@ -214,7 +214,7 @@
 }
 
 bool AnomalyTracker::isInRefractoryPeriod(const uint64_t& timestampNs,
-                                          const HashableDimensionKey& key) {
+                                          const MetricDimensionKey& key) {
     const auto& it = mRefractoryPeriodEndsSec.find(key);
     if (it != mRefractoryPeriodEndsSec.end()) {
         if ((timestampNs / NS_PER_SEC) <= it->second) {
@@ -226,7 +226,7 @@
     return false;
 }
 
-void AnomalyTracker::informSubscribers(const HashableDimensionKey& key) {
+void AnomalyTracker::informSubscribers(const MetricDimensionKey& key) {
     VLOG("informSubscribers called.");
     if (mSubscriptions.empty()) {
         ALOGE("Attempt to call with no subscribers.");
diff --git a/bin/src/anomaly/AnomalyTracker.h b/bin/src/anomaly/AnomalyTracker.h
index 472c02c..f01a97f 100644
--- a/bin/src/anomaly/AnomalyTracker.h
+++ b/bin/src/anomaly/AnomalyTracker.h
@@ -48,19 +48,19 @@
     // Adds a bucket.
     // Bucket index starts from 0.
     void addPastBucket(std::shared_ptr<DimToValMap> bucketValues, const int64_t& bucketNum);
-    void addPastBucket(const HashableDimensionKey& key, const int64_t& bucketValue,
+    void addPastBucket(const MetricDimensionKey& key, const int64_t& bucketValue,
                        const int64_t& bucketNum);
 
     // Returns true if detected anomaly for the existing buckets on one or more dimension keys.
-    bool detectAnomaly(const int64_t& currBucketNum, const HashableDimensionKey& key,
+    bool detectAnomaly(const int64_t& currBucketNum, const MetricDimensionKey& key,
                        const int64_t& currentBucketValue);
 
     // Informs incidentd about the detected alert.
-    void declareAnomaly(const uint64_t& timestampNs, const HashableDimensionKey& key);
+    void declareAnomaly(const uint64_t& timestampNs, const MetricDimensionKey& key);
 
     // Detects the alert and informs the incidentd when applicable.
     void detectAndDeclareAnomaly(const uint64_t& timestampNs, const int64_t& currBucketNum,
-                                 const HashableDimensionKey& key,
+                                 const MetricDimensionKey& key,
                                  const int64_t& currentBucketValue);
 
     // Init the AnomalyMonitor which is shared across anomaly trackers.
@@ -69,10 +69,10 @@
     }
 
     // Helper function to return the sum value of past buckets at given dimension.
-    int64_t getSumOverPastBuckets(const HashableDimensionKey& key) const;
+    int64_t getSumOverPastBuckets(const MetricDimensionKey& key) const;
 
     // Helper function to return the value for a past bucket.
-    int64_t getPastBucketValue(const HashableDimensionKey& key, const int64_t& bucketNum) const;
+    int64_t getPastBucketValue(const MetricDimensionKey& key, const int64_t& bucketNum) const;
 
     // Returns the anomaly threshold.
     inline int64_t getAnomalyThreshold() const {
@@ -81,7 +81,7 @@
 
     // Returns the refractory period timestamp (in seconds) for the given key.
     // If there is no stored refractory period ending timestamp, returns 0.
-    uint32_t getRefractoryPeriodEndsSec(const HashableDimensionKey& key) const {
+    uint32_t getRefractoryPeriodEndsSec(const MetricDimensionKey& key) const {
         const auto& it = mRefractoryPeriodEndsSec.find(key);
         return it != mRefractoryPeriodEndsSec.end() ? it->second : 0;
     }
@@ -124,7 +124,7 @@
     // declared for that dimension) ends, in seconds. Only anomalies that occur after this period
     // ends will be declared.
     // Entries may be, but are not guaranteed to be, removed after the period is finished.
-    unordered_map<HashableDimensionKey, uint32_t> mRefractoryPeriodEndsSec;
+    unordered_map<MetricDimensionKey, uint32_t> mRefractoryPeriodEndsSec;
 
     void flushPastBuckets(const int64_t& currBucketNum);
 
@@ -135,7 +135,7 @@
     // and remove any items with value 0.
     void subtractBucketFromSum(const shared_ptr<DimToValMap>& bucket);
 
-    bool isInRefractoryPeriod(const uint64_t& timestampNs, const HashableDimensionKey& key);
+    bool isInRefractoryPeriod(const uint64_t& timestampNs, const MetricDimensionKey& key);
 
     // Calculates the corresponding bucket index within the circular array.
     size_t index(int64_t bucketNum) const;
@@ -144,7 +144,7 @@
     virtual void resetStorage();
 
     // Informs the subscribers that an anomaly has occurred.
-    void informSubscribers(const HashableDimensionKey& key);
+    void informSubscribers(const MetricDimensionKey& key);
 
     FRIEND_TEST(AnomalyTrackerTest, TestConsecutiveBuckets);
     FRIEND_TEST(AnomalyTrackerTest, TestSparseBuckets);
diff --git a/bin/src/anomaly/DurationAnomalyTracker.cpp b/bin/src/anomaly/DurationAnomalyTracker.cpp
index 7576a38..bbee9fa 100644
--- a/bin/src/anomaly/DurationAnomalyTracker.cpp
+++ b/bin/src/anomaly/DurationAnomalyTracker.cpp
@@ -37,8 +37,8 @@
     if (!mAlarms.empty()) VLOG("AnomalyTracker.resetStorage() called but mAlarms is NOT empty!");
 }
 
-void DurationAnomalyTracker::declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey,
-                                                  const uint64_t& timestampNs) {
+void DurationAnomalyTracker::declareAnomalyIfAlarmExpired(const MetricDimensionKey& dimensionKey,
+                                                          const uint64_t& timestampNs) {
     auto itr = mAlarms.find(dimensionKey);
     if (itr == mAlarms.end()) {
         return;
@@ -51,7 +51,7 @@
     }
 }
 
-void DurationAnomalyTracker::startAlarm(const HashableDimensionKey& dimensionKey,
+void DurationAnomalyTracker::startAlarm(const MetricDimensionKey& dimensionKey,
                                 const uint64_t& timestampNs) {
 
     uint32_t timestampSec = static_cast<uint32_t>(timestampNs / NS_PER_SEC);
@@ -66,7 +66,7 @@
     }
 }
 
-void DurationAnomalyTracker::stopAlarm(const HashableDimensionKey& dimensionKey) {
+void DurationAnomalyTracker::stopAlarm(const MetricDimensionKey& dimensionKey) {
     auto itr = mAlarms.find(dimensionKey);
     if (itr != mAlarms.end()) {
         mAlarms.erase(dimensionKey);
@@ -77,7 +77,7 @@
 }
 
 void DurationAnomalyTracker::stopAllAlarms() {
-    std::set<HashableDimensionKey> keys;
+    std::set<MetricDimensionKey> keys;
     for (auto itr = mAlarms.begin(); itr != mAlarms.end(); ++itr) {
         keys.insert(itr->first);
     }
@@ -95,7 +95,7 @@
     // seldomly called. The alternative would be having AnomalyAlarms store information about the
     // DurationAnomalyTracker and key, but that's a lot of data overhead to speed up something that is
     // rarely ever called.
-    unordered_map<HashableDimensionKey, sp<const AnomalyAlarm>> matchedAlarms;
+    unordered_map<MetricDimensionKey, sp<const AnomalyAlarm>> matchedAlarms;
     for (const auto& kv : mAlarms) {
         if (firedAlarms.count(kv.second) > 0) {
             matchedAlarms.insert({kv.first, kv.second});
diff --git a/bin/src/anomaly/DurationAnomalyTracker.h b/bin/src/anomaly/DurationAnomalyTracker.h
index 33e55ab..052fdf5 100644
--- a/bin/src/anomaly/DurationAnomalyTracker.h
+++ b/bin/src/anomaly/DurationAnomalyTracker.h
@@ -32,10 +32,10 @@
     virtual ~DurationAnomalyTracker();
 
     // Starts the alarm at the given timestamp.
-    void startAlarm(const HashableDimensionKey& dimensionKey, const uint64_t& eventTime);
+    void startAlarm(const MetricDimensionKey& dimensionKey, const uint64_t& eventTime);
 
     // Stops the alarm.
-    void stopAlarm(const HashableDimensionKey& dimensionKey);
+    void stopAlarm(const MetricDimensionKey& dimensionKey);
 
     // Stop all the alarms owned by this tracker.
     void stopAllAlarms();
@@ -46,7 +46,7 @@
     }
 
     // Declares the anomaly when the alarm expired given the current timestamp.
-    void declareAnomalyIfAlarmExpired(const HashableDimensionKey& dimensionKey,
+    void declareAnomalyIfAlarmExpired(const MetricDimensionKey& dimensionKey,
                                       const uint64_t& timestampNs);
 
     // Declares an anomaly for each alarm in firedAlarms that belongs to this DurationAnomalyTracker
@@ -59,7 +59,7 @@
 protected:
     // The alarms owned by this tracker. The alarm monitor also shares the alarm pointers when they
     // are still active.
-    std::unordered_map<HashableDimensionKey, sp<const AnomalyAlarm>> mAlarms;
+    std::unordered_map<MetricDimensionKey, sp<const AnomalyAlarm>> mAlarms;
 
     // Anomaly alarm monitor.
     sp<AnomalyMonitor> mAnomalyMonitor;
diff --git a/bin/src/condition/CombinationConditionTracker.cpp b/bin/src/condition/CombinationConditionTracker.cpp
index ea6586c..4c20ccb 100644
--- a/bin/src/condition/CombinationConditionTracker.cpp
+++ b/bin/src/condition/CombinationConditionTracker.cpp
@@ -78,6 +78,7 @@
             return false;
         }
 
+
         bool initChildSucceeded = childTracker->init(allConditionConfig, allConditionTrackers,
                                                      conditionIdIndexMap, stack);
 
@@ -88,8 +89,10 @@
             ALOGW("Child initialization success %lld ", (long long)child);
         }
 
+        if (allConditionTrackers[childIndex]->isSliced()) {
+            setSliced(true);
+        }
         mChildren.push_back(childIndex);
-
         mTrackerIndex.insert(childTracker->getLogTrackerIndex().begin(),
                              childTracker->getLogTrackerIndex().end());
     }
@@ -105,11 +108,15 @@
 void CombinationConditionTracker::isConditionMet(
         const ConditionKey& conditionParameters,
         const vector<sp<ConditionTracker>>& allConditions,
-        vector<ConditionState>& conditionCache) const {
+        const FieldMatcher& dimensionFields,
+        vector<ConditionState>& conditionCache,
+        std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+    // So far, this is fine as there is at most one child having sliced output.
     for (const int childIndex : mChildren) {
         if (conditionCache[childIndex] == ConditionState::kNotEvaluated) {
             allConditions[childIndex]->isConditionMet(conditionParameters, allConditions,
-                                                      conditionCache);
+                                                      dimensionFields, conditionCache,
+                                                      dimensionsKeySet);
         }
     }
     conditionCache[mIndex] =
@@ -127,6 +134,7 @@
     }
 
     for (const int childIndex : mChildren) {
+        // So far, this is fine as there is at most one child having sliced output.
         if (nonSlicedConditionCache[childIndex] == ConditionState::kNotEvaluated) {
             const sp<ConditionTracker>& child = mAllConditions[childIndex];
             child->evaluateCondition(event, eventMatcherValues, mAllConditions,
@@ -159,6 +167,24 @@
     }
 }
 
+ConditionState CombinationConditionTracker::getMetConditionDimension(
+        const std::vector<sp<ConditionTracker>>& allConditions,
+        const FieldMatcher& dimensionFields,
+        std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+    vector<ConditionState> conditionCache(allConditions.size(), ConditionState::kNotEvaluated);
+    // So far, this is fine as there is at most one child having sliced output.
+    for (const int childIndex : mChildren) {
+        conditionCache[childIndex] = conditionCache[childIndex] |
+            allConditions[childIndex]->getMetConditionDimension(
+                allConditions, dimensionFields, dimensionsKeySet);
+    }
+    evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
+    if (conditionCache[mIndex] == ConditionState::kTrue && dimensionsKeySet.empty()) {
+        dimensionsKeySet.insert(DEFAULT_DIMENSION_KEY);
+    }
+    return conditionCache[mIndex];
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/bin/src/condition/CombinationConditionTracker.h b/bin/src/condition/CombinationConditionTracker.h
index dfd3837..0b7f949 100644
--- a/bin/src/condition/CombinationConditionTracker.h
+++ b/bin/src/condition/CombinationConditionTracker.h
@@ -41,12 +41,20 @@
                            std::vector<ConditionState>& conditionCache,
                            std::vector<bool>& changedCache) override;
 
-    void isConditionMet(const ConditionKey& conditionParameters,
-                        const std::vector<sp<ConditionTracker>>& allConditions,
-                        std::vector<ConditionState>& conditionCache) const override;
+    void isConditionMet(
+        const ConditionKey& conditionParameters,
+        const std::vector<sp<ConditionTracker>>& allConditions,
+        const FieldMatcher& dimensionFields,
+        std::vector<ConditionState>& conditionCache,
+        std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
 
+    ConditionState getMetConditionDimension(
+            const std::vector<sp<ConditionTracker>>& allConditions,
+            const FieldMatcher& dimensionFields,
+            std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
 private:
     LogicalOperation mLogicalOperation;
+
     // Store index of the children Predicates.
     // We don't store string name of the Children, because we want to get rid of the hash map to
     // map the name to object. We don't want to store smart pointers to children, because it
diff --git a/bin/src/condition/ConditionTracker.h b/bin/src/condition/ConditionTracker.h
index 773860f..81abbdb 100644
--- a/bin/src/condition/ConditionTracker.h
+++ b/bin/src/condition/ConditionTracker.h
@@ -24,6 +24,7 @@
 #include <log/logprint.h>
 #include <utils/RefBase.h>
 
+#include <unordered_set>
 #include <unordered_map>
 
 namespace android {
@@ -84,10 +85,19 @@
     // [allConditions]: all condition trackers. This is needed because the condition evaluation is
     //                  done recursively
     // [conditionCache]: the cache holding the condition evaluation values.
+    // [dimensionsKeySet]: the dimensions where the sliced condition is true. For combination
+    //                    condition, it assumes that only one child predicate is sliced.
     virtual void isConditionMet(
             const ConditionKey& conditionParameters,
             const std::vector<sp<ConditionTracker>>& allConditions,
-            std::vector<ConditionState>& conditionCache) const = 0;
+            const FieldMatcher& dimensionFields,
+            std::vector<ConditionState>& conditionCache,
+            std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const = 0;
+
+    virtual ConditionState getMetConditionDimension(
+            const std::vector<sp<ConditionTracker>>& allConditions,
+            const FieldMatcher& dimensionFields,
+            std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const = 0;
 
     // return the list of LogMatchingTracker index that this ConditionTracker uses.
     virtual const std::set<int>& getLogTrackerIndex() const {
@@ -98,6 +108,10 @@
         mSliced = mSliced | sliced;
     }
 
+    bool isSliced() const {
+        return mSliced;
+    }
+
 protected:
     const int64_t mConditionId;
 
diff --git a/bin/src/condition/ConditionWizard.cpp b/bin/src/condition/ConditionWizard.cpp
index d99c2cc..0427700 100644
--- a/bin/src/condition/ConditionWizard.cpp
+++ b/bin/src/condition/ConditionWizard.cpp
@@ -14,6 +14,7 @@
  * limitations under the License.
  */
 #include "ConditionWizard.h"
+#include <unordered_set>
 
 namespace android {
 namespace os {
@@ -23,14 +24,26 @@
 using std::string;
 using std::vector;
 
-ConditionState ConditionWizard::query(const int index,
-                                      const ConditionKey& parameters) {
+ConditionState ConditionWizard::query(
+    const int index, const ConditionKey& parameters,
+    const FieldMatcher& dimensionFields,
+    std::unordered_set<HashableDimensionKey> *dimensionKeySet) {
+
     vector<ConditionState> cache(mAllConditions.size(), ConditionState::kNotEvaluated);
 
-    mAllConditions[index]->isConditionMet(parameters, mAllConditions, cache);
+    mAllConditions[index]->isConditionMet(
+        parameters, mAllConditions, dimensionFields, cache, *dimensionKeySet);
     return cache[index];
 }
 
+ConditionState ConditionWizard::getMetConditionDimension(
+    const int index, const FieldMatcher& dimensionFields,
+    std::unordered_set<HashableDimensionKey> *dimensionsKeySet) const {
+
+    return mAllConditions[index]->getMetConditionDimension(mAllConditions, dimensionFields,
+                                 *dimensionsKeySet);
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
\ No newline at end of file
diff --git a/bin/src/condition/ConditionWizard.h b/bin/src/condition/ConditionWizard.h
index 4ff5c07..b38b59f 100644
--- a/bin/src/condition/ConditionWizard.h
+++ b/bin/src/condition/ConditionWizard.h
@@ -41,7 +41,14 @@
     // the conditionParameters contains the parameters for it's children SimpleConditionTrackers.
     virtual ConditionState query(
             const int conditionIndex,
-            const ConditionKey& conditionParameters);
+            const ConditionKey& conditionParameters,
+            const FieldMatcher& dimensionFields,
+            std::unordered_set<HashableDimensionKey> *dimensionKeySet);
+
+    virtual ConditionState getMetConditionDimension(
+            const int index,
+            const FieldMatcher& dimensionFields,
+            std::unordered_set<HashableDimensionKey> *dimensionsKeySet) const;
 
 private:
     std::vector<sp<ConditionTracker>> mAllConditions;
diff --git a/bin/src/condition/SimpleConditionTracker.cpp b/bin/src/condition/SimpleConditionTracker.cpp
index 5cfc349..25265d5 100644
--- a/bin/src/condition/SimpleConditionTracker.cpp
+++ b/bin/src/condition/SimpleConditionTracker.cpp
@@ -104,6 +104,9 @@
                                   vector<bool>& stack) {
     // SimpleConditionTracker does not have dependency on other conditions, thus we just return
     // if the initialization was successful.
+    if (mOutputDimensions.has_field() || mOutputDimensions.child_size() > 0) {
+        setSliced(true);
+    }
     return mInitialized;
 }
 
@@ -234,11 +237,12 @@
          conditionChangedCache[mIndex] == true);
 }
 
-void SimpleConditionTracker::evaluateCondition(const LogEvent& event,
-                                               const vector<MatchingState>& eventMatcherValues,
-                                               const vector<sp<ConditionTracker>>& mAllConditions,
-                                               vector<ConditionState>& conditionCache,
-                                               vector<bool>& conditionChangedCache) {
+void SimpleConditionTracker::evaluateCondition(
+        const LogEvent& event,
+        const vector<MatchingState>& eventMatcherValues,
+        const vector<sp<ConditionTracker>>& mAllConditions,
+        vector<ConditionState>& conditionCache,
+        vector<bool>& conditionChangedCache) {
     if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
         // it has been evaluated.
         VLOG("Yes, already evaluated, %lld %d",
@@ -271,7 +275,7 @@
         if (mSliced) {
             // if the condition result is sliced. metrics won't directly get value from the
             // cache, so just set any value other than kNotEvaluated.
-            conditionCache[mIndex] = ConditionState::kUnknown;
+            conditionCache[mIndex] = mInitialValue;
         } else {
             const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
             if (itr == mSlicedConditionState.end()) {
@@ -310,10 +314,8 @@
             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);
         }
@@ -323,42 +325,73 @@
 void SimpleConditionTracker::isConditionMet(
         const ConditionKey& conditionParameters,
         const vector<sp<ConditionTracker>>& allConditions,
-        vector<ConditionState>& conditionCache) const {
-    const auto pair = conditionParameters.find(mConditionId);
-
-    if (pair == conditionParameters.end() && mOutputDimensions.child_size() > 0) {
-        ALOGE("Predicate %lld output has dimension, but it's not specified in the query!",
-              (long long)mConditionId);
-        conditionCache[mIndex] = mInitialValue;
+        const FieldMatcher& dimensionFields,
+        vector<ConditionState>& conditionCache,
+        std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+    if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
+        // it has been evaluated.
+        VLOG("Yes, already evaluated, %lld %d",
+            (long long)mConditionId, conditionCache[mIndex]);
         return;
     }
-    std::vector<HashableDimensionKey> defaultKeys = {DEFAULT_DIMENSION_KEY};
+    const auto pair = conditionParameters.find(mConditionId);
+
+    if (pair == conditionParameters.end()) {
+        ConditionState conditionState = ConditionState::kNotEvaluated;
+        if (dimensionFields.has_field() && dimensionFields.child_size() > 0 &&
+            dimensionFields.field() == mOutputDimensions.field()) {
+            conditionState = conditionState | getMetConditionDimension(
+                allConditions, dimensionFields, dimensionsKeySet);
+        } else {
+            conditionState = conditionState | mInitialValue;
+            if (!mSliced) {
+                const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
+                if (itr != mSlicedConditionState.end()) {
+                    ConditionState sliceState =
+                        itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+                    conditionState = conditionState | sliceState;
+                }
+            }
+        }
+        conditionCache[mIndex] = conditionState;
+        return;
+    }
+    std::vector<HashableDimensionKey> defaultKeys = { DEFAULT_DIMENSION_KEY };
     const std::vector<HashableDimensionKey> &keys =
             (pair == conditionParameters.end()) ? defaultKeys : pair->second;
 
     ConditionState conditionState = ConditionState::kNotEvaluated;
-    for (const auto& key : keys) {
+    for (size_t i = 0; i < keys.size(); ++i) {
+        const HashableDimensionKey& key = keys[i];
         auto startedCountIt = mSlicedConditionState.find(key);
         if (startedCountIt != mSlicedConditionState.end()) {
-            conditionState = conditionState |
-                    (startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse);
+            ConditionState sliceState =
+                startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+            conditionState = conditionState | sliceState;
+            if (sliceState == ConditionState::kTrue && dimensionFields.has_field()) {
+                HashableDimensionKey dimensionKey;
+                if (getSubDimension(startedCountIt->first.getDimensionsValue(), dimensionFields,
+                                    dimensionKey.getMutableDimensionsValue())) {
+                    dimensionsKeySet.insert(dimensionKey);
+                }
+            }
         } else {
             // For unseen key, check whether the require dimensions are subset of sliced condition
             // output.
-            bool seenDimension = false;
+            conditionState = conditionState | mInitialValue;
             for (const auto& slice : mSlicedConditionState) {
-                if (IsSubDimension(slice.first.getDimensionsValue(),
-                                   key.getDimensionsValue())) {
-                    seenDimension = true;
-                    conditionState = conditionState |
-                        (slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse);
+                ConditionState sliceState =
+                    slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+                if (IsSubDimension(slice.first.getDimensionsValue(), key.getDimensionsValue())) {
+                    conditionState = conditionState | sliceState;
+                    if (sliceState == ConditionState::kTrue && dimensionFields.has_field()) {
+                        HashableDimensionKey dimensionKey;
+                        if (getSubDimension(slice.first.getDimensionsValue(),
+                                            dimensionFields, dimensionKey.getMutableDimensionsValue())) {
+                            dimensionsKeySet.insert(dimensionKey);
+                        }
+                    }
                 }
-                if (conditionState == ConditionState::kTrue) {
-                    break;
-                }
-            }
-            if (!seenDimension) {
-                conditionState = conditionState | mInitialValue;
             }
         }
     }
@@ -366,6 +399,38 @@
     VLOG("Predicate %lld return %d", (long long)mConditionId, conditionCache[mIndex]);
 }
 
+ConditionState SimpleConditionTracker::getMetConditionDimension(
+        const std::vector<sp<ConditionTracker>>& allConditions,
+        const FieldMatcher& dimensionFields,
+        std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const {
+    ConditionState conditionState = mInitialValue;
+    if (!dimensionFields.has_field() ||
+        !mOutputDimensions.has_field() ||
+        dimensionFields.field() != mOutputDimensions.field()) {
+        const auto& itr = mSlicedConditionState.find(DEFAULT_DIMENSION_KEY);
+        if (itr != mSlicedConditionState.end()) {
+            ConditionState sliceState =
+                itr->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+            conditionState = conditionState | sliceState;
+        }
+        return conditionState;
+    }
+
+    for (const auto& slice : mSlicedConditionState) {
+        ConditionState sliceState =
+            slice.second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+        DimensionsValue dimensionsValue;
+        conditionState = conditionState | sliceState;
+        HashableDimensionKey dimensionKey;
+        if (sliceState == ConditionState::kTrue &&
+            getSubDimension(slice.first.getDimensionsValue(), dimensionFields,
+                            dimensionKey.getMutableDimensionsValue())) {
+            dimensionsKeySet.insert(dimensionKey);
+        }
+    }
+    return conditionState;
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/bin/src/condition/SimpleConditionTracker.h b/bin/src/condition/SimpleConditionTracker.h
index 815b445..ce9a02d 100644
--- a/bin/src/condition/SimpleConditionTracker.h
+++ b/bin/src/condition/SimpleConditionTracker.h
@@ -48,7 +48,14 @@
 
     void isConditionMet(const ConditionKey& conditionParameters,
                         const std::vector<sp<ConditionTracker>>& allConditions,
-                        std::vector<ConditionState>& conditionCache) const override;
+                        const FieldMatcher& dimensionFields,
+                        std::vector<ConditionState>& conditionCache,
+                        std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
+
+    ConditionState getMetConditionDimension(
+            const std::vector<sp<ConditionTracker>>& allConditions,
+            const FieldMatcher& dimensionFields,
+            std::unordered_set<HashableDimensionKey> &dimensionsKeySet) const override;
 
 private:
     const ConfigKey mConfigKey;
@@ -73,7 +80,8 @@
     void handleStopAll(std::vector<ConditionState>& conditionCache,
                        std::vector<bool>& changedCache);
 
-    void handleConditionEvent(const HashableDimensionKey& outputKey, bool matchStart,
+    void handleConditionEvent(const HashableDimensionKey& outputKey,
+                              bool matchStart,
                               std::vector<ConditionState>& conditionCache,
                               std::vector<bool>& changedCache);
 
diff --git a/bin/src/condition/condition_util.cpp b/bin/src/condition/condition_util.cpp
index 3b2d480..0ab33cf 100644
--- a/bin/src/condition/condition_util.cpp
+++ b/bin/src/condition/condition_util.cpp
@@ -118,6 +118,9 @@
 
 void getFieldsFromFieldMatcher(const FieldMatcher& matcher, Field* rootField, Field* leafField,
                                std::vector<Field> *allFields) {
+    if (matcher.has_position()) {
+        leafField->set_position_index(0);
+    }
     if (matcher.child_size() == 0) {
         allFields->push_back(*rootField);
         return;
diff --git a/bin/src/dimension.cpp b/bin/src/dimension.cpp
index 04445ca..8a2e871 100644
--- a/bin/src/dimension.cpp
+++ b/bin/src/dimension.cpp
@@ -253,6 +253,9 @@
 }
 
 void DimensionsValueToString(const DimensionsValue& value, std::string *flattened) {
+    if (!value.has_field()) {
+        return;
+    }
     *flattened += std::to_string(value.field());
     *flattened += ":";
     switch (value.value_case()) {
@@ -352,6 +355,46 @@
     }
 }
 
+bool getSubDimension(const DimensionsValue& dimension, const FieldMatcher& matcher,
+                     DimensionsValue* subDimension) {
+    if (!matcher.has_field()) {
+        return false;
+    }
+    if (matcher.field() != dimension.field()) {
+        return false;
+    }
+    if (matcher.child_size() <= 0) {
+        if (dimension.value_case() == DimensionsValue::ValueCase::kValueTuple ||
+            dimension.value_case() == DimensionsValue::ValueCase::VALUE_NOT_SET) {
+            return false;
+        }
+        *subDimension = dimension;
+        return true;
+    } else {
+        if (dimension.value_case() != DimensionsValue::ValueCase::kValueTuple) {
+            return false;
+        }
+        bool found_value = true;
+        auto value_tuple = dimension.value_tuple();
+        subDimension->set_field(dimension.field());
+        for (int i = 0; found_value && i < matcher.child_size(); ++i) {
+            int j = 0;
+            for (; j < value_tuple.dimensions_value_size(); ++j) {
+                if (value_tuple.dimensions_value(j).field() == matcher.child(i).field()) {
+                    break;
+                }
+            }
+            if (j < value_tuple.dimensions_value_size()) {
+                found_value &= getSubDimension(value_tuple.dimensions_value(j), matcher.child(i),
+                    subDimension->mutable_value_tuple()->add_dimensions_value());
+            } else {
+                found_value = false;
+            }
+        }
+        return found_value;
+    }
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/bin/src/dimension.h b/bin/src/dimension.h
index e900c5e..138c6e9 100644
--- a/bin/src/dimension.h
+++ b/bin/src/dimension.h
@@ -63,6 +63,9 @@
 
 // Helper function to get long value from the DimensionsValue proto.
 long getLongFromDimenValue(const DimensionsValue& dimensionValue);
+
+bool getSubDimension(const DimensionsValue& dimension, const FieldMatcher& matcher,
+                    DimensionsValue* subDimension);
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/bin/src/metrics/CountMetricProducer.cpp b/bin/src/metrics/CountMetricProducer.cpp
index 0455f6a..ae4df3e 100644
--- a/bin/src/metrics/CountMetricProducer.cpp
+++ b/bin/src/metrics/CountMetricProducer.cpp
@@ -21,6 +21,7 @@
 #include "guardrail/StatsdStats.h"
 #include "stats_util.h"
 #include "stats_log_util.h"
+#include "dimension.h"
 
 #include <limits.h>
 #include <stdlib.h>
@@ -71,13 +72,15 @@
     }
 
     // TODO: use UidMap if uid->pkg_name is required
-    mDimensions = metric.dimensions_in_what();
+    mDimensionsInWhat = metric.dimensions_in_what();
+    mDimensionsInCondition = metric.dimensions_in_condition();
 
     if (metric.links().size() > 0) {
         mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
                                metric.links().end());
-        mConditionSliced = true;
     }
+    mConditionSliced = (metric.links().size() > 0)||
+        (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
 
     VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
          (long long)mBucketSizeNs, (long long)mStartTimeNs);
@@ -99,7 +102,10 @@
     auto count_metrics = report->mutable_count_metrics();
     for (const auto& counter : mPastBuckets) {
         CountMetricData* metricData = count_metrics->add_data();
-        *metricData->mutable_dimensions_in_what() = counter.first.getDimensionsValue();
+        *metricData->mutable_dimensions_in_what() =
+            counter.first.getDimensionKeyInWhat().getDimensionsValue();
+        *metricData->mutable_dimensions_in_condition() =
+            counter.first.getDimensionKeyInCondition().getDimensionsValue();
         for (const auto& bucket : counter.second) {
             CountBucketInfo* bucketInfo = metricData->add_bucket_info();
             bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
@@ -123,17 +129,26 @@
     VLOG("metric %lld dump report now...",(long long)mMetricId);
 
     for (const auto& counter : mPastBuckets) {
-        const HashableDimensionKey& hashableKey = counter.first;
-        VLOG("  dimension key %s", hashableKey.c_str());
+        const MetricDimensionKey& dimensionKey = counter.first;
+        VLOG("  dimension key %s", dimensionKey.c_str());
 
         long long wrapperToken =
                 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
 
         // First fill dimension.
-        long long dimensionToken = protoOutput->start(
+        long long dimensionInWhatToken = protoOutput->start(
                 FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
-        writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
-        protoOutput->end(dimensionToken);
+        writeDimensionsValueProtoToStream(
+            dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
+        protoOutput->end(dimensionInWhatToken);
+
+        if (dimensionKey.hasDimensionKeyInCondition()) {
+            long long dimensionInConditionToken = protoOutput->start(
+                    FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+            writeDimensionsValueProtoToStream(
+                dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+            protoOutput->end(dimensionInConditionToken);
+        }
 
         // Then fill bucket_info (CountBucketInfo).
         for (const auto& bucket : counter.second) {
@@ -166,7 +181,7 @@
     mCondition = conditionMet;
 }
 
-bool CountMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
+bool CountMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
     if (mCurrentSlicedCounter->find(newKey) != mCurrentSlicedCounter->end()) {
         return false;
     }
@@ -187,7 +202,7 @@
 }
 
 void CountMetricProducer::onMatchedLogEventInternalLocked(
-        const size_t matcherIndex, const HashableDimensionKey& eventKey,
+        const size_t matcherIndex, const MetricDimensionKey& eventKey,
         const ConditionKey& conditionKey, bool condition,
         const LogEvent& event) {
     uint64_t eventTimeNs = event.GetTimestampNs();
diff --git a/bin/src/metrics/CountMetricProducer.h b/bin/src/metrics/CountMetricProducer.h
index 061b7a3..8659d47 100644
--- a/bin/src/metrics/CountMetricProducer.h
+++ b/bin/src/metrics/CountMetricProducer.h
@@ -50,7 +50,7 @@
 
 protected:
     void onMatchedLogEventInternalLocked(
-            const size_t matcherIndex, const HashableDimensionKey& eventKey,
+            const size_t matcherIndex, const MetricDimensionKey& eventKey,
             const ConditionKey& conditionKey, bool condition,
             const LogEvent& event) override;
 
@@ -74,14 +74,14 @@
     void flushIfNeededLocked(const uint64_t& newEventTime);
 
     // TODO: Add a lock to mPastBuckets.
-    std::unordered_map<HashableDimensionKey, std::vector<CountBucket>> mPastBuckets;
+    std::unordered_map<MetricDimensionKey, std::vector<CountBucket>> mPastBuckets;
 
     // The current bucket.
     std::shared_ptr<DimToValMap> mCurrentSlicedCounter = std::make_shared<DimToValMap>();
 
     static const size_t kBucketSize = sizeof(CountBucket{});
 
-    bool hitGuardRailLocked(const HashableDimensionKey& newKey);
+    bool hitGuardRailLocked(const MetricDimensionKey& newKey);
 
     FRIEND_TEST(CountMetricProducerTest, TestNonDimensionalEvents);
     FRIEND_TEST(CountMetricProducerTest, TestEventsWithNonSlicedCondition);
diff --git a/bin/src/metrics/DurationMetricProducer.cpp b/bin/src/metrics/DurationMetricProducer.cpp
index 000874c..efbdae1 100644
--- a/bin/src/metrics/DurationMetricProducer.cpp
+++ b/bin/src/metrics/DurationMetricProducer.cpp
@@ -21,6 +21,7 @@
 #include "guardrail/StatsdStats.h"
 #include "stats_util.h"
 #include "stats_log_util.h"
+#include "dimension.h"
 
 #include <limits.h>
 #include <stdlib.h>
@@ -81,13 +82,15 @@
     }
 
     // TODO: use UidMap if uid->pkg_name is required
-    mDimensions = metric.dimensions_in_what();
+    mDimensionsInWhat = metric.dimensions_in_what();
+    mDimensionsInCondition = metric.dimensions_in_condition();
 
     if (metric.links().size() > 0) {
         mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
                                metric.links().end());
-        mConditionSliced = true;
     }
+    mConditionSliced = (metric.links().size() > 0)||
+        (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
 
     VLOG("metric %lld created. bucket size %lld start_time: %lld", (long long)metric.id(),
          (long long)mBucketSizeNs, (long long)mStartTimeNs);
@@ -113,15 +116,17 @@
 }
 
 unique_ptr<DurationTracker> DurationMetricProducer::createDurationTracker(
-        const HashableDimensionKey& eventKey) const {
+        const MetricDimensionKey& eventKey) const {
     switch (mAggregationType) {
         case DurationMetric_AggregationType_SUM:
             return make_unique<OringDurationTracker>(
-                    mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
+                    mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
+                    mDimensionsInCondition, mNested,
                     mCurrentBucketStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers);
         case DurationMetric_AggregationType_MAX_SPARSE:
             return make_unique<MaxDurationTracker>(
-                    mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex, mNested,
+                    mConfigKey, mMetricId, eventKey, mWizard, mConditionTrackerIndex,
+                    mDimensionsInCondition, mNested,
                     mCurrentBucketStartTimeNs, mBucketSizeNs, mConditionSliced, mAnomalyTrackers);
     }
 }
@@ -129,10 +134,34 @@
 void DurationMetricProducer::onSlicedConditionMayChangeLocked(const uint64_t eventTime) {
     VLOG("Metric %lld onSlicedConditionMayChange", (long long)mMetricId);
     flushIfNeededLocked(eventTime);
+
     // Now for each of the on-going event, check if the condition has changed for them.
-    for (auto& pair : mCurrentSlicedDuration) {
+    for (auto& pair : mCurrentSlicedDurationTrackerMap) {
         pair.second->onSlicedConditionMayChange(eventTime);
     }
+
+
+    std::unordered_set<HashableDimensionKey> conditionDimensionsKeySet;
+    ConditionState conditionState = mWizard->getMetConditionDimension(
+        mConditionTrackerIndex, mDimensionsInCondition, &conditionDimensionsKeySet);
+
+    bool condition = (conditionState == ConditionState::kTrue);
+    for (auto& pair : mCurrentSlicedDurationTrackerMap) {
+        conditionDimensionsKeySet.erase(pair.first.getDimensionKeyInCondition());
+    }
+    std::unordered_set<MetricDimensionKey> newKeys;
+    for (const auto& conditionDimensionsKey : conditionDimensionsKeySet) {
+        for (auto& pair : mCurrentSlicedDurationTrackerMap) {
+            auto newKey =
+                MetricDimensionKey(pair.first.getDimensionKeyInWhat(), conditionDimensionsKey);
+            if (newKeys.find(newKey) == newKeys.end()) {
+                mCurrentSlicedDurationTrackerMap[newKey] = pair.second->clone(eventTime);
+                mCurrentSlicedDurationTrackerMap[newKey]->setEventKey(newKey);
+                mCurrentSlicedDurationTrackerMap[newKey]->onSlicedConditionMayChange(eventTime);
+            }
+            newKeys.insert(newKey);
+        }
+    }
 }
 
 void DurationMetricProducer::onConditionChangedLocked(const bool conditionMet,
@@ -142,7 +171,7 @@
     flushIfNeededLocked(eventTime);
     // TODO: need to populate the condition change time from the event which triggers the condition
     // change, instead of using current time.
-    for (auto& pair : mCurrentSlicedDuration) {
+    for (auto& pair : mCurrentSlicedDurationTrackerMap) {
         pair.second->onConditionChanged(conditionMet, eventTime);
     }
 }
@@ -155,7 +184,10 @@
     auto duration_metrics = report->mutable_duration_metrics();
     for (const auto& pair : mPastBuckets) {
         DurationMetricData* metricData = duration_metrics->add_data();
-        *metricData->mutable_dimensions_in_what() = pair.first.getDimensionsValue();
+        *metricData->mutable_dimensions_in_what() =
+            pair.first.getDimensionKeyInWhat().getDimensionsValue();
+        *metricData->mutable_dimensions_in_condition() =
+            pair.first.getDimensionKeyInCondition().getDimensionsValue();
         for (const auto& bucket : pair.second) {
             auto bucketInfo = metricData->add_bucket_info();
             bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
@@ -179,8 +211,8 @@
     VLOG("metric %lld dump report now...", (long long)mMetricId);
 
     for (const auto& pair : mPastBuckets) {
-        const HashableDimensionKey& hashableKey = pair.first;
-        VLOG("  dimension key %s", hashableKey.c_str());
+        const MetricDimensionKey& dimensionKey = pair.first;
+        VLOG("  dimension key %s", dimensionKey.c_str());
 
         long long wrapperToken =
                 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
@@ -188,9 +220,18 @@
         // First fill dimension.
         long long dimensionToken = protoOutput->start(
                 FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
-        writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+        writeDimensionsValueProtoToStream(
+            dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
         protoOutput->end(dimensionToken);
 
+        if (dimensionKey.hasDimensionKeyInCondition()) {
+            long long dimensionInConditionToken = protoOutput->start(
+                    FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+            writeDimensionsValueProtoToStream(
+                dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+            protoOutput->end(dimensionInConditionToken);
+        }
+
         // Then fill bucket_info (DurationBucketInfo).
         for (const auto& bucket : pair.second) {
             long long bucketInfoToken = protoOutput->start(
@@ -219,10 +260,11 @@
         return;
     }
     VLOG("flushing...........");
-    for (auto it = mCurrentSlicedDuration.begin(); it != mCurrentSlicedDuration.end();) {
+    for (auto it = mCurrentSlicedDurationTrackerMap.begin();
+            it != mCurrentSlicedDurationTrackerMap.end();) {
         if (it->second->flushIfNeeded(eventTime, &mPastBuckets)) {
             VLOG("erase bucket for key %s", it->first.c_str());
-            it = mCurrentSlicedDuration.erase(it);
+            it = mCurrentSlicedDurationTrackerMap.erase(it);
         } else {
             ++it;
         }
@@ -234,28 +276,28 @@
 }
 
 void DurationMetricProducer::dumpStatesLocked(FILE* out, bool verbose) const {
-    if (mCurrentSlicedDuration.size() == 0) {
+    if (mCurrentSlicedDurationTrackerMap.size() == 0) {
         return;
     }
 
     fprintf(out, "DurationMetric %lld dimension size %lu\n", (long long)mMetricId,
-            (unsigned long)mCurrentSlicedDuration.size());
+            (unsigned long)mCurrentSlicedDurationTrackerMap.size());
     if (verbose) {
-        for (const auto& slice : mCurrentSlicedDuration) {
+        for (const auto& slice : mCurrentSlicedDurationTrackerMap) {
             fprintf(out, "\t%s\n", slice.first.c_str());
             slice.second->dumpStates(out, verbose);
         }
     }
 }
 
-bool DurationMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
+bool DurationMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
     // the key is not new, we are good.
-    if (mCurrentSlicedDuration.find(newKey) != mCurrentSlicedDuration.end()) {
+    if (mCurrentSlicedDurationTrackerMap.find(newKey) != mCurrentSlicedDurationTrackerMap.end()) {
         return false;
     }
     // 1. Report the tuple count if the tuple count > soft limit
-    if (mCurrentSlicedDuration.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
-        size_t newTupleCount = mCurrentSlicedDuration.size() + 1;
+    if (mCurrentSlicedDurationTrackerMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+        size_t newTupleCount = mCurrentSlicedDurationTrackerMap.size() + 1;
         StatsdStats::getInstance().noteMetricDimensionSize(mConfigKey, mMetricId, newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
@@ -268,27 +310,26 @@
 }
 
 void DurationMetricProducer::onMatchedLogEventInternalLocked(
-        const size_t matcherIndex, const HashableDimensionKey& eventKey,
+        const size_t matcherIndex, const MetricDimensionKey& eventKey,
         const ConditionKey& conditionKeys, bool condition,
         const LogEvent& event) {
     flushIfNeededLocked(event.GetTimestampNs());
 
     if (matcherIndex == mStopAllIndex) {
-        for (auto& pair : mCurrentSlicedDuration) {
+        for (auto& pair : mCurrentSlicedDurationTrackerMap) {
             pair.second->noteStopAll(event.GetTimestampNs());
         }
         return;
     }
 
-
-    if (mCurrentSlicedDuration.find(eventKey) == mCurrentSlicedDuration.end()) {
+    if (mCurrentSlicedDurationTrackerMap.find(eventKey) == mCurrentSlicedDurationTrackerMap.end()) {
         if (hitGuardRailLocked(eventKey)) {
             return;
         }
-        mCurrentSlicedDuration[eventKey] = createDurationTracker(eventKey);
+        mCurrentSlicedDurationTrackerMap[eventKey] = createDurationTracker(eventKey);
     }
 
-    auto it = mCurrentSlicedDuration.find(eventKey);
+    auto it = mCurrentSlicedDurationTrackerMap.find(eventKey);
 
     std::vector<DimensionsValue> values;
     getDimensionKeys(event, mInternalDimensions, &values);
@@ -302,10 +343,11 @@
     } else {
         for (const DimensionsValue& value : values) {
             if (matcherIndex == mStartIndex) {
-                it->second->noteStart(HashableDimensionKey(value), condition,
-                                      event.GetTimestampNs(), conditionKeys);
+                it->second->noteStart(
+                    HashableDimensionKey(value), condition, event.GetTimestampNs(), conditionKeys);
             } else if (matcherIndex == mStopIndex) {
-                it->second->noteStop(HashableDimensionKey(value), event.GetTimestampNs(), false);
+                it->second->noteStop(
+                   HashableDimensionKey(value), event.GetTimestampNs(), false);
             }
         }
     }
diff --git a/bin/src/metrics/DurationMetricProducer.h b/bin/src/metrics/DurationMetricProducer.h
index d8cab92..152e570 100644
--- a/bin/src/metrics/DurationMetricProducer.h
+++ b/bin/src/metrics/DurationMetricProducer.h
@@ -50,7 +50,7 @@
 
 protected:
     void onMatchedLogEventInternalLocked(
-            const size_t matcherIndex, const HashableDimensionKey& eventKey,
+            const size_t matcherIndex, const MetricDimensionKey& eventKey,
             const ConditionKey& conditionKeys, bool condition,
             const LogEvent& event) override;
 
@@ -92,21 +92,21 @@
 
     // Save the past buckets and we can clear when the StatsLogReport is dumped.
     // TODO: Add a lock to mPastBuckets.
-    std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>> mPastBuckets;
+    std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>> mPastBuckets;
 
     // The current bucket.
-    std::unordered_map<HashableDimensionKey, std::unique_ptr<DurationTracker>>
-            mCurrentSlicedDuration;
+    std::unordered_map<MetricDimensionKey, std::unique_ptr<DurationTracker>>
+            mCurrentSlicedDurationTrackerMap;
 
     // Helper function to create a duration tracker given the metric aggregation type.
     std::unique_ptr<DurationTracker> createDurationTracker(
-            const HashableDimensionKey& eventKey) const;
+        const MetricDimensionKey& eventKey) const;
 
     // This hides the base class's std::vector<sp<AnomalyTracker>> mAnomalyTrackers
     std::vector<sp<DurationAnomalyTracker>> mAnomalyTrackers;
 
     // Util function to check whether the specified dimension hits the guardrail.
-    bool hitGuardRailLocked(const HashableDimensionKey& newKey);
+    bool hitGuardRailLocked(const MetricDimensionKey& newKey);
 
     static const size_t kBucketSize = sizeof(DurationBucket{});
 
diff --git a/bin/src/metrics/EventMetricProducer.cpp b/bin/src/metrics/EventMetricProducer.cpp
index 25c86d0..820d591 100644
--- a/bin/src/metrics/EventMetricProducer.cpp
+++ b/bin/src/metrics/EventMetricProducer.cpp
@@ -128,7 +128,7 @@
 }
 
 void EventMetricProducer::onMatchedLogEventInternalLocked(
-        const size_t matcherIndex, const HashableDimensionKey& eventKey,
+        const size_t matcherIndex, const MetricDimensionKey& eventKey,
         const ConditionKey& conditionKey, bool condition,
         const LogEvent& event) {
     if (!condition) {
diff --git a/bin/src/metrics/EventMetricProducer.h b/bin/src/metrics/EventMetricProducer.h
index 9da0dd0..935f206 100644
--- a/bin/src/metrics/EventMetricProducer.h
+++ b/bin/src/metrics/EventMetricProducer.h
@@ -45,7 +45,7 @@
 
 private:
     void onMatchedLogEventInternalLocked(
-            const size_t matcherIndex, const HashableDimensionKey& eventKey,
+            const size_t matcherIndex, const MetricDimensionKey& eventKey,
             const ConditionKey& conditionKey, bool condition,
             const LogEvent& event) override;
 
diff --git a/bin/src/metrics/GaugeMetricProducer.cpp b/bin/src/metrics/GaugeMetricProducer.cpp
index 1072c5a..d6cb189 100644
--- a/bin/src/metrics/GaugeMetricProducer.cpp
+++ b/bin/src/metrics/GaugeMetricProducer.cpp
@@ -82,13 +82,15 @@
     mFieldFilter = metric.gauge_fields_filter();
 
     // TODO: use UidMap if uid->pkg_name is required
-    mDimensions = metric.dimensions_in_what();
+    mDimensionsInWhat = metric.dimensions_in_what();
+    mDimensionsInCondition = metric.dimensions_in_condition();
 
     if (metric.links().size() > 0) {
         mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
                                metric.links().end());
-        mConditionSliced = true;
     }
+    mConditionSliced = (metric.links().size() > 0)||
+        (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
 
     // Kicks off the puller immediately.
     if (mPullTagId != -1 && mSamplingType == GaugeMetric::RANDOM_ONE_SAMPLE) {
@@ -136,18 +138,27 @@
     long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_GAUGE_METRICS);
 
     for (const auto& pair : mPastBuckets) {
-        const HashableDimensionKey& hashableKey = pair.first;
+        const MetricDimensionKey& dimensionKey = pair.first;
 
-        VLOG("  dimension key %s", hashableKey.c_str());
+        VLOG("  dimension key %s", dimensionKey.c_str());
         long long wrapperToken =
                 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
 
         // First fill dimension.
         long long dimensionToken = protoOutput->start(
                 FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
-        writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+        writeDimensionsValueProtoToStream(
+            dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
         protoOutput->end(dimensionToken);
 
+        if (dimensionKey.hasDimensionKeyInCondition()) {
+            long long dimensionInConditionToken = protoOutput->start(
+                    FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+            writeDimensionsValueProtoToStream(
+                dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+            protoOutput->end(dimensionInConditionToken);
+        }
+
         // Then fill bucket_info (GaugeBucketInfo).
         for (const auto& bucket : pair.second) {
             long long bucketInfoToken = protoOutput->start(
@@ -248,7 +259,7 @@
     }
 }
 
-bool GaugeMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
+bool GaugeMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
     if (mCurrentSlicedBucket->find(newKey) != mCurrentSlicedBucket->end()) {
         return false;
     }
@@ -268,7 +279,7 @@
 }
 
 void GaugeMetricProducer::onMatchedLogEventInternalLocked(
-        const size_t matcherIndex, const HashableDimensionKey& eventKey,
+        const size_t matcherIndex, const MetricDimensionKey& eventKey,
         const ConditionKey& conditionKey, bool condition,
         const LogEvent& event) {
     if (condition == false) {
diff --git a/bin/src/metrics/GaugeMetricProducer.h b/bin/src/metrics/GaugeMetricProducer.h
index 6c01347..86d0ccd 100644
--- a/bin/src/metrics/GaugeMetricProducer.h
+++ b/bin/src/metrics/GaugeMetricProducer.h
@@ -44,7 +44,7 @@
     uint64_t mBucketNum;
 };
 
-typedef std::unordered_map<HashableDimensionKey, std::vector<GaugeAtom>>
+typedef std::unordered_map<MetricDimensionKey, std::vector<GaugeAtom>>
     DimToGaugeAtomsMap;
 
 // This gauge metric producer first register the puller to automatically pull the gauge at the
@@ -64,7 +64,7 @@
 
 protected:
     void onMatchedLogEventInternalLocked(
-            const size_t matcherIndex, const HashableDimensionKey& eventKey,
+            const size_t matcherIndex, const MetricDimensionKey& eventKey,
             const ConditionKey& conditionKey, bool condition,
             const LogEvent& event) override;
 
@@ -99,7 +99,7 @@
 
     // Save the past buckets and we can clear when the StatsLogReport is dumped.
     // TODO: Add a lock to mPastBuckets.
-    std::unordered_map<HashableDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
+    std::unordered_map<MetricDimensionKey, std::vector<GaugeBucket>> mPastBuckets;
 
     // The current bucket.
     std::shared_ptr<DimToGaugeAtomsMap> mCurrentSlicedBucket;
@@ -119,7 +119,7 @@
     std::shared_ptr<FieldValueMap> getGaugeFields(const LogEvent& event);
 
     // Util function to check whether the specified dimension hits the guardrail.
-    bool hitGuardRailLocked(const HashableDimensionKey& newKey);
+    bool hitGuardRailLocked(const MetricDimensionKey& newKey);
 
     static const size_t kBucketSize = sizeof(GaugeBucket{});
 
diff --git a/bin/src/metrics/MetricProducer.cpp b/bin/src/metrics/MetricProducer.cpp
index e74924a..85e655b 100644
--- a/bin/src/metrics/MetricProducer.cpp
+++ b/bin/src/metrics/MetricProducer.cpp
@@ -15,6 +15,8 @@
  */
 #include "MetricProducer.h"
 
+#include "dimension.h"
+
 namespace android {
 namespace os {
 namespace statsd {
@@ -30,29 +32,51 @@
 
     bool condition;
     ConditionKey conditionKey;
+
+    std::unordered_set<HashableDimensionKey> dimensionKeysInCondition;
     if (mConditionSliced) {
         for (const auto& link : mConditionLinks) {
             getDimensionKeysForCondition(event, link, &conditionKey[link.condition()]);
         }
-        if (mWizard->query(mConditionTrackerIndex, conditionKey) != ConditionState::kTrue) {
-            condition = false;
-        } else {
-            condition = true;
-        }
+        auto conditionState =
+            mWizard->query(mConditionTrackerIndex, conditionKey, mDimensionsInCondition,
+                           &dimensionKeysInCondition);
+        condition = (conditionState == ConditionState::kTrue);
     } else {
         condition = mCondition;
     }
 
-    if (mDimensions.has_field() && mDimensions.child_size() > 0) {
-        vector<DimensionsValue> dimensionValues;
-        getDimensionKeys(event, mDimensions, &dimensionValues);
-        for (const DimensionsValue& dimensionValue : dimensionValues) {
+    vector<DimensionsValue> dimensionInWhatValues;
+    if (mDimensionsInWhat.has_field() && mDimensionsInWhat.child_size() > 0) {
+        getDimensionKeys(event, mDimensionsInWhat, &dimensionInWhatValues);
+    }
+
+    if (dimensionInWhatValues.empty() && dimensionKeysInCondition.empty()) {
+        onMatchedLogEventInternalLocked(
+            matcherIndex, DEFAULT_METRIC_DIMENSION_KEY, conditionKey, condition, event);
+    } else if (dimensionKeysInCondition.empty()) {
+        for (const DimensionsValue& whatValue : dimensionInWhatValues) {
             onMatchedLogEventInternalLocked(
-                matcherIndex, HashableDimensionKey(dimensionValue), conditionKey, condition, event);
+                matcherIndex,
+                MetricDimensionKey(HashableDimensionKey(whatValue), DEFAULT_DIMENSION_KEY),
+                conditionKey, condition, event);
+        }
+    } else if (dimensionInWhatValues.empty()) {
+        for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
+            onMatchedLogEventInternalLocked(
+                matcherIndex,
+                MetricDimensionKey(DEFAULT_DIMENSION_KEY, conditionDimensionKey),
+                conditionKey, condition, event);
         }
     } else {
-        onMatchedLogEventInternalLocked(
-            matcherIndex, DEFAULT_DIMENSION_KEY, conditionKey, condition, event);
+        for (const DimensionsValue& whatValue : dimensionInWhatValues) {
+            for (const auto& conditionDimensionKey : dimensionKeysInCondition) {
+                onMatchedLogEventInternalLocked(
+                    matcherIndex,
+                    MetricDimensionKey(HashableDimensionKey(whatValue), conditionDimensionKey),
+                    conditionKey, condition, event);
+            }
+        }
     }
 }
 
diff --git a/bin/src/metrics/MetricProducer.h b/bin/src/metrics/MetricProducer.h
index 6f33073..3b1498f 100644
--- a/bin/src/metrics/MetricProducer.h
+++ b/bin/src/metrics/MetricProducer.h
@@ -91,6 +91,7 @@
         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);
@@ -156,7 +157,8 @@
 
     int mConditionTrackerIndex;
 
-    FieldMatcher mDimensions;  // The dimension defined in statsd_config
+    FieldMatcher mDimensionsInWhat;  // The dimensions_in_what defined in statsd_config
+    FieldMatcher mDimensionsInCondition;  // The dimensions_in_condition defined in statsd_config
 
     std::vector<MetricConditionLink> mConditionLinks;
 
@@ -178,7 +180,7 @@
      * [event]: the log event, just in case the metric needs its data, e.g., EventMetric.
      */
     virtual void onMatchedLogEventInternalLocked(
-            const size_t matcherIndex, const HashableDimensionKey& eventKey,
+            const size_t matcherIndex, const MetricDimensionKey& eventKey,
             const ConditionKey& conditionKey, bool condition,
             const LogEvent& event) = 0;
 
diff --git a/bin/src/metrics/MetricsManager.h b/bin/src/metrics/MetricsManager.h
index 9cdbafc..d4b9102 100644
--- a/bin/src/metrics/MetricsManager.h
+++ b/bin/src/metrics/MetricsManager.h
@@ -143,6 +143,10 @@
     FRIEND_TEST(MetricConditionLinkE2eTest, TestMultiplePredicatesAndLinks);
     FRIEND_TEST(AttributionE2eTest, TestAttributionMatchAndSlice);
     FRIEND_TEST(GaugeMetricE2eTest, TestMultipleFieldsForPushedEvent);
+    FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricNoLink);
+    FRIEND_TEST(DimensionInConditionE2eTest, TestCountMetricWithLink);
+    FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink);
+    FRIEND_TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink);
 };
 
 }  // namespace statsd
diff --git a/bin/src/metrics/ValueMetricProducer.cpp b/bin/src/metrics/ValueMetricProducer.cpp
index ae0c673..c9cc7bb 100644
--- a/bin/src/metrics/ValueMetricProducer.cpp
+++ b/bin/src/metrics/ValueMetricProducer.cpp
@@ -81,13 +81,15 @@
     }
 
     mBucketSizeNs = bucketSizeMills * 1000000;
-    mDimensions = metric.dimensions_in_what();
+    mDimensionsInWhat = metric.dimensions_in_what();
+    mDimensionsInCondition = metric.dimensions_in_condition();
 
     if (metric.links().size() > 0) {
         mConditionLinks.insert(mConditionLinks.begin(), metric.links().begin(),
                                metric.links().end());
-        mConditionSliced = true;
     }
+    mConditionSliced = (metric.links().size() > 0)||
+        (mDimensionsInCondition.has_field() && mDimensionsInCondition.child_size() > 0);
 
     if (!metric.has_condition() && mPullTagId != -1) {
         VLOG("Setting up periodic pulling for %d", mPullTagId);
@@ -124,7 +126,10 @@
     auto value_metrics = report->mutable_value_metrics();
     for (const auto& pair : mPastBuckets) {
         ValueMetricData* metricData = value_metrics->add_data();
-        *metricData->mutable_dimensions_in_what() = pair.first.getDimensionsValue();
+        *metricData->mutable_dimensions_in_what() =
+            pair.first.getDimensionKeyInWhat().getDimensionsValue();
+        *metricData->mutable_dimensions_in_condition() =
+            pair.first.getDimensionKeyInCondition().getDimensionsValue();
         for (const auto& bucket : pair.second) {
             ValueBucketInfo* bucketInfo = metricData->add_bucket_info();
             bucketInfo->set_start_bucket_nanos(bucket.mBucketStartNs);
@@ -146,16 +151,24 @@
     long long protoToken = protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_ID_VALUE_METRICS);
 
     for (const auto& pair : mPastBuckets) {
-        const HashableDimensionKey& hashableKey = pair.first;
-        VLOG("  dimension key %s", hashableKey.c_str());
+        const MetricDimensionKey& dimensionKey = pair.first;
+        VLOG("  dimension key %s", dimensionKey.c_str());
         long long wrapperToken =
                 protoOutput->start(FIELD_TYPE_MESSAGE | FIELD_COUNT_REPEATED | FIELD_ID_DATA);
 
         // First fill dimension.
         long long dimensionToken = protoOutput->start(
             FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_WHAT);
-        writeDimensionsValueProtoToStream(hashableKey.getDimensionsValue(), protoOutput);
+        writeDimensionsValueProtoToStream(
+            dimensionKey.getDimensionKeyInWhat().getDimensionsValue(), protoOutput);
         protoOutput->end(dimensionToken);
+        if (dimensionKey.hasDimensionKeyInCondition()) {
+            long long dimensionInConditionToken = protoOutput->start(
+                    FIELD_TYPE_MESSAGE | FIELD_ID_DIMENSION_IN_CONDITION);
+            writeDimensionsValueProtoToStream(
+                dimensionKey.getDimensionKeyInCondition().getDimensionsValue(), protoOutput);
+            protoOutput->end(dimensionInConditionToken);
+        }
 
         // Then fill bucket_info (ValueBucketInfo).
         for (const auto& bucket : pair.second) {
@@ -239,7 +252,7 @@
     }
 }
 
-bool ValueMetricProducer::hitGuardRailLocked(const HashableDimensionKey& newKey) {
+bool ValueMetricProducer::hitGuardRailLocked(const MetricDimensionKey& newKey) {
     // ===========GuardRail==============
     // 1. Report the tuple count if the tuple count > soft limit
     if (mCurrentSlicedBucket.find(newKey) != mCurrentSlicedBucket.end()) {
@@ -260,7 +273,7 @@
 }
 
 void ValueMetricProducer::onMatchedLogEventInternalLocked(
-        const size_t matcherIndex, const HashableDimensionKey& eventKey,
+        const size_t matcherIndex, const MetricDimensionKey& eventKey,
         const ConditionKey& conditionKey, bool condition,
         const LogEvent& event) {
     uint64_t eventTimeNs = event.GetTimestampNs();
diff --git a/bin/src/metrics/ValueMetricProducer.h b/bin/src/metrics/ValueMetricProducer.h
index 9f750cf..121ec7d 100644
--- a/bin/src/metrics/ValueMetricProducer.h
+++ b/bin/src/metrics/ValueMetricProducer.h
@@ -49,7 +49,7 @@
 
 protected:
     void onMatchedLogEventInternalLocked(
-            const size_t matcherIndex, const HashableDimensionKey& eventKey,
+            const size_t matcherIndex, const MetricDimensionKey& eventKey,
             const ConditionKey& conditionKey, bool condition,
             const LogEvent& event) override;
 
@@ -99,16 +99,16 @@
         long sum;
     } Interval;
 
-    std::unordered_map<HashableDimensionKey, Interval> mCurrentSlicedBucket;
+    std::unordered_map<MetricDimensionKey, Interval> mCurrentSlicedBucket;
 
     // Save the past buckets and we can clear when the StatsLogReport is dumped.
     // TODO: Add a lock to mPastBuckets.
-    std::unordered_map<HashableDimensionKey, std::vector<ValueBucket>> mPastBuckets;
+    std::unordered_map<MetricDimensionKey, std::vector<ValueBucket>> mPastBuckets;
 
     std::shared_ptr<FieldValueMap> getValueFields(const LogEvent& event);
 
     // Util function to check whether the specified dimension hits the guardrail.
-    bool hitGuardRailLocked(const HashableDimensionKey& newKey);
+    bool hitGuardRailLocked(const MetricDimensionKey& newKey);
 
     static const size_t kBucketSize = sizeof(ValueBucket{});
 
diff --git a/bin/src/metrics/duration_helper/DurationTracker.h b/bin/src/metrics/duration_helper/DurationTracker.h
index c2d2cea..45735a8 100644
--- a/bin/src/metrics/duration_helper/DurationTracker.h
+++ b/bin/src/metrics/duration_helper/DurationTracker.h
@@ -60,8 +60,9 @@
 
 class DurationTracker {
 public:
-    DurationTracker(const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey,
-                    sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
+    DurationTracker(const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
+                    sp<ConditionWizard> wizard, int conditionIndex,
+                    const FieldMatcher& dimensionInCondition, bool nesting,
                     uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced,
                     const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
         : mConfigKey(key),
@@ -70,6 +71,7 @@
           mWizard(wizard),
           mConditionTrackerIndex(conditionIndex),
           mBucketSizeNs(bucketSizeNs),
+          mDimensionInCondition(dimensionInCondition),
           mNested(nesting),
           mCurrentBucketStartTimeNs(currentBucketStartNs),
           mDuration(0),
@@ -79,6 +81,8 @@
 
     virtual ~DurationTracker(){};
 
+    virtual unique_ptr<DurationTracker> clone(const uint64_t eventTime) = 0;
+
     virtual void noteStart(const HashableDimensionKey& key, bool condition,
                            const uint64_t eventTime, const ConditionKey& conditionKey) = 0;
     virtual void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
@@ -92,7 +96,7 @@
     // events, so that the owner can safely remove the tracker.
     virtual bool flushIfNeeded(
             uint64_t timestampNs,
-            std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) = 0;
+            std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) = 0;
 
     // Predict the anomaly timestamp given the current status.
     virtual int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
@@ -100,6 +104,10 @@
     // Dump internal states for debugging
     virtual void dumpStates(FILE* out, bool verbose) const = 0;
 
+    void setEventKey(const MetricDimensionKey& eventKey) {
+         mEventKey = eventKey;
+    }
+
 protected:
     // Starts the anomaly alarm.
     void startAnomalyAlarm(const uint64_t eventTime) {
@@ -150,7 +158,7 @@
 
     const int64_t mTrackerId;
 
-    HashableDimensionKey mEventKey;
+    MetricDimensionKey mEventKey;
 
     sp<ConditionWizard> mWizard;
 
@@ -158,6 +166,8 @@
 
     const int64_t mBucketSizeNs;
 
+    const FieldMatcher mDimensionInCondition;
+
     const bool mNested;
 
     uint64_t mCurrentBucketStartTimeNs;
diff --git a/bin/src/metrics/duration_helper/MaxDurationTracker.cpp b/bin/src/metrics/duration_helper/MaxDurationTracker.cpp
index 412a0c9..db7dea4 100644
--- a/bin/src/metrics/duration_helper/MaxDurationTracker.cpp
+++ b/bin/src/metrics/duration_helper/MaxDurationTracker.cpp
@@ -25,13 +25,23 @@
 namespace statsd {
 
 MaxDurationTracker::MaxDurationTracker(const ConfigKey& key, const int64_t& id,
-                                       const HashableDimensionKey& eventKey,
-                                       sp<ConditionWizard> wizard, int conditionIndex, bool nesting,
+                                       const MetricDimensionKey& eventKey,
+                                       sp<ConditionWizard> wizard, int conditionIndex,
+                                       const FieldMatcher& dimensionInCondition, bool nesting,
                                        uint64_t currentBucketStartNs, uint64_t bucketSizeNs,
                                        bool conditionSliced,
                                        const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
-    : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
-                      bucketSizeNs, conditionSliced, anomalyTrackers) {
+    : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting,
+                      currentBucketStartNs, bucketSizeNs, conditionSliced, anomalyTrackers) {
+}
+
+unique_ptr<DurationTracker> MaxDurationTracker::clone(const uint64_t eventTime) {
+    auto clonedTracker = make_unique<MaxDurationTracker>(*this);
+    for (auto it = clonedTracker->mInfos.begin(); it != clonedTracker->mInfos.end(); ++it) {
+        it->second.lastStartTime = eventTime;
+        it->second.lastDuration = 0;
+    }
+    return clonedTracker;
 }
 
 bool MaxDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
@@ -44,7 +54,7 @@
     if (mInfos.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
         size_t newTupleCount = mInfos.size() + 1;
         StatsdStats::getInstance().noteMetricDimensionSize(
-            mConfigKey, hashDimensionsValue(mTrackerId, mEventKey.getDimensionsValue()),
+            mConfigKey, hashMetricDimensionKey(mTrackerId, mEventKey),
             newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
@@ -149,7 +159,7 @@
 }
 
 bool MaxDurationTracker::flushIfNeeded(
-        uint64_t eventTime, unordered_map<HashableDimensionKey, vector<DurationBucket>>* output) {
+        uint64_t eventTime, unordered_map<MetricDimensionKey, vector<DurationBucket>>* output) {
     if (mCurrentBucketStartTimeNs + mBucketSizeNs > eventTime) {
         return false;
     }
@@ -236,8 +246,14 @@
         if (pair.second.state == kStopped) {
             continue;
         }
-        bool conditionMet = mWizard->query(mConditionTrackerIndex, pair.second.conditionKeys) ==
-                            ConditionState::kTrue;
+        std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
+        ConditionState conditionState = mWizard->query(
+            mConditionTrackerIndex, pair.second.conditionKeys, mDimensionInCondition,
+            &conditionDimensionKeySet);
+        bool conditionMet = (conditionState == ConditionState::kTrue) &&
+            (!mDimensionInCondition.has_field() ||
+             conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) !=
+                conditionDimensionKeySet.end());
         VLOG("key: %s, condition: %d", pair.first.c_str(), conditionMet);
         noteConditionChanged(pair.first, conditionMet, timestamp);
     }
diff --git a/bin/src/metrics/duration_helper/MaxDurationTracker.h b/bin/src/metrics/duration_helper/MaxDurationTracker.h
index 661d131..4d32a06 100644
--- a/bin/src/metrics/duration_helper/MaxDurationTracker.h
+++ b/bin/src/metrics/duration_helper/MaxDurationTracker.h
@@ -29,10 +29,15 @@
 class MaxDurationTracker : public DurationTracker {
 public:
     MaxDurationTracker(const ConfigKey& key, const int64_t& id,
-                       const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
-                       int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
-                       uint64_t bucketSizeNs, bool conditionSliced,
+                       const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard,
+                       int conditionIndex, const FieldMatcher& dimensionInCondition, bool nesting,
+                       uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced,
                        const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
+
+    MaxDurationTracker(const MaxDurationTracker& tracker) = default;
+
+    unique_ptr<DurationTracker> clone(const uint64_t eventTime) override;
+
     void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
                    const ConditionKey& conditionKey) override;
     void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
@@ -41,7 +46,7 @@
 
     bool flushIfNeeded(
             uint64_t timestampNs,
-            std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) override;
+            std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
 
     void onSlicedConditionMayChange(const uint64_t timestamp) override;
     void onConditionChanged(bool condition, const uint64_t timestamp) override;
diff --git a/bin/src/metrics/duration_helper/OringDurationTracker.cpp b/bin/src/metrics/duration_helper/OringDurationTracker.cpp
index 75d7c08..0feae36 100644
--- a/bin/src/metrics/duration_helper/OringDurationTracker.cpp
+++ b/bin/src/metrics/duration_helper/OringDurationTracker.cpp
@@ -25,17 +25,25 @@
 using std::pair;
 
 OringDurationTracker::OringDurationTracker(
-        const ConfigKey& key, const int64_t& id, const HashableDimensionKey& eventKey,
-        sp<ConditionWizard> wizard, int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
+        const ConfigKey& key, const int64_t& id, const MetricDimensionKey& eventKey,
+        sp<ConditionWizard> wizard, int conditionIndex,
+        const FieldMatcher& dimensionInCondition, bool nesting, uint64_t currentBucketStartNs,
         uint64_t bucketSizeNs, bool conditionSliced,
         const vector<sp<DurationAnomalyTracker>>& anomalyTrackers)
-    : DurationTracker(key, id, eventKey, wizard, conditionIndex, nesting, currentBucketStartNs,
-                      bucketSizeNs, conditionSliced, anomalyTrackers),
+    : DurationTracker(key, id, eventKey, wizard, conditionIndex, dimensionInCondition, nesting,
+                      currentBucketStartNs, bucketSizeNs, conditionSliced, anomalyTrackers),
       mStarted(),
       mPaused() {
     mLastStartTime = 0;
 }
 
+unique_ptr<DurationTracker> OringDurationTracker::clone(const uint64_t eventTime) {
+    auto clonedTracker = make_unique<OringDurationTracker>(*this);
+    clonedTracker->mLastStartTime = eventTime;
+    clonedTracker->mDuration = 0;
+    return clonedTracker;
+}
+
 bool OringDurationTracker::hitGuardRail(const HashableDimensionKey& newKey) {
     // ===========GuardRail==============
     // 1. Report the tuple count if the tuple count > soft limit
@@ -45,7 +53,7 @@
     if (mConditionKeyMap.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
         size_t newTupleCount = mConditionKeyMap.size() + 1;
         StatsdStats::getInstance().noteMetricDimensionSize(
-            mConfigKey, hashDimensionsValue(mTrackerId, mEventKey.getDimensionsValue()),
+            mConfigKey, hashMetricDimensionKey(mTrackerId, mEventKey),
             newTupleCount);
         // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
         if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
@@ -76,7 +84,6 @@
     if (mConditionSliced && mConditionKeyMap.find(key) == mConditionKeyMap.end()) {
         mConditionKeyMap[key] = conditionKey;
     }
-
     VLOG("Oring: %s start, condition %d", key.c_str(), condition);
 }
 
@@ -128,7 +135,7 @@
 }
 
 bool OringDurationTracker::flushIfNeeded(
-        uint64_t eventTime, unordered_map<HashableDimensionKey, vector<DurationBucket>>* output) {
+        uint64_t eventTime, unordered_map<MetricDimensionKey, vector<DurationBucket>>* output) {
     if (eventTime < mCurrentBucketStartTimeNs + mBucketSizeNs) {
         return false;
     }
@@ -184,8 +191,14 @@
                 ++it;
                 continue;
             }
-            if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) !=
-                ConditionState::kTrue) {
+            std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
+            ConditionState conditionState =
+                mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key],
+                               mDimensionInCondition, &conditionDimensionKeySet);
+            if (conditionState != ConditionState::kTrue ||
+                (mDimensionInCondition.has_field() &&
+                 conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition()) ==
+                    conditionDimensionKeySet.end())) {
                 startedToPaused.push_back(*it);
                 it = mStarted.erase(it);
                 VLOG("Key %s started -> paused", key.c_str());
@@ -210,8 +223,14 @@
                 ++it;
                 continue;
             }
-            if (mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key]) ==
-                ConditionState::kTrue) {
+            std::unordered_set<HashableDimensionKey> conditionDimensionKeySet;
+            ConditionState conditionState =
+                mWizard->query(mConditionTrackerIndex, mConditionKeyMap[key],
+                               mDimensionInCondition, &conditionDimensionKeySet);
+            if (conditionState == ConditionState::kTrue &&
+                (!mDimensionInCondition.has_field() ||
+                 conditionDimensionKeySet.find(mEventKey.getDimensionKeyInCondition())
+                    != conditionDimensionKeySet.end())) {
                 pausedToStarted.push_back(*it);
                 it = mPaused.erase(it);
                 VLOG("Key %s paused -> started", key.c_str());
diff --git a/bin/src/metrics/duration_helper/OringDurationTracker.h b/bin/src/metrics/duration_helper/OringDurationTracker.h
index 43469ca..75b5a81 100644
--- a/bin/src/metrics/duration_helper/OringDurationTracker.h
+++ b/bin/src/metrics/duration_helper/OringDurationTracker.h
@@ -28,11 +28,15 @@
 class OringDurationTracker : public DurationTracker {
 public:
     OringDurationTracker(const ConfigKey& key, const int64_t& id,
-                         const HashableDimensionKey& eventKey, sp<ConditionWizard> wizard,
-                         int conditionIndex, bool nesting, uint64_t currentBucketStartNs,
-                         uint64_t bucketSizeNs, bool conditionSliced,
+                         const MetricDimensionKey& eventKey, sp<ConditionWizard> wizard,
+                         int conditionIndex, const FieldMatcher& dimensionInCondition, bool nesting,
+                         uint64_t currentBucketStartNs, uint64_t bucketSizeNs, bool conditionSliced,
                          const std::vector<sp<DurationAnomalyTracker>>& anomalyTrackers);
 
+    OringDurationTracker(const OringDurationTracker& tracker) = default;
+
+    unique_ptr<DurationTracker> clone(const uint64_t eventTime) override;
+
     void noteStart(const HashableDimensionKey& key, bool condition, const uint64_t eventTime,
                    const ConditionKey& conditionKey) override;
     void noteStop(const HashableDimensionKey& key, const uint64_t eventTime,
@@ -44,7 +48,7 @@
 
     bool flushIfNeeded(
             uint64_t timestampNs,
-            std::unordered_map<HashableDimensionKey, std::vector<DurationBucket>>* output) override;
+            std::unordered_map<MetricDimensionKey, std::vector<DurationBucket>>* output) override;
 
     int64_t predictAnomalyTimestampNs(const DurationAnomalyTracker& anomalyTracker,
                                       const uint64_t currentTimestamp) const override;
diff --git a/bin/src/stats_log_util.cpp b/bin/src/stats_log_util.cpp
index a41f30c..6c61400 100644
--- a/bin/src/stats_log_util.cpp
+++ b/bin/src/stats_log_util.cpp
@@ -54,6 +54,9 @@
 
 void writeDimensionsValueProtoToStream(const DimensionsValue& dimensionsValue,
                                        ProtoOutputStream* protoOutput) {
+    if (!dimensionsValue.has_field()) {
+        return;
+    }
     protoOutput->write(FIELD_TYPE_INT32 | DIMENSIONS_VALUE_FIELD, dimensionsValue.field());
     switch (dimensionsValue.value_case()) {
         case DimensionsValue::ValueCase::kValueStr:
@@ -103,6 +106,9 @@
 
 void writeFieldProtoToStream(
     const Field& field, util::ProtoOutputStream* protoOutput) {
+    if (!field.has_field()) {
+        return;
+    }
     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());
diff --git a/bin/src/stats_util.h b/bin/src/stats_util.h
index 160b1f4..31f51a7 100644
--- a/bin/src/stats_util.h
+++ b/bin/src/stats_util.h
@@ -28,13 +28,14 @@
 namespace statsd {
 
 const HashableDimensionKey DEFAULT_DIMENSION_KEY = HashableDimensionKey();
+const MetricDimensionKey DEFAULT_METRIC_DIMENSION_KEY = MetricDimensionKey();
 
 // Minimum bucket size in seconds
 const long kMinBucketSizeSec = 5 * 60;
 
 typedef std::map<int64_t, std::vector<HashableDimensionKey>> ConditionKey;
 
-typedef std::unordered_map<HashableDimensionKey, int64_t> DimToValMap;
+typedef std::unordered_map<MetricDimensionKey, int64_t> DimToValMap;
 
 }  // namespace statsd
 }  // namespace os
diff --git a/bin/src/subscriber/SubscriberReporter.cpp b/bin/src/subscriber/SubscriberReporter.cpp
index f912e4b..3af684f 100644
--- a/bin/src/subscriber/SubscriberReporter.cpp
+++ b/bin/src/subscriber/SubscriberReporter.cpp
@@ -56,7 +56,7 @@
 
 void SubscriberReporter::alertBroadcastSubscriber(const ConfigKey& configKey,
                                                   const Subscription& subscription,
-                                                  const HashableDimensionKey& dimKey) const {
+                                                  const MetricDimensionKey& dimKey) const {
     // Reminder about ids:
     //  subscription id - name of the Subscription (that ties the Alert to the broadcast)
     //  subscription rule_id - the name of the Alert (that triggers the broadcast)
@@ -92,7 +92,7 @@
 void SubscriberReporter::sendBroadcastLocked(const sp<IBinder>& intentSender,
                                              const ConfigKey& configKey,
                                              const Subscription& subscription,
-                                             const HashableDimensionKey& dimKey) const {
+                                             const MetricDimensionKey& dimKey) const {
     VLOG("SubscriberReporter::sendBroadcastLocked called.");
     if (mStatsCompanionService == nullptr) {
         ALOGW("Failed to send subscriber broadcast: could not access StatsCompanionService.");
@@ -107,8 +107,8 @@
 }
 
 StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue(
-        const HashableDimensionKey& dimKey) {
-    return protoToStatsDimensionsValue(dimKey.getDimensionsValue());
+        const MetricDimensionKey& dimKey) {
+    return protoToStatsDimensionsValue(dimKey.getDimensionKeyInWhat().getDimensionsValue());
 }
 
 StatsDimensionsValue SubscriberReporter::protoToStatsDimensionsValue(
diff --git a/bin/src/subscriber/SubscriberReporter.h b/bin/src/subscriber/SubscriberReporter.h
index 5bb458a..13fc7fd 100644
--- a/bin/src/subscriber/SubscriberReporter.h
+++ b/bin/src/subscriber/SubscriberReporter.h
@@ -80,7 +80,7 @@
      */
     void alertBroadcastSubscriber(const ConfigKey& configKey,
                                   const Subscription& subscription,
-                                  const HashableDimensionKey& dimKey) const;
+                                  const MetricDimensionKey& dimKey) const;
 
 private:
     SubscriberReporter() {};
@@ -101,7 +101,7 @@
     void sendBroadcastLocked(const sp<android::IBinder>& intentSender,
                              const ConfigKey& configKey,
                              const Subscription& subscription,
-                             const HashableDimensionKey& dimKey) const;
+                             const MetricDimensionKey& dimKey) const;
 
     /** Converts a stats_log.proto DimensionsValue to a StatsDimensionsValue. */
     static StatsDimensionsValue protoToStatsDimensionsValue(
@@ -109,7 +109,7 @@
 
     /** Converts a HashableDimensionKey to a StatsDimensionsValue. */
     static StatsDimensionsValue protoToStatsDimensionsValue(
-            const HashableDimensionKey& dimKey);
+            const MetricDimensionKey& dimKey);
 };
 
 }  // namespace statsd
diff --git a/bin/tests/anomaly/AnomalyTracker_test.cpp b/bin/tests/anomaly/AnomalyTracker_test.cpp
index 66bfa68..a415ea1 100644
--- a/bin/tests/anomaly/AnomalyTracker_test.cpp
+++ b/bin/tests/anomaly/AnomalyTracker_test.cpp
@@ -33,14 +33,14 @@
 
 const ConfigKey kConfigKey(0, 12345);
 
-HashableDimensionKey getMockDimensionKey(int key, string value) {
+MetricDimensionKey getMockMetricDimensionKey(int key, string value) {
     DimensionsValue dimensionsValue;
     dimensionsValue.set_field(key);
     dimensionsValue.set_value_str(value);
-    return HashableDimensionKey(dimensionsValue);
+    return MetricDimensionKey(HashableDimensionKey(dimensionsValue), DEFAULT_DIMENSION_KEY);
 }
 
-void AddValueToBucket(const std::vector<std::pair<HashableDimensionKey, long>>& key_value_pair_list,
+void AddValueToBucket(const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list,
                       std::shared_ptr<DimToValMap> bucket) {
     for (auto itr = key_value_pair_list.begin(); itr != key_value_pair_list.end(); itr++) {
         (*bucket)[itr->first] += itr->second;
@@ -48,7 +48,7 @@
 }
 
 std::shared_ptr<DimToValMap> MockBucket(
-        const std::vector<std::pair<HashableDimensionKey, long>>& key_value_pair_list) {
+        const std::vector<std::pair<MetricDimensionKey, long>>& key_value_pair_list) {
     std::shared_ptr<DimToValMap> bucket = std::make_shared<DimToValMap>();
     AddValueToBucket(key_value_pair_list, bucket);
     return bucket;
@@ -56,7 +56,7 @@
 
 // Returns the value, for the given key, in that bucket, or 0 if not present.
 int64_t getBucketValue(const std::shared_ptr<DimToValMap>& bucket,
-                       const HashableDimensionKey& key) {
+                       const MetricDimensionKey& key) {
     const auto& itr = bucket->find(key);
     if (itr != bucket->end()) {
         return itr->second;
@@ -68,14 +68,14 @@
 bool detectAnomaliesPass(AnomalyTracker& tracker,
                          const int64_t& bucketNum,
                          const std::shared_ptr<DimToValMap>& currentBucket,
-                         const std::set<const HashableDimensionKey>& trueList,
-                         const std::set<const HashableDimensionKey>& falseList) {
-    for (HashableDimensionKey key : trueList) {
+                         const std::set<const MetricDimensionKey>& trueList,
+                         const std::set<const MetricDimensionKey>& falseList) {
+    for (MetricDimensionKey key : trueList) {
         if (!tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
             return false;
         }
     }
-    for (HashableDimensionKey key : falseList) {
+    for (MetricDimensionKey key : falseList) {
         if (tracker.detectAnomaly(bucketNum, key, getBucketValue(currentBucket, key))) {
             return false;
         }
@@ -100,7 +100,7 @@
 void checkRefractoryTimes(AnomalyTracker& tracker,
                           const int64_t& currTimestampNs,
                           const int32_t& refractoryPeriodSec,
-                          const std::unordered_map<HashableDimensionKey, int64_t>& timestamps) {
+                          const std::unordered_map<MetricDimensionKey, int64_t>& timestamps) {
     for (const auto& kv : timestamps) {
         if (kv.second < 0) {
             // Make sure that, if there is a refractory period, it is already past.
@@ -124,9 +124,9 @@
     alert.set_trigger_if_sum_gt(2);
 
     AnomalyTracker anomalyTracker(alert, kConfigKey);
-    HashableDimensionKey keyA = getMockDimensionKey(1, "a");
-    HashableDimensionKey keyB = getMockDimensionKey(1, "b");
-    HashableDimensionKey keyC = getMockDimensionKey(1, "c");
+    MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a");
+    MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b");
+    MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c");
 
     int64_t eventTimestamp0 = 10 * NS_PER_SEC;
     int64_t eventTimestamp1 = bucketSizeNs + 11 * NS_PER_SEC;
@@ -269,11 +269,11 @@
     alert.set_trigger_if_sum_gt(2);
 
     AnomalyTracker anomalyTracker(alert, kConfigKey);
-    HashableDimensionKey keyA = getMockDimensionKey(1, "a");
-    HashableDimensionKey keyB = getMockDimensionKey(1, "b");
-    HashableDimensionKey keyC = getMockDimensionKey(1, "c");
-    HashableDimensionKey keyD = getMockDimensionKey(1, "d");
-    HashableDimensionKey keyE = getMockDimensionKey(1, "e");
+    MetricDimensionKey keyA = getMockMetricDimensionKey(1, "a");
+    MetricDimensionKey keyB = getMockMetricDimensionKey(1, "b");
+    MetricDimensionKey keyC = getMockMetricDimensionKey(1, "c");
+    MetricDimensionKey keyD = getMockMetricDimensionKey(1, "d");
+    MetricDimensionKey keyE = getMockMetricDimensionKey(1, "e");
 
     std::shared_ptr<DimToValMap> bucket9 = MockBucket({{keyA, 1}, {keyB, 2}, {keyC, 1}});
     std::shared_ptr<DimToValMap> bucket16 = MockBucket({{keyB, 4}});
diff --git a/bin/tests/condition/SimpleConditionTracker_test.cpp b/bin/tests/condition/SimpleConditionTracker_test.cpp
index 819f2be..d1b7b28 100644
--- a/bin/tests/condition/SimpleConditionTracker_test.cpp
+++ b/bin/tests/condition/SimpleConditionTracker_test.cpp
@@ -78,7 +78,7 @@
 std::map<int64_t, std::vector<HashableDimensionKey>> getWakeLockQueryKey(
     const Position position,
     const std::vector<int> &uids, const string& conditionName) {
-    std::map<int64_t, std::vector<HashableDimensionKey>>  outputKeyMap;
+    std::map<int64_t, std::vector<HashableDimensionKey>> outputKeyMap;
     std::vector<int> uid_indexes;
     switch(position) {
         case Position::FIRST:
@@ -265,6 +265,9 @@
 TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
     for (Position position :
             { Position::ANY, Position::FIRST, Position::LAST}) {
+        FieldMatcher dimensionInCondition;
+        std::unordered_set<HashableDimensionKey> dimensionKeys;
+
         SimplePredicate simplePredicate = getWakeLockHeldCondition(
                 true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
                 position);
@@ -307,7 +310,8 @@
         const auto queryKey = getWakeLockQueryKey(position, uids, conditionName);
         conditionCache[0] = ConditionState::kNotEvaluated;
 
-        conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+        conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+                                        conditionCache, dimensionKeys);
         EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
 
         // another wake lock acquired by this uid
@@ -361,7 +365,8 @@
 
         // query again
         conditionCache[0] = ConditionState::kNotEvaluated;
-        conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+        conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+                                        conditionCache, dimensionKeys);
         EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
 
     }
@@ -369,6 +374,9 @@
 }
 
 TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
+    FieldMatcher dimensionInCondition;
+    std::unordered_set<HashableDimensionKey> dimensionKeys;
+
     SimplePredicate simplePredicate = getWakeLockHeldCondition(
             true /*nesting*/, true /*default to false*/, false /*slice output by uid*/,
             Position::ANY /* position */);
@@ -410,7 +418,8 @@
     ConditionKey queryKey;
     conditionCache[0] = ConditionState::kNotEvaluated;
 
-    conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+    conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+                                    conditionCache, dimensionKeys);
     EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
 
     // another wake lock acquired by this uid
@@ -452,13 +461,17 @@
 
     // query again
     conditionCache[0] = ConditionState::kNotEvaluated;
-    conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+    dimensionKeys.clear();
+    conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+                                    conditionCache, dimensionKeys);
     EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
 }
 
 TEST(SimpleConditionTrackerTest, TestStopAll) {
     for (Position position :
             {Position::ANY, Position::FIRST, Position::LAST}) {
+        FieldMatcher dimensionInCondition;
+        std::unordered_set<HashableDimensionKey> dimensionKeys;
         SimplePredicate simplePredicate = getWakeLockHeldCondition(
                 true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
                 position);
@@ -502,7 +515,8 @@
         const auto queryKey = getWakeLockQueryKey(position, uid_list1, conditionName);
         conditionCache[0] = ConditionState::kNotEvaluated;
 
-        conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+        conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+                                        conditionCache, dimensionKeys);
         EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
 
         // another wake lock acquired by uid2
@@ -528,8 +542,9 @@
         // TEST QUERY
         const auto queryKey2 = getWakeLockQueryKey(position, uid_list2, conditionName);
         conditionCache[0] = ConditionState::kNotEvaluated;
+        conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+                                        conditionCache, dimensionKeys);
 
-        conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
         EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
 
 
@@ -550,15 +565,15 @@
         // TEST QUERY
         const auto queryKey3 = getWakeLockQueryKey(position, uid_list1, conditionName);
         conditionCache[0] = ConditionState::kNotEvaluated;
-
-        conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+        conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+                                        conditionCache, dimensionKeys);
         EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
 
         // TEST QUERY
         const auto queryKey4 = getWakeLockQueryKey(position, uid_list2, conditionName);
         conditionCache[0] = ConditionState::kNotEvaluated;
-
-        conditionTracker.isConditionMet(queryKey, allPredicates, conditionCache);
+        conditionTracker.isConditionMet(queryKey, allPredicates, dimensionInCondition,
+                                        conditionCache, dimensionKeys);
         EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
     }
 
diff --git a/bin/tests/dimension_test.cpp b/bin/tests/dimension_test.cpp
new file mode 100644
index 0000000..678abae
--- /dev/null
+++ b/bin/tests/dimension_test.cpp
@@ -0,0 +1,149 @@
+// 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 "dimension.h"
+
+#include <gtest/gtest.h>
+
+using namespace android::os::statsd;
+
+#ifdef __ANDROID__
+
+TEST(DimensionTest, subLeafNodes) {
+    DimensionsValue dimension;
+    int tagId = 100;
+    dimension.set_field(tagId);
+    auto child = dimension.mutable_value_tuple()->add_dimensions_value();
+    child->set_field(1);
+    child->set_value_int(2000);
+
+    child = dimension.mutable_value_tuple()->add_dimensions_value();
+    child->set_field(3);
+    child->set_value_str("test");
+
+    child = dimension.mutable_value_tuple()->add_dimensions_value();
+    child->set_field(4);
+    auto grandChild = child->mutable_value_tuple()->add_dimensions_value();
+    grandChild->set_field(1);
+    grandChild->set_value_float(1.3f);
+    grandChild = child->mutable_value_tuple()->add_dimensions_value();
+    grandChild->set_field(3);
+    grandChild->set_value_str("tag");
+
+    child = dimension.mutable_value_tuple()->add_dimensions_value();
+    child->set_field(6);
+    child->set_value_bool(false);
+
+    DimensionsValue sub_dimension;
+    FieldMatcher matcher;
+
+    // Tag id not matched.
+    matcher.set_field(tagId + 1);
+    EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    // Field not exist.
+    matcher.Clear();
+    matcher.set_field(tagId);
+    matcher.add_child()->set_field(5);
+    EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    // Field exists.
+    matcher.Clear();
+    matcher.set_field(tagId);
+    matcher.add_child()->set_field(3);
+    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    // Field exists.
+    matcher.Clear();
+    sub_dimension.Clear();
+    matcher.set_field(tagId);
+    matcher.add_child()->set_field(6);
+    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    // Field exists.
+    matcher.Clear();
+    sub_dimension.Clear();
+    matcher.set_field(tagId);
+    matcher.add_child()->set_field(1);
+    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    // Not leaf field.
+    matcher.Clear();
+    sub_dimension.Clear();
+    matcher.set_field(tagId);
+    matcher.add_child()->set_field(4);
+    EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    // Grand-child leaf field not exist.
+    matcher.Clear();
+    sub_dimension.Clear();
+    matcher.set_field(tagId);
+    auto childMatcher = matcher.add_child();
+    childMatcher->set_field(4);
+    childMatcher->add_child()->set_field(2);
+    EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    // Grand-child leaf field.
+    matcher.Clear();
+    sub_dimension.Clear();
+    matcher.set_field(tagId);
+    childMatcher = matcher.add_child();
+    childMatcher->set_field(4);
+    childMatcher->add_child()->set_field(1);
+    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    matcher.Clear();
+    sub_dimension.Clear();
+    matcher.set_field(tagId);
+    childMatcher = matcher.add_child();
+    childMatcher->set_field(4);
+    childMatcher->add_child()->set_field(3);
+    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    // Multiple grand-child fields.
+    matcher.Clear();
+    sub_dimension.Clear();
+    matcher.set_field(tagId);
+    childMatcher = matcher.add_child();
+    childMatcher->set_field(4);
+    childMatcher->add_child()->set_field(3);
+    childMatcher->add_child()->set_field(1);
+    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    // Multiple fields.
+    matcher.Clear();
+    sub_dimension.Clear();
+    matcher.set_field(tagId);
+    childMatcher = matcher.add_child();
+    childMatcher->set_field(4);
+    childMatcher->add_child()->set_field(3);
+    childMatcher->add_child()->set_field(1);
+    matcher.add_child()->set_field(3);
+    EXPECT_TRUE(getSubDimension(dimension, matcher, &sub_dimension));
+
+    // Subset of the fields not exist.
+    matcher.Clear();
+    sub_dimension.Clear();
+    matcher.set_field(tagId);
+    childMatcher = matcher.add_child();
+    childMatcher->set_field(4);
+    childMatcher->add_child()->set_field(3);
+    childMatcher->add_child()->set_field(1);
+    matcher.add_child()->set_field(2);
+    EXPECT_FALSE(getSubDimension(dimension, matcher, &sub_dimension));
+}
+
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif
diff --git a/bin/tests/e2e/DimensionInCondition_e2e_test.cpp b/bin/tests/e2e/DimensionInCondition_e2e_test.cpp
new file mode 100644
index 0000000..b5d48ef
--- /dev/null
+++ b/bin/tests/e2e/DimensionInCondition_e2e_test.cpp
@@ -0,0 +1,725 @@
+// 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 "src/stats_log_util.h"
+#include "tests/statsd_test_util.h"
+
+#include <vector>
+
+namespace android {
+namespace os {
+namespace statsd {
+
+#ifdef __ANDROID__
+
+namespace {
+
+StatsdConfig CreateCountMetricWithNoLinkConfig() {
+    StatsdConfig config;
+    auto screenBrightnessChangeAtomMatcher = CreateScreenBrightnessChangedAtomMatcher();
+    *config.add_atom_matcher() = screenBrightnessChangeAtomMatcher;
+    *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});
+    *config.add_predicate() = holdingWakelockPredicate;
+
+    auto combinationPredicate = config.add_predicate();
+    combinationPredicate->set_id(987654);
+    combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
+    addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+    addPredicateToPredicateCombination(holdingWakelockPredicate, combinationPredicate);
+
+    auto metric = config.add_count_metric();
+    metric->set_id(StringToId("ScreenBrightnessChangeMetric"));
+    metric->set_what(screenBrightnessChangeAtomMatcher.id());
+    metric->set_condition(combinationPredicate->id());
+    *metric->mutable_dimensions_in_what() = CreateDimensions(
+            android::util::SCREEN_BRIGHTNESS_CHANGED, {1 /* level */});
+    *metric->mutable_dimensions_in_condition() = CreateAttributionUidDimensions(
+            android::util::WAKELOCK_STATE_CHANGED, {Position::FIRST});
+    metric->set_bucket(ONE_MINUTE);
+    return config;
+}
+
+}  // namespace
+
+TEST(DimensionInConditionE2eTest, TestCountMetricNoLink) {
+    ConfigKey cfgKey;
+    auto config = CreateCountMetricWithNoLinkConfig();
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs =
+        TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+    std::vector<AttributionNode> attributions1 =
+        {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+         CreateAttribution(222, "GMSCoreModule2")};
+
+    std::vector<AttributionNode> attributions2 =
+        {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+         CreateAttribution(555, "GMSCoreModule2")};
+
+    std::vector<std::unique_ptr<LogEvent>> events;
+    events.push_back(CreateScreenStateChangedEvent(
+        android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+    events.push_back(CreateScreenStateChangedEvent(
+        android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
+    events.push_back(CreateScreenStateChangedEvent(
+        android::view::DISPLAY_STATE_ON, bucketStartTimeNs + bucketSizeNs + 1));
+    events.push_back(CreateScreenStateChangedEvent(
+        android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 2 * bucketSizeNs - 10));
+
+    events.push_back(CreateAcquireWakelockEvent(
+        attributions1, "wl1", bucketStartTimeNs + 200));
+    events.push_back(CreateReleaseWakelockEvent(
+        attributions1, "wl1", bucketStartTimeNs + bucketSizeNs + 1));
+
+    events.push_back(CreateAcquireWakelockEvent(
+        attributions2, "wl2", bucketStartTimeNs + bucketSizeNs - 100));
+    events.push_back(CreateReleaseWakelockEvent(
+        attributions2, "wl2", bucketStartTimeNs + 2 * bucketSizeNs - 50));
+
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        123, bucketStartTimeNs + 11));
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        123, bucketStartTimeNs + 101));
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        123, bucketStartTimeNs + 201));
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        456, bucketStartTimeNs + 203));
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        456, bucketStartTimeNs + bucketSizeNs - 99));
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        456, bucketStartTimeNs + bucketSizeNs - 2));
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        789, bucketStartTimeNs + bucketSizeNs - 1));
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        456, bucketStartTimeNs + bucketSizeNs + 2));
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        789, bucketStartTimeNs + 2 * bucketSizeNs - 11));
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        789, bucketStartTimeNs + 2 * bucketSizeNs - 9));
+    events.push_back(CreateScreenBrightnessChangedEvent(
+        789, bucketStartTimeNs + 2 * bucketSizeNs - 1));
+
+    sortLogEventsByTimestamp(&events);
+
+    for (const auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+
+    ConfigMetricsReportList reports;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+
+    EXPECT_EQ(reports.reports_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+    StatsLogReport::CountMetricDataWrapper countMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+
+    EXPECT_EQ(countMetrics.data_size(), 7);
+    auto data = countMetrics.data(0);
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs );
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 123);
+    EXPECT_FALSE(data.dimensions_in_condition().has_field());
+
+    data = countMetrics.data(1);
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 123);
+    ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111);
+
+    data = countMetrics.data(2);
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 3);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456);
+    ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111);
+
+    data = countMetrics.data(3);
+    EXPECT_EQ(data.bucket_info_size(), 2);
+    EXPECT_EQ(data.bucket_info(0).count(), 2);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.bucket_info(1).count(), 1);
+    EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 456);
+    ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 333);
+
+    data = countMetrics.data(4);
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 2);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
+    EXPECT_FALSE(data.dimensions_in_condition().has_field());
+
+    data = countMetrics.data(5);
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
+    ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 111);
+
+    data = countMetrics.data(6);
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::SCREEN_BRIGHTNESS_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 789);
+    ValidateAttributionUidDimension(data.dimensions_in_condition(), android::util::WAKELOCK_STATE_CHANGED, 333);
+}
+
+namespace {
+
+StatsdConfig CreateCountMetricWithLinkConfig() {
+    StatsdConfig config;
+    auto appCrashMatcher = CreateProcessCrashAtomMatcher();
+    *config.add_atom_matcher() = appCrashMatcher;
+    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
+    *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
+
+    auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+    auto isSyncingPredicate = CreateIsSyncingPredicate();
+    auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
+    *syncDimension = CreateAttributionUidAndTagDimensions(
+        android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    syncDimension->add_child()->set_field(2 /* name field*/);
+
+    *config.add_predicate() = screenIsOffPredicate;
+    *config.add_predicate() = isSyncingPredicate;
+    auto combinationPredicate = config.add_predicate();
+    combinationPredicate->set_id(987654);
+    combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
+    addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+    addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
+
+    auto metric = config.add_count_metric();
+    metric->set_bucket(ONE_MINUTE);
+    metric->set_id(StringToId("AppCrashMetric"));
+    metric->set_what(appCrashMatcher.id());
+    metric->set_condition(combinationPredicate->id());
+    *metric->mutable_dimensions_in_what() = CreateDimensions(
+            android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED, {1 /* uid */});
+    *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
+            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+
+    // Links between crash atom and condition of app is in syncing.
+    auto links = metric->add_links();
+    links->set_condition(isSyncingPredicate.id());
+    auto dimensionWhat = links->mutable_fields_in_what();
+    dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    dimensionWhat->add_child()->set_field(1);  // uid field.
+    *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
+            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    return config;
+}
+
+}  // namespace
+
+TEST(DimensionInConditionE2eTest, TestCountMetricWithLink) {
+    ConfigKey cfgKey;
+    auto config = CreateCountMetricWithLinkConfig();
+    int64_t bucketStartTimeNs = 10000000000;
+    int64_t bucketSizeNs =
+        TimeUnitToBucketSizeInMillis(config.count_metric(0).bucket()) * 1000000LL;
+
+    auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+    EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+    EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+    std::vector<AttributionNode> attributions1 =
+        {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+         CreateAttribution(222, "GMSCoreModule2")};
+
+    std::vector<AttributionNode> attributions2 =
+        {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+         CreateAttribution(555, "GMSCoreModule2")};
+
+    std::vector<std::unique_ptr<LogEvent>> events;
+
+    events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 11));
+    events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 101));
+    events.push_back(CreateAppCrashEvent(222, bucketStartTimeNs + 101));
+
+    events.push_back(CreateAppCrashEvent(222, bucketStartTimeNs + 201));
+    events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 211));
+    events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + 211));
+
+    events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + 401));
+    events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + 401));
+    events.push_back(CreateAppCrashEvent(555, bucketStartTimeNs + 401));
+
+    events.push_back(CreateAppCrashEvent(111, bucketStartTimeNs + bucketSizeNs + 301));
+    events.push_back(CreateAppCrashEvent(333, bucketStartTimeNs + bucketSizeNs + 301));
+
+    events.push_back(CreateAppCrashEvent(777, bucketStartTimeNs + bucketSizeNs + 701));
+
+    events.push_back(CreateScreenStateChangedEvent(
+        android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+    events.push_back(CreateScreenStateChangedEvent(
+        android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
+    events.push_back(CreateScreenStateChangedEvent(
+        android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202));
+    events.push_back(CreateScreenStateChangedEvent(
+        android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 700));
+
+    events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
+    events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
+        bucketStartTimeNs + bucketSizeNs + 300));
+
+    events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
+    events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
+        bucketStartTimeNs + bucketSizeNs - 1));
+
+    events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 400));
+    events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
+        bucketStartTimeNs + bucketSizeNs + 600));
+
+    sortLogEventsByTimestamp(&events);
+
+    for (const auto& event : events) {
+        processor->OnLogEvent(event.get());
+    }
+
+    ConfigMetricsReportList reports;
+    processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+
+    EXPECT_EQ(reports.reports_size(), 1);
+    EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+    StatsLogReport::CountMetricDataWrapper countMetrics;
+    sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).count_metrics(), &countMetrics);
+
+    EXPECT_EQ(countMetrics.data_size(), 5);
+    auto data = countMetrics.data(0);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
+    EXPECT_FALSE(data.dimensions_in_condition().has_field());
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+    data = countMetrics.data(1);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
+    ValidateAttributionUidAndTagDimension(
+        data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1");
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 2);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+    data = countMetrics.data(2);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 222);
+    EXPECT_FALSE(data.dimensions_in_condition().has_field());
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 2);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+    data = countMetrics.data(3);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333);
+    ValidateAttributionUidAndTagDimension(
+        data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2");
+    EXPECT_EQ(data.bucket_info_size(), 2);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.bucket_info(1).count(), 1);
+    EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+    data = countMetrics.data(4);
+    EXPECT_EQ(data.dimensions_in_what().field(), android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value_size(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+    EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 777);
+    EXPECT_FALSE(data.dimensions_in_condition().has_field());
+    EXPECT_EQ(data.bucket_info_size(), 1);
+    EXPECT_EQ(data.bucket_info(0).count(), 1);
+    EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+    EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+}
+
+namespace {
+
+StatsdConfig CreateDurationMetricConfigNoLink(DurationMetric::AggregationType aggregationType) {
+    StatsdConfig config;
+    *config.add_atom_matcher() = CreateBatterySaverModeStartAtomMatcher();
+    *config.add_atom_matcher() = CreateBatterySaverModeStopAtomMatcher();
+    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
+    *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
+
+    auto inBatterySaverModePredicate = CreateBatterySaverModePredicate();
+
+    auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+    auto isSyncingPredicate = CreateIsSyncingPredicate();
+    auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
+    *syncDimension = CreateAttributionUidAndTagDimensions(
+        android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    syncDimension->add_child()->set_field(2 /* name field */);
+
+    *config.add_predicate() = inBatterySaverModePredicate;
+    *config.add_predicate() = screenIsOffPredicate;
+    *config.add_predicate() = isSyncingPredicate;
+    auto combinationPredicate = config.add_predicate();
+    combinationPredicate->set_id(987654);
+    combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
+    addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+    addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
+
+    auto metric = config.add_duration_metric();
+    metric->set_bucket(ONE_MINUTE);
+    metric->set_id(StringToId("BatterySaverModeDurationMetric"));
+    metric->set_what(inBatterySaverModePredicate.id());
+    metric->set_condition(combinationPredicate->id());
+    *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
+            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    return config;
+}
+
+}  // namespace
+
+
+TEST(DimensionInConditionE2eTest, TestDurationMetricNoLink) {
+    for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
+        ConfigKey cfgKey;
+        auto config = CreateDurationMetricConfigNoLink(aggregationType);
+        int64_t bucketStartTimeNs = 10000000000;
+        int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+
+        auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+        EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+        EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+        std::vector<AttributionNode> attributions1 =
+            {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+             CreateAttribution(222, "GMSCoreModule2")};
+
+        std::vector<AttributionNode> attributions2 =
+            {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+             CreateAttribution(555, "GMSCoreModule2")};
+
+        std::vector<std::unique_ptr<LogEvent>> events;
+
+        events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 1));
+        events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 101));
+        events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 110));
+
+        events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 201));
+        events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + 500));
+
+        events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + 600));
+        events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + bucketSizeNs + 850));
+
+        events.push_back(CreateBatterySaverOnEvent(bucketStartTimeNs + bucketSizeNs + 870));
+        events.push_back(CreateBatterySaverOffEvent(bucketStartTimeNs + bucketSizeNs + 900));
+
+        events.push_back(CreateScreenStateChangedEvent(
+            android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+        events.push_back(CreateScreenStateChangedEvent(
+            android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
+        events.push_back(CreateScreenStateChangedEvent(
+            android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202));
+        events.push_back(CreateScreenStateChangedEvent(
+            android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 800));
+
+        events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
+        events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
+            bucketStartTimeNs + bucketSizeNs + 300));
+
+        events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
+        events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
+            bucketStartTimeNs + bucketSizeNs - 1));
+
+        events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 401));
+        events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
+            bucketStartTimeNs + bucketSizeNs + 700));
+
+        sortLogEventsByTimestamp(&events);
+
+        for (const auto& event : events) {
+            processor->OnLogEvent(event.get());
+        }
+
+        ConfigMetricsReportList reports;
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+
+        EXPECT_EQ(reports.reports_size(), 1);
+        EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+        StatsLogReport::DurationMetricDataWrapper metrics;
+        sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metrics);
+
+        EXPECT_EQ(metrics.data_size(), 3);
+        auto data = metrics.data(0);
+        EXPECT_FALSE(data.dimensions_in_what().has_field());
+        EXPECT_FALSE(data.dimensions_in_condition().has_field());
+        EXPECT_EQ(data.bucket_info_size(), 2);
+        EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9);
+        EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+        EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+        EXPECT_EQ(data.bucket_info(1).duration_nanos(), 30);
+        EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+        EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+        data = metrics.data(1);
+        EXPECT_FALSE(data.dimensions_in_what().has_field());
+        ValidateAttributionUidAndTagDimension(
+            data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1");
+        EXPECT_EQ(data.bucket_info_size(), 2);
+        EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 201 + bucketSizeNs - 600);
+        EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+        EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+        EXPECT_EQ(data.bucket_info(1).duration_nanos(), 300);
+        EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+        EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+        data = metrics.data(2);
+        EXPECT_FALSE(data.dimensions_in_what().has_field());
+        ValidateAttributionUidAndTagDimension(
+            data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2");
+        EXPECT_EQ(data.bucket_info_size(), 2);
+        EXPECT_EQ(data.bucket_info(0).duration_nanos(), 500 - 401 + bucketSizeNs - 600);
+        EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+        EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+        EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
+        EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+        EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+    }
+}
+
+namespace {
+
+StatsdConfig CreateDurationMetricConfigWithLink(DurationMetric::AggregationType aggregationType) {
+    StatsdConfig config;
+    *config.add_atom_matcher() = CreateMoveToBackgroundAtomMatcher();
+    *config.add_atom_matcher() = CreateMoveToForegroundAtomMatcher();
+    *config.add_atom_matcher() = CreateScreenTurnedOnAtomMatcher();
+    *config.add_atom_matcher() = CreateScreenTurnedOffAtomMatcher();
+    *config.add_atom_matcher() = CreateSyncStartAtomMatcher();
+    *config.add_atom_matcher() = CreateSyncEndAtomMatcher();
+
+    auto screenIsOffPredicate = CreateScreenIsOffPredicate();
+    auto isSyncingPredicate = CreateIsSyncingPredicate();
+    auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
+    *syncDimension = CreateAttributionUidAndTagDimensions(
+        android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    syncDimension->add_child()->set_field(2 /* name 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_id(987654);
+    combinationPredicate->mutable_combination()->set_operation(LogicalOperation::OR);
+    addPredicateToPredicateCombination(screenIsOffPredicate, combinationPredicate);
+    addPredicateToPredicateCombination(isSyncingPredicate, combinationPredicate);
+
+    auto metric = config.add_duration_metric();
+    metric->set_bucket(ONE_MINUTE);
+    metric->set_id(StringToId("AppInBackgroundMetric"));
+    metric->set_what(isInBackgroundPredicate.id());
+    metric->set_condition(combinationPredicate->id());
+    *metric->mutable_dimensions_in_what() = CreateDimensions(
+        android::util::ACTIVITY_FOREGROUND_STATE_CHANGED, {1 /* uid field */ });
+    *metric->mutable_dimensions_in_condition() = CreateAttributionUidAndTagDimensions(
+            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+
+    // Links between crash atom and condition of app is in syncing.
+    auto links = metric->add_links();
+    links->set_condition(isSyncingPredicate.id());
+    auto dimensionWhat = links->mutable_fields_in_what();
+    dimensionWhat->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
+    dimensionWhat->add_child()->set_field(1);  // uid field.
+    *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
+            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    return config;
+}
+
+}  // namespace
+
+TEST(DimensionInConditionE2eTest, TestDurationMetricWithLink) {
+    for (auto aggregationType : { DurationMetric::SUM, DurationMetric::MAX_SPARSE}) {
+        ConfigKey cfgKey;
+        auto config = CreateDurationMetricConfigWithLink(aggregationType);
+        int64_t bucketStartTimeNs = 10000000000;
+        int64_t bucketSizeNs =
+            TimeUnitToBucketSizeInMillis(config.duration_metric(0).bucket()) * 1000000LL;
+
+        auto processor = CreateStatsLogProcessor(bucketStartTimeNs / NS_PER_SEC, config, cfgKey);
+        EXPECT_EQ(processor->mMetricsManagers.size(), 1u);
+        EXPECT_TRUE(processor->mMetricsManagers.begin()->second->isConfigValid());
+
+        std::vector<AttributionNode> attributions1 =
+            {CreateAttribution(111, "App1"), CreateAttribution(222, "GMSCoreModule1"),
+             CreateAttribution(222, "GMSCoreModule2")};
+
+        std::vector<AttributionNode> attributions2 =
+            {CreateAttribution(333, "App2"), CreateAttribution(222, "GMSCoreModule1"),
+             CreateAttribution(555, "GMSCoreModule2")};
+
+        std::vector<std::unique_ptr<LogEvent>> events;
+
+        events.push_back(CreateMoveToBackgroundEvent(111, bucketStartTimeNs + 101));
+        events.push_back(CreateMoveToForegroundEvent(111, bucketStartTimeNs + 110));
+
+        events.push_back(CreateMoveToBackgroundEvent(111, bucketStartTimeNs + 201));
+        events.push_back(CreateMoveToForegroundEvent(111, bucketStartTimeNs + bucketSizeNs + 100));
+
+        events.push_back(CreateMoveToBackgroundEvent(333, bucketStartTimeNs + 399));
+        events.push_back(CreateMoveToForegroundEvent(333, bucketStartTimeNs + bucketSizeNs + 800));
+
+        events.push_back(CreateScreenStateChangedEvent(
+            android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 10));
+        events.push_back(CreateScreenStateChangedEvent(
+            android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + 100));
+        events.push_back(CreateScreenStateChangedEvent(
+            android::view::DISPLAY_STATE_ON, bucketStartTimeNs + 202));
+        events.push_back(CreateScreenStateChangedEvent(
+            android::view::DISPLAY_STATE_OFF, bucketStartTimeNs + bucketSizeNs + 801));
+
+        events.push_back(CreateSyncStartEvent(attributions1, "ReadEmail", bucketStartTimeNs + 200));
+        events.push_back(CreateSyncEndEvent(attributions1, "ReadEmail",
+            bucketStartTimeNs + bucketSizeNs + 300));
+
+        events.push_back(CreateSyncStartEvent(attributions1, "ReadDoc", bucketStartTimeNs + 400));
+        events.push_back(CreateSyncEndEvent(attributions1, "ReadDoc",
+            bucketStartTimeNs + bucketSizeNs - 1));
+
+        events.push_back(CreateSyncStartEvent(attributions2, "ReadEmail", bucketStartTimeNs + 401));
+        events.push_back(CreateSyncEndEvent(attributions2, "ReadEmail",
+            bucketStartTimeNs + bucketSizeNs + 700));
+
+        sortLogEventsByTimestamp(&events);
+
+        for (const auto& event : events) {
+            processor->OnLogEvent(event.get());
+        }
+
+        ConfigMetricsReportList reports;
+        processor->onDumpReport(cfgKey, bucketStartTimeNs + 2 * bucketSizeNs + 1, &reports);
+
+        EXPECT_EQ(reports.reports_size(), 1);
+        EXPECT_EQ(reports.reports(0).metrics_size(), 1);
+        StatsLogReport::DurationMetricDataWrapper metrics;
+        sortMetricDataByDimensionsValue(reports.reports(0).metrics(0).duration_metrics(), &metrics);
+
+        EXPECT_EQ(metrics.data_size(), 3);
+        auto data = metrics.data(0);
+        EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+        EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
+        EXPECT_FALSE(data.dimensions_in_condition().has_field());
+        EXPECT_EQ(data.bucket_info_size(), 1);
+        EXPECT_EQ(data.bucket_info(0).duration_nanos(), 9);
+        EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+        EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+
+        data = metrics.data(1);
+        EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+        EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 111);
+        ValidateAttributionUidAndTagDimension(
+            data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 111, "App1");
+        EXPECT_EQ(data.bucket_info_size(), 2);
+        EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 201);
+        EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+        EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+        EXPECT_EQ(data.bucket_info(1).duration_nanos(), 100);
+        EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+        EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+
+        data = metrics.data(2);
+        EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).field(), 1);
+        EXPECT_EQ(data.dimensions_in_what().value_tuple().dimensions_value(0).value_int(), 333);
+        ValidateAttributionUidAndTagDimension(
+            data.dimensions_in_condition(), android::util::SYNC_STATE_CHANGED, 333, "App2");
+        EXPECT_EQ(data.bucket_info_size(), 2);
+        EXPECT_EQ(data.bucket_info(0).duration_nanos(), bucketSizeNs - 401);
+        EXPECT_EQ(data.bucket_info(0).start_bucket_nanos(), bucketStartTimeNs);
+        EXPECT_EQ(data.bucket_info(0).end_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+        EXPECT_EQ(data.bucket_info(1).duration_nanos(), 700);
+        EXPECT_EQ(data.bucket_info(1).start_bucket_nanos(), bucketStartTimeNs + bucketSizeNs);
+        EXPECT_EQ(data.bucket_info(1).end_bucket_nanos(), bucketStartTimeNs + 2 * bucketSizeNs);
+    }
+}
+
+#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/bin/tests/e2e/MetricConditionLink_e2e_test.cpp b/bin/tests/e2e/MetricConditionLink_e2e_test.cpp
index 4504a95..233031c 100644
--- a/bin/tests/e2e/MetricConditionLink_e2e_test.cpp
+++ b/bin/tests/e2e/MetricConditionLink_e2e_test.cpp
@@ -44,9 +44,10 @@
     auto screenIsOffPredicate = CreateScreenIsOffPredicate();
 
     auto isSyncingPredicate = CreateIsSyncingPredicate();
-    *isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions() =
-        CreateDimensions(
-            android::util::SYNC_STATE_CHANGED, {1 /* uid field */, 2 /* name field*/});
+    auto syncDimension = isSyncingPredicate.mutable_simple_predicate()->mutable_dimensions();
+    *syncDimension = CreateAttributionUidDimensions(
+        android::util::SYNC_STATE_CHANGED, {Position::FIRST});
+    syncDimension->add_child()->set_field(2 /* name field*/);
 
     auto isInBackgroundPredicate = CreateIsInBackgroundPredicate();
     *isInBackgroundPredicate.mutable_simple_predicate()->mutable_dimensions() =
@@ -78,9 +79,8 @@
     auto dimensionWhat = links->mutable_fields_in_what();
     dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
     dimensionWhat->add_child()->set_field(1);  // uid field.
-    auto dimensionCondition = links->mutable_fields_in_condition();
-    dimensionCondition->set_field(android::util::SYNC_STATE_CHANGED);
-    dimensionCondition->add_child()->set_field(1);  // uid field.
+    *links->mutable_fields_in_condition() = CreateAttributionUidDimensions(
+            android::util::SYNC_STATE_CHANGED, {Position::FIRST});
 
     // Links between crash atom and condition of app is in background.
     links = countMetric->add_links();
@@ -88,7 +88,7 @@
     dimensionWhat = links->mutable_fields_in_what();
     dimensionWhat->set_field(android::util::PROCESS_LIFE_CYCLE_STATE_CHANGED);
     dimensionWhat->add_child()->set_field(1);  // uid field.
-    dimensionCondition = links->mutable_fields_in_condition();
+    auto dimensionCondition = links->mutable_fields_in_condition();
     dimensionCondition->set_field(android::util::ACTIVITY_FOREGROUND_STATE_CHANGED);
     dimensionCondition->add_child()->set_field(1);  // uid field.
     return config;
@@ -132,12 +132,14 @@
         CreateScreenStateChangedEvent(android::view::DisplayStateEnum::DISPLAY_STATE_ON,
                                       bucketStartTimeNs + 2 * bucketSizeNs - 100);
 
+    std::vector<AttributionNode> attributions =
+        {CreateAttribution(appUid, "App1"), CreateAttribution(appUid + 1, "GMSCoreModule1")};
     auto syncOnEvent1 =
-        CreateSyncStartEvent(appUid, "ReadEmail", bucketStartTimeNs + 50);
+        CreateSyncStartEvent(attributions, "ReadEmail", bucketStartTimeNs + 50);
     auto syncOffEvent1 =
-        CreateSyncEndEvent(appUid, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300);
+        CreateSyncEndEvent(attributions, "ReadEmail", bucketStartTimeNs + bucketSizeNs + 300);
     auto syncOnEvent2 =
-        CreateSyncStartEvent(appUid, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000);
+        CreateSyncStartEvent(attributions, "ReadDoc", bucketStartTimeNs + bucketSizeNs + 2000);
 
     auto moveToBackgroundEvent1 =
         CreateMoveToBackgroundEvent(appUid, bucketStartTimeNs + 15);
diff --git a/bin/tests/metrics/CountMetricProducer_test.cpp b/bin/tests/metrics/CountMetricProducer_test.cpp
index 4ad2097..50b3532 100644
--- a/bin/tests/metrics/CountMetricProducer_test.cpp
+++ b/bin/tests/metrics/CountMetricProducer_test.cpp
@@ -67,9 +67,9 @@
     // Flushes.
     countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
-    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
                 countProducer.mPastBuckets.end());
-    const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+    const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
     EXPECT_EQ(1UL, buckets.size());
     EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
@@ -80,10 +80,10 @@
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
     countProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
     EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
-    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
                 countProducer.mPastBuckets.end());
-    EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY].size());
-    const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY][1];
+    EXPECT_EQ(2UL, countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY].size());
+    const auto& bucketInfo2 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY][1];
     EXPECT_EQ(bucket2StartTimeNs, bucketInfo2.mBucketStartNs);
     EXPECT_EQ(bucket2StartTimeNs + bucketSizeNs, bucketInfo2.mBucketEndNs);
     EXPECT_EQ(1LL, bucketInfo2.mCount);
@@ -91,9 +91,9 @@
     // nothing happens in bucket 3. we should not record anything for bucket 3.
     countProducer.flushIfNeededLocked(bucketStartTimeNs + 3 * bucketSizeNs + 1);
     EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
-    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
                 countProducer.mPastBuckets.end());
-    const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+    const auto& buckets3 = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
     EXPECT_EQ(2UL, buckets3.size());
 }
 
@@ -124,10 +124,10 @@
 
     countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
-    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
                 countProducer.mPastBuckets.end());
     {
-        const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+        const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
         EXPECT_EQ(1UL, buckets.size());
         const auto& bucketInfo = buckets[0];
         EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
@@ -167,9 +167,9 @@
         {getMockedDimensionKey(conditionTagId, 2, "222")};
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse));
+    EXPECT_CALL(*wizard, query(_, key1, _, _)).WillOnce(Return(ConditionState::kFalse));
 
-    EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue));
+    EXPECT_CALL(*wizard, query(_, key2, _, _)).WillOnce(Return(ConditionState::kTrue));
 
     CountMetricProducer countProducer(kConfigKey, metric, 1 /*condition tracker index*/, wizard,
                                       bucketStartTimeNs);
@@ -181,9 +181,9 @@
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event2);
     countProducer.flushIfNeededLocked(bucketStartTimeNs + bucketSizeNs + 1);
     EXPECT_EQ(1UL, countProducer.mPastBuckets.size());
-    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+    EXPECT_TRUE(countProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
                 countProducer.mPastBuckets.end());
-    const auto& buckets = countProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+    const auto& buckets = countProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
     EXPECT_EQ(1UL, buckets.size());
     const auto& bucketInfo = buckets[0];
     EXPECT_EQ(bucketStartTimeNs, bucketInfo.mBucketStartNs);
@@ -229,13 +229,13 @@
 
     EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
     EXPECT_EQ(2L, countProducer.mCurrentSlicedCounter->begin()->second);
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
 
     // One event in bucket #2. No alarm as bucket #0 is trashed out.
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event3);
     EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
     EXPECT_EQ(1L, countProducer.mCurrentSlicedCounter->begin()->second);
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
 
     // Two events in bucket #3.
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event4);
@@ -244,13 +244,13 @@
     EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
     EXPECT_EQ(3L, countProducer.mCurrentSlicedCounter->begin()->second);
     // Anomaly at event 6 is within refractory period. The alarm is at event 5 timestamp not event 6
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
             event5.GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 
     countProducer.onMatchedLogEvent(1 /*log matcher index*/, event7);
     EXPECT_EQ(1UL, countProducer.mCurrentSlicedCounter->size());
     EXPECT_EQ(4L, countProducer.mCurrentSlicedCounter->begin()->second);
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
             event7.GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 }
 
diff --git a/bin/tests/metrics/DurationMetricProducer_test.cpp b/bin/tests/metrics/DurationMetricProducer_test.cpp
index a59f1fe..c9fe252 100644
--- a/bin/tests/metrics/DurationMetricProducer_test.cpp
+++ b/bin/tests/metrics/DurationMetricProducer_test.cpp
@@ -62,9 +62,9 @@
     durationProducer.onMatchedLogEvent(2 /* stop index*/, event2);
     durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
     EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
-    EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+    EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
                 durationProducer.mPastBuckets.end());
-    const auto& buckets = durationProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+    const auto& buckets = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
     EXPECT_EQ(2UL, buckets.size());
     EXPECT_EQ(bucketStartTimeNs, buckets[0].mBucketStartNs);
     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets[0].mBucketEndNs);
@@ -107,9 +107,9 @@
     durationProducer.onMatchedLogEvent(2 /* stop index*/, event4);
     durationProducer.flushIfNeededLocked(bucketStartTimeNs + 2 * bucketSizeNs + 1);
     EXPECT_EQ(1UL, durationProducer.mPastBuckets.size());
-    EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_DIMENSION_KEY) !=
+    EXPECT_TRUE(durationProducer.mPastBuckets.find(DEFAULT_METRIC_DIMENSION_KEY) !=
                 durationProducer.mPastBuckets.end());
-    const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_DIMENSION_KEY];
+    const auto& buckets2 = durationProducer.mPastBuckets[DEFAULT_METRIC_DIMENSION_KEY];
     EXPECT_EQ(1UL, buckets2.size());
     EXPECT_EQ(bucketStartTimeNs + bucketSizeNs, buckets2[0].mBucketStartNs);
     EXPECT_EQ(bucketStartTimeNs + 2 * bucketSizeNs, buckets2[0].mBucketEndNs);
diff --git a/bin/tests/metrics/EventMetricProducer_test.cpp b/bin/tests/metrics/EventMetricProducer_test.cpp
index da00cae..3deab37 100644
--- a/bin/tests/metrics/EventMetricProducer_test.cpp
+++ b/bin/tests/metrics/EventMetricProducer_test.cpp
@@ -114,9 +114,9 @@
     key2[StringToId("APP_IN_BACKGROUND_PER_UID")] = {getMockedDimensionKey(conditionTagId, 2, "222")};
 
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
-    EXPECT_CALL(*wizard, query(_, key1)).WillOnce(Return(ConditionState::kFalse));
+    EXPECT_CALL(*wizard, query(_, key1, _, _)).WillOnce(Return(ConditionState::kFalse));
 
-    EXPECT_CALL(*wizard, query(_, key2)).WillOnce(Return(ConditionState::kTrue));
+    EXPECT_CALL(*wizard, query(_, key2, _, _)).WillOnce(Return(ConditionState::kTrue));
 
     EventMetricProducer eventProducer(kConfigKey, metric, 1, wizard, bucketStartTimeNs);
 
diff --git a/bin/tests/metrics/GaugeMetricProducer_test.cpp b/bin/tests/metrics/GaugeMetricProducer_test.cpp
index 4533ac6..58be5b0 100644
--- a/bin/tests/metrics/GaugeMetricProducer_test.cpp
+++ b/bin/tests/metrics/GaugeMetricProducer_test.cpp
@@ -218,7 +218,7 @@
     EXPECT_EQ(13L,
         gaugeProducer.mCurrentSlicedBucket->begin()->
             second.front().mFields->begin()->second.value_int());
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
 
     std::shared_ptr<LogEvent> event2 =
             std::make_shared<LogEvent>(tagId, bucketStartTimeNs + bucketSizeNs + 20);
@@ -231,7 +231,7 @@
     EXPECT_EQ(15L,
         gaugeProducer.mCurrentSlicedBucket->begin()->
             second.front().mFields->begin()->second.value_int());
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
             event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 
     std::shared_ptr<LogEvent> event3 =
@@ -245,7 +245,7 @@
     EXPECT_EQ(26L,
         gaugeProducer.mCurrentSlicedBucket->begin()->
             second.front().mFields->begin()->second.value_int());
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
             event2->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 
     // The event4 does not have the gauge field. Thus the current bucket value is 0.
diff --git a/bin/tests/metrics/MaxDurationTracker_test.cpp b/bin/tests/metrics/MaxDurationTracker_test.cpp
index 0772b0d..203f028 100644
--- a/bin/tests/metrics/MaxDurationTracker_test.cpp
+++ b/bin/tests/metrics/MaxDurationTracker_test.cpp
@@ -41,22 +41,24 @@
 
 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) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(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");
+
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     int64_t metricId = 1;
-    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
-                               bucketSizeNs, false, {});
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+                               false, bucketStartTimeNs, bucketSizeNs, false, {});
 
     tracker.noteStart(key1, true, bucketStartTimeNs, ConditionKey());
     // Event starts again. This would not change anything as it already starts.
@@ -75,16 +77,22 @@
 }
 
 TEST(MaxDurationTrackerTest, TestStopAll) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(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");
+
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     int64_t metricId = 1;
-    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
-                               bucketSizeNs, false, {});
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+                               false, bucketStartTimeNs, bucketSizeNs, false, {});
 
     tracker.noteStart(key1, true, bucketStartTimeNs + 1, ConditionKey());
 
@@ -105,21 +113,26 @@
 }
 
 TEST(MaxDurationTrackerTest, TestCrossBucketBoundary) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(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");
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     int64_t metricId = 1;
-    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, false, bucketStartTimeNs,
-                               bucketSizeNs, false, {});
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+                               false, bucketStartTimeNs, bucketSizeNs, false, {});
 
     // The event starts.
     tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
 
-    // Starts again. Does not change anything.
+    // Starts again. Does not DEFAULT_DIMENSION_KEY anything.
     tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + bucketSizeNs + 1,
                       ConditionKey());
 
@@ -135,16 +148,21 @@
 }
 
 TEST(MaxDurationTrackerTest, TestCrossBucketBoundary_nested) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(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");
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
     int64_t metricId = 1;
-    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs,
-                               bucketSizeNs, false, {});
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+                               true, bucketStartTimeNs, bucketSizeNs, false, {});
 
     // 2 starts
     tracker.noteStart(DEFAULT_DIMENSION_KEY, true, bucketStartTimeNs + 1, ConditionKey());
@@ -160,7 +178,8 @@
     EXPECT_EQ(bucketSizeNs, buckets[eventKey][1].mDuration);
 
     // real stop now.
-    tracker.noteStop(DEFAULT_DIMENSION_KEY, bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
+    tracker.noteStop(DEFAULT_DIMENSION_KEY,
+                     bucketStartTimeNs + (2 * bucketSizeNs) + 5, false);
     tracker.flushIfNeeded(bucketStartTimeNs + (3 * bucketSizeNs) + 1, &buckets);
 
     EXPECT_EQ(3u, buckets[eventKey].size());
@@ -170,16 +189,20 @@
 }
 
 TEST(MaxDurationTrackerTest, TestMaxDurationWithCondition) {
+    const std::vector<HashableDimensionKey> conditionKey = {getMockedDimensionKey(TagId, 4, "1")};
+    const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey conditionKey1;
-    HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 2, "maps");
+    MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 2, "maps");
     conditionKey1[StringToId("APP_BACKGROUND")] = conditionKey;
 
-    EXPECT_CALL(*wizard, query(_, conditionKey1))  // #4
+    EXPECT_CALL(*wizard, query(_, conditionKey1, _, _))  // #4
             .WillOnce(Return(ConditionState::kFalse));
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
@@ -187,8 +210,8 @@
     int64_t durationTimeNs = 2 * 1000;
 
     int64_t metricId = 1;
-    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false, bucketStartTimeNs,
-                               bucketSizeNs, true, {});
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                               false, bucketStartTimeNs, bucketSizeNs, true, {});
     EXPECT_TRUE(tracker.mAnomalyTrackers.empty());
 
     tracker.noteStart(key1, true, eventStartTimeNs, conditionKey1);
@@ -204,6 +227,10 @@
 }
 
 TEST(MaxDurationTrackerTest, TestAnomalyDetection) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "1");
+    const HashableDimensionKey key1 = getMockedDimensionKey(TagId, 1, "1");
+    const HashableDimensionKey key2 = getMockedDimensionKey(TagId, 1, "2");
+    FieldMatcher dimensionInCondition;
     int64_t metricId = 1;
     Alert alert;
     alert.set_id(101);
@@ -213,7 +240,7 @@
     const int32_t refPeriodSec = 1;
     alert.set_refractory_period_secs(refPeriodSec);
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
@@ -221,8 +248,8 @@
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
-    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, true, bucketStartTimeNs,
-                               bucketSizeNs, false, {anomalyTracker});
+    MaxDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, -1, dimensionInCondition,
+                               true, bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
 
     tracker.noteStart(key1, true, eventStartTimeNs, ConditionKey());
     tracker.noteStop(key1, eventStartTimeNs + 10, false);
diff --git a/bin/tests/metrics/OringDurationTracker_test.cpp b/bin/tests/metrics/OringDurationTracker_test.cpp
index 6b8893e..80e16a1 100644
--- a/bin/tests/metrics/OringDurationTracker_test.cpp
+++ b/bin/tests/metrics/OringDurationTracker_test.cpp
@@ -38,24 +38,26 @@
 const ConfigKey kConfigKey(0, 12345);
 const int TagId = 1;
 const int64_t metricId = 123;
-const HashableDimensionKey eventKey = getMockedDimensionKey(TagId, 0, "event");
-
-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) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+    const std::vector<HashableDimensionKey> kConditionKey1 =
+        {getMockedDimensionKey(TagId, 1, "maps")};
+    const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+    const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
-                                 bucketStartTimeNs, bucketSizeNs, false, {});
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                                 false, bucketStartTimeNs, bucketSizeNs, false, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
@@ -71,16 +73,23 @@
 }
 
 TEST(OringDurationTrackerTest, TestDurationNested) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+    const std::vector<HashableDimensionKey> kConditionKey1 =
+        {getMockedDimensionKey(TagId, 1, "maps")};
+    const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+    const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, false, {});
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                                 true, bucketStartTimeNs, bucketSizeNs, false, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
     tracker.noteStart(kEventKey1, true, eventStartTimeNs + 10, ConditionKey());  // overlapping wl
@@ -95,16 +104,23 @@
 }
 
 TEST(OringDurationTrackerTest, TestStopAll) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+    const std::vector<HashableDimensionKey> kConditionKey1 =
+        {getMockedDimensionKey(TagId, 1, "maps")};
+    const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+    const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, false, {});
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                                 true, bucketStartTimeNs, bucketSizeNs, false, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
     tracker.noteStart(kEventKey2, true, eventStartTimeNs + 10, ConditionKey());  // overlapping wl
@@ -118,17 +134,24 @@
 }
 
 TEST(OringDurationTrackerTest, TestCrossBucketBoundary) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+    const std::vector<HashableDimensionKey> kConditionKey1 =
+        {getMockedDimensionKey(TagId, 1, "maps")};
+    const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+    const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, false, {});
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                                 true, bucketStartTimeNs, bucketSizeNs, false, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
     EXPECT_EQ((long long)eventStartTimeNs, tracker.mLastStartTime);
@@ -150,23 +173,30 @@
 }
 
 TEST(OringDurationTrackerTest, TestDurationConditionChange) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+    const std::vector<HashableDimensionKey> kConditionKey1 =
+        {getMockedDimensionKey(TagId, 1, "maps")};
+    const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+    const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
     key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
 
-    EXPECT_CALL(*wizard, query(_, key1))  // #4
+    EXPECT_CALL(*wizard, query(_, key1, _, _))  // #4
             .WillOnce(Return(ConditionState::kFalse));
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
-                                 bucketStartTimeNs, bucketSizeNs, true, {});
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                                 false, bucketStartTimeNs, bucketSizeNs, true, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
 
@@ -181,25 +211,32 @@
 }
 
 TEST(OringDurationTrackerTest, TestDurationConditionChange2) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+    const std::vector<HashableDimensionKey> kConditionKey1 =
+        {getMockedDimensionKey(TagId, 1, "maps")};
+    const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+    const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
     key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
 
-    EXPECT_CALL(*wizard, query(_, key1))
+    EXPECT_CALL(*wizard, query(_, key1, _, _))
             .Times(2)
             .WillOnce(Return(ConditionState::kFalse))
             .WillOnce(Return(ConditionState::kTrue));
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
     uint64_t durationTimeNs = 2 * 1000;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, false,
-                                 bucketStartTimeNs, bucketSizeNs, true, {});
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                                 false, bucketStartTimeNs, bucketSizeNs, true, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
     // condition to false; record duration 5n
@@ -216,22 +253,29 @@
 }
 
 TEST(OringDurationTrackerTest, TestDurationConditionChangeNested) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+    const std::vector<HashableDimensionKey> kConditionKey1 =
+        {getMockedDimensionKey(TagId, 1, "maps")};
+    const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+    const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+    FieldMatcher dimensionInCondition;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     ConditionKey key1;
     key1[StringToId("APP_BACKGROUND")] = kConditionKey1;
 
-    EXPECT_CALL(*wizard, query(_, key1))  // #4
+    EXPECT_CALL(*wizard, query(_, key1, _, _))  // #4
             .WillOnce(Return(ConditionState::kFalse));
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
 
     uint64_t bucketStartTimeNs = 10000000000;
     uint64_t eventStartTimeNs = bucketStartTimeNs + 1;
     uint64_t bucketSizeNs = 30 * 1000 * 1000 * 1000LL;
 
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, true, {});
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                                 true, bucketStartTimeNs, bucketSizeNs, true, {});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, key1);
     tracker.noteStart(kEventKey1, true, eventStartTimeNs + 2, key1);
@@ -249,6 +293,13 @@
 }
 
 TEST(OringDurationTrackerTest, TestPredictAnomalyTimestamp) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+    const std::vector<HashableDimensionKey> kConditionKey1 =
+        {getMockedDimensionKey(TagId, 1, "maps")};
+    const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+    const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+    FieldMatcher dimensionInCondition;
     Alert alert;
     alert.set_id(101);
     alert.set_metric_id(1);
@@ -256,7 +307,7 @@
     alert.set_num_buckets(2);
     alert.set_refractory_period_secs(1);
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
@@ -264,8 +315,8 @@
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true, bucketStartTimeNs,
-                                 bucketSizeNs, true, {anomalyTracker});
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                                 true, bucketStartTimeNs, bucketSizeNs, true, {anomalyTracker});
 
     // Nothing in the past bucket.
     tracker.noteStart(DEFAULT_DIMENSION_KEY, true, eventStartTimeNs, ConditionKey());
@@ -310,6 +361,12 @@
 }
 
 TEST(OringDurationTrackerTest, TestAnomalyDetectionExpiredAlarm) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+    const std::vector<HashableDimensionKey> kConditionKey1 = {getMockedDimensionKey(TagId, 1, "maps")};
+    const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+    const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+    FieldMatcher dimensionInCondition;
     Alert alert;
     alert.set_id(101);
     alert.set_metric_id(1);
@@ -318,7 +375,7 @@
     const int32_t refPeriodSec = 45;
     alert.set_refractory_period_secs(refPeriodSec);
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
 
     uint64_t bucketStartTimeNs = 10 * NS_PER_SEC;
@@ -326,8 +383,8 @@
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
-                                 bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                                 true /*nesting*/, bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
 
     tracker.noteStart(kEventKey1, true, eventStartTimeNs, ConditionKey());
     tracker.noteStop(kEventKey1, eventStartTimeNs + 10, false);
@@ -352,6 +409,13 @@
 }
 
 TEST(OringDurationTrackerTest, TestAnomalyDetectionFiredAlarm) {
+    const MetricDimensionKey eventKey = getMockedMetricDimensionKey(TagId, 0, "event");
+
+    const std::vector<HashableDimensionKey> kConditionKey1 =
+        {getMockedDimensionKey(TagId, 1, "maps")};
+    const HashableDimensionKey kEventKey1 = getMockedDimensionKey(TagId, 2, "maps");
+    const HashableDimensionKey kEventKey2 = getMockedDimensionKey(TagId, 3, "maps");
+    FieldMatcher dimensionInCondition;
     Alert alert;
     alert.set_id(101);
     alert.set_metric_id(1);
@@ -360,7 +424,7 @@
     const int32_t refPeriodSec = 45;
     alert.set_refractory_period_secs(refPeriodSec);
 
-    unordered_map<HashableDimensionKey, vector<DurationBucket>> buckets;
+    unordered_map<MetricDimensionKey, vector<DurationBucket>> buckets;
     sp<MockConditionWizard> wizard = new NaggyMock<MockConditionWizard>();
     ConditionKey conkey;
     conkey[StringToId("APP_BACKGROUND")] = kConditionKey1;
@@ -369,8 +433,9 @@
     uint64_t bucketSizeNs = 30 * NS_PER_SEC;
 
     sp<DurationAnomalyTracker> anomalyTracker = new DurationAnomalyTracker(alert, kConfigKey);
-    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, true /*nesting*/,
-                                 bucketStartTimeNs, bucketSizeNs, false, {anomalyTracker});
+    OringDurationTracker tracker(kConfigKey, metricId, eventKey, wizard, 1, dimensionInCondition,
+                                 true /*nesting*/, bucketStartTimeNs, bucketSizeNs, false,
+                                 {anomalyTracker});
 
     tracker.noteStart(kEventKey1, true, 15 * NS_PER_SEC, conkey); // start key1
     EXPECT_EQ(1u, anomalyTracker->mAlarms.size());
diff --git a/bin/tests/metrics/ValueMetricProducer_test.cpp b/bin/tests/metrics/ValueMetricProducer_test.cpp
index fff3dbf..55c078d 100644
--- a/bin/tests/metrics/ValueMetricProducer_test.cpp
+++ b/bin/tests/metrics/ValueMetricProducer_test.cpp
@@ -299,26 +299,26 @@
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event1);
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event2);
     // Value sum == 30 <= 130.
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
 
     // One event in bucket #2. No alarm as bucket #0 is trashed out.
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event3);
     // Value sum == 130 <= 130.
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY), 0U);
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY), 0U);
 
     // Three events in bucket #3.
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event4);
     // Anomaly at event 4 since Value sum == 131 > 130!
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
             event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event5);
     // Event 5 is within 3 sec refractory period. Thus last alarm timestamp is still event4.
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
             event4->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 
     valueProducer.onMatchedLogEvent(1 /*log matcher index*/, *event6);
     // Anomaly at event 6 since Value sum == 160 > 130 and after refractory period.
-    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_DIMENSION_KEY),
+    EXPECT_EQ(anomalyTracker->getRefractoryPeriodEndsSec(DEFAULT_METRIC_DIMENSION_KEY),
             event6->GetTimestampNs() / NS_PER_SEC + refPeriodSec);
 }
 
diff --git a/bin/tests/metrics/metrics_test_helper.cpp b/bin/tests/metrics/metrics_test_helper.cpp
index fc7245c..ab9345a 100644
--- a/bin/tests/metrics/metrics_test_helper.cpp
+++ b/bin/tests/metrics/metrics_test_helper.cpp
@@ -26,6 +26,13 @@
     return HashableDimensionKey(dimensionsValue);
 }
 
+MetricDimensionKey getMockedMetricDimensionKey(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 MetricDimensionKey(HashableDimensionKey(dimensionsValue), DEFAULT_DIMENSION_KEY);
+}
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
\ No newline at end of file
diff --git a/bin/tests/metrics/metrics_test_helper.h b/bin/tests/metrics/metrics_test_helper.h
index 23e86f9..0a97456 100644
--- a/bin/tests/metrics/metrics_test_helper.h
+++ b/bin/tests/metrics/metrics_test_helper.h
@@ -25,10 +25,12 @@
 
 class MockConditionWizard : public ConditionWizard {
 public:
-    MOCK_METHOD2(
+    MOCK_METHOD4(
             query,
             ConditionState(const int conditionIndex,
-                           const ConditionKey& conditionParameters));
+                           const ConditionKey& conditionParameters,
+                           const FieldMatcher& dimensionFields,
+                           std::unordered_set<HashableDimensionKey> *dimensionKeySet));
 };
 
 class MockStatsPullerManager : public StatsPullerManager {
@@ -39,6 +41,7 @@
 };
 
 HashableDimensionKey getMockedDimensionKey(int tagId, int key, std::string value);
+MetricDimensionKey getMockedMetricDimensionKey(int tagId, int key, std::string value);
 
 }  // namespace statsd
 }  // namespace os
diff --git a/bin/tests/statsd_test_util.cpp b/bin/tests/statsd_test_util.cpp
index 9f4582d..13055cb 100644
--- a/bin/tests/statsd_test_util.cpp
+++ b/bin/tests/statsd_test_util.cpp
@@ -12,7 +12,6 @@
 // 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 {
@@ -27,6 +26,22 @@
     return atom_matcher;
 }
 
+AtomMatcher CreateScreenBrightnessChangedAtomMatcher() {
+    AtomMatcher atom_matcher;
+    atom_matcher.set_id(StringToId("ScreenBrightnessChanged"));
+    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+    simple_atom_matcher->set_atom_id(android::util::SCREEN_BRIGHTNESS_CHANGED);
+    return atom_matcher;
+}
+
+AtomMatcher CreateUidProcessStateChangedAtomMatcher() {
+    AtomMatcher atom_matcher;
+    atom_matcher.set_id(StringToId("UidProcessStateChanged"));
+    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+    simple_atom_matcher->set_atom_id(android::util::UID_PROCESS_STATE_CHANGED);
+    return atom_matcher;
+}
+
 AtomMatcher CreateWakelockStateChangedAtomMatcher(const string& name,
                                                   WakelockStateChanged::State state) {
     AtomMatcher atom_matcher;
@@ -47,6 +62,30 @@
     return CreateWakelockStateChangedAtomMatcher("ReleaseWakelock", WakelockStateChanged::RELEASE);
 }
 
+AtomMatcher CreateBatterySaverModeStateChangedAtomMatcher(
+    const string& name, BatterySaverModeStateChanged::State state) {
+    AtomMatcher atom_matcher;
+    atom_matcher.set_id(StringToId(name));
+    auto simple_atom_matcher = atom_matcher.mutable_simple_atom_matcher();
+    simple_atom_matcher->set_atom_id(android::util::BATTERY_SAVER_MODE_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 CreateBatterySaverModeStartAtomMatcher() {
+    return CreateBatterySaverModeStateChangedAtomMatcher(
+        "BatterySaverModeStart", BatterySaverModeStateChanged::ON);
+}
+
+
+AtomMatcher CreateBatterySaverModeStopAtomMatcher() {
+    return CreateBatterySaverModeStateChangedAtomMatcher(
+        "BatterySaverModeStop", BatterySaverModeStateChanged::OFF);
+}
+
+
 AtomMatcher CreateScreenStateChangedAtomMatcher(
     const string& name, android::view::DisplayStateEnum state) {
     AtomMatcher atom_matcher;
@@ -59,6 +98,7 @@
     return atom_matcher;
 }
 
+
 AtomMatcher CreateScreenTurnedOnAtomMatcher() {
     return CreateScreenStateChangedAtomMatcher("ScreenTurnedOn",
             android::view::DisplayStateEnum::DISPLAY_STATE_ON);
@@ -128,6 +168,13 @@
         "ProcessCrashed", ProcessLifeCycleStateChanged::PROCESS_CRASHED);
 }
 
+Predicate CreateBatterySaverModePredicate() {
+    Predicate predicate;
+    predicate.set_id(StringToId("BatterySaverIsOn"));
+    predicate.mutable_simple_predicate()->set_start(StringToId("BatterySaverModeStart"));
+    predicate.mutable_simple_predicate()->set_stop(StringToId("BatterySaverModeStop"));
+    return predicate;
+}
 
 Predicate CreateScreenIsOnPredicate() {
     Predicate predicate;
@@ -218,6 +265,31 @@
     return event;
 }
 
+std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs) {
+    auto event = std::make_unique<LogEvent>(
+        android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs);
+    EXPECT_TRUE(event->write(BatterySaverModeStateChanged::ON));
+    event->init();
+    return event;
+}
+
+std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs) {
+    auto event = std::make_unique<LogEvent>(
+        android::util::BATTERY_SAVER_MODE_STATE_CHANGED, timestampNs);
+    EXPECT_TRUE(event->write(BatterySaverModeStateChanged::OFF));
+    event->init();
+    return event;
+}
+
+std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
+    int level, uint64_t timestampNs) {
+    auto event = std::make_unique<LogEvent>(android::util::SCREEN_BRIGHTNESS_CHANGED, timestampNs);
+    EXPECT_TRUE(event->write(level));
+    event->init();
+    return event;
+
+}
+
 std::unique_ptr<LogEvent> CreateWakelockStateChangedEvent(
     const std::vector<AttributionNode>& attributions, const string& wakelockName,
     const WakelockStateChanged::State state, uint64_t timestampNs) {
@@ -267,9 +339,10 @@
 }
 
 std::unique_ptr<LogEvent> CreateSyncStateChangedEvent(
-    const int uid, const string& name, const SyncStateChanged::State state, uint64_t timestampNs) {
+    const std::vector<AttributionNode>& attributions,
+    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(attributions);
     event->write(name);
     event->write(state);
     event->init();
@@ -277,13 +350,13 @@
 }
 
 std::unique_ptr<LogEvent> CreateSyncStartEvent(
-    const int uid, const string& name, uint64_t timestampNs){
-    return CreateSyncStateChangedEvent(uid, name, SyncStateChanged::ON, timestampNs);
+    const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs){
+    return CreateSyncStateChangedEvent(attributions, 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);
+    const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs) {
+    return CreateSyncStateChangedEvent(attributions, name, SyncStateChanged::OFF, timestampNs);
 }
 
 std::unique_ptr<LogEvent> CreateProcessLifeCycleStateChangedEvent(
diff --git a/bin/tests/statsd_test_util.h b/bin/tests/statsd_test_util.h
index ff8fef0..6638893 100644
--- a/bin/tests/statsd_test_util.h
+++ b/bin/tests/statsd_test_util.h
@@ -14,6 +14,7 @@
 
 #pragma once
 
+#include <gtest/gtest.h>
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "statslog.h"
 #include "src/logd/LogEvent.h"
@@ -26,6 +27,18 @@
 // Create AtomMatcher proto to simply match a specific atom type.
 AtomMatcher CreateSimpleAtomMatcher(const string& name, int atomId);
 
+// Create AtomMatcher proto for screen brightness state changed.
+AtomMatcher CreateScreenBrightnessChangedAtomMatcher();
+
+// Create AtomMatcher proto for starting battery save mode.
+AtomMatcher CreateBatterySaverModeStartAtomMatcher();
+
+// Create AtomMatcher proto for stopping battery save mode.
+AtomMatcher CreateBatterySaverModeStopAtomMatcher();
+
+// Create AtomMatcher proto for process state changed.
+AtomMatcher CreateUidProcessStateChangedAtomMatcher();
+
 // Create AtomMatcher proto for acquiring wakelock.
 AtomMatcher CreateAcquireWakelockAtomMatcher();
 
@@ -59,6 +72,9 @@
 // Create Predicate proto for screen is off.
 Predicate CreateScreenIsOffPredicate();
 
+// Create Predicate proto for battery saver mode.
+Predicate CreateBatterySaverModePredicate();
+
 // Create Predicate proto for holding wakelock.
 Predicate CreateHoldingWakelockPredicate();
 
@@ -86,6 +102,15 @@
 std::unique_ptr<LogEvent> CreateScreenStateChangedEvent(
     const android::view::DisplayStateEnum state, uint64_t timestampNs);
 
+// Create log event for screen brightness state changed.
+std::unique_ptr<LogEvent> CreateScreenBrightnessChangedEvent(
+   int level, uint64_t timestampNs);
+
+// Create log event when battery saver starts.
+std::unique_ptr<LogEvent> CreateBatterySaverOnEvent(uint64_t timestampNs);
+// Create log event when battery saver stops.
+std::unique_ptr<LogEvent> CreateBatterySaverOffEvent(uint64_t timestampNs);
+
 // Create log event for app moving to background.
 std::unique_ptr<LogEvent> CreateMoveToBackgroundEvent(const int uid, uint64_t timestampNs);
 
@@ -94,11 +119,11 @@
 
 // Create log event when the app sync starts.
 std::unique_ptr<LogEvent> CreateSyncStartEvent(
-    const int uid, const string& name, uint64_t timestampNs);
+    const std::vector<AttributionNode>& attributions, 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);
+    const std::vector<AttributionNode>& attributions, const string& name, uint64_t timestampNs);
 
 // Create log event when the app sync ends.
 std::unique_ptr<LogEvent> CreateAppCrashEvent(
@@ -136,9 +161,12 @@
 
 template <typename T>
 void sortMetricDataByDimensionsValue(const T& metricData, T* sortedMetricData) {
-    std::map<HashableDimensionKey, int> dimensionIndexMap;
+    std::map<MetricDimensionKey, int> dimensionIndexMap;
     for (int i = 0; i < metricData.data_size(); ++i) {
-        dimensionIndexMap.insert(std::make_pair(metricData.data(i).dimensions_in_what(), i));
+        dimensionIndexMap.insert(std::make_pair(
+            MetricDimensionKey(HashableDimensionKey(metricData.data(i).dimensions_in_what()),
+            HashableDimensionKey(metricData.data(i).dimensions_in_condition())),
+            i));
     }
     for (const auto& itr : dimensionIndexMap) {
         *sortedMetricData->add_data() = metricData.data(itr.second);