Make SimpleCondition satisfactorily complicated.

+ Support nested counting
+ Support StopAll
+ Added default_condition to SimpleCondition config
+ Some refactoring/clean up
+ Added unit tests

Test: Added unit tests, statsd_test
Change-Id: I6564ac2e068ce6810e8090c0818064c625c7847a
diff --git a/bin/Android.mk b/bin/Android.mk
index d99136f..929c3cc 100644
--- a/bin/Android.mk
+++ b/bin/Android.mk
@@ -151,19 +151,19 @@
     $(statsd_common_src) \
     tests/AnomalyMonitor_test.cpp \
     tests/anomaly/AnomalyTracker_test.cpp \
-    tests/ConditionTracker_test.cpp \
     tests/ConfigManager_test.cpp \
     tests/indexed_priority_queue_test.cpp \
     tests/LogEntryMatcher_test.cpp \
     tests/LogReader_test.cpp \
     tests/MetricsManager_test.cpp \
     tests/UidMap_test.cpp \
+    tests/condition/CombinationConditionTracker_test.cpp \
+    tests/condition/SimpleConditionTracker_test.cpp \
     tests/metrics/OringDurationTracker_test.cpp \
     tests/metrics/MaxDurationTracker_test.cpp \
     tests/metrics/CountMetricProducer_test.cpp \
     tests/metrics/EventMetricProducer_test.cpp
 
-
 LOCAL_STATIC_LIBRARIES := \
     libgmock
 
diff --git a/bin/src/condition/CombinationConditionTracker.cpp b/bin/src/condition/CombinationConditionTracker.cpp
index 953bcb3..41f5fca 100644
--- a/bin/src/condition/CombinationConditionTracker.cpp
+++ b/bin/src/condition/CombinationConditionTracker.cpp
@@ -115,49 +115,47 @@
             evaluateCombinationCondition(mChildren, mLogicalOperation, conditionCache);
 }
 
-bool CombinationConditionTracker::evaluateCondition(
+void CombinationConditionTracker::evaluateCondition(
         const LogEvent& event, const std::vector<MatchingState>& eventMatcherValues,
         const std::vector<sp<ConditionTracker>>& mAllConditions,
         std::vector<ConditionState>& nonSlicedConditionCache,
-        std::vector<bool>& nonSlicedChangedCache, vector<bool>& slicedConditionChanged) {
+        std::vector<bool>& conditionChangedCache) {
     // value is up to date.
     if (nonSlicedConditionCache[mIndex] != ConditionState::kNotEvaluated) {
-        return false;
+        return;
     }
 
     for (const int childIndex : mChildren) {
         if (nonSlicedConditionCache[childIndex] == ConditionState::kNotEvaluated) {
             const sp<ConditionTracker>& child = mAllConditions[childIndex];
             child->evaluateCondition(event, eventMatcherValues, mAllConditions,
-                                     nonSlicedConditionCache, nonSlicedChangedCache,
-                                     slicedConditionChanged);
+                                     nonSlicedConditionCache, conditionChangedCache);
         }
     }
 
-    ConditionState newCondition =
-            evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache);
+    if (!mSliced) {
+        ConditionState newCondition =
+                evaluateCombinationCondition(mChildren, mLogicalOperation, nonSlicedConditionCache);
 
-    bool nonSlicedChanged = (mNonSlicedConditionState != newCondition);
-    mNonSlicedConditionState = newCondition;
+        bool nonSlicedChanged = (mNonSlicedConditionState != newCondition);
+        mNonSlicedConditionState = newCondition;
 
-    nonSlicedConditionCache[mIndex] = mNonSlicedConditionState;
+        nonSlicedConditionCache[mIndex] = mNonSlicedConditionState;
 
-    nonSlicedChangedCache[mIndex] = nonSlicedChanged;
-
-    if (mSliced) {
+        conditionChangedCache[mIndex] = nonSlicedChanged;
+    } else {
         for (const int childIndex : mChildren) {
             // If any of the sliced condition in children condition changes, the combination
             // condition may be changed too.
-            if (slicedConditionChanged[childIndex]) {
-                slicedConditionChanged[mIndex] = true;
+            if (conditionChangedCache[childIndex]) {
+                conditionChangedCache[mIndex] = true;
                 break;
             }
         }
+        nonSlicedConditionCache[mIndex] = ConditionState::kUnknown;
         ALOGD("CombinationCondition %s sliced may changed? %d", mName.c_str(),
-              slicedConditionChanged[mIndex] == true);
+              conditionChangedCache[mIndex] == true);
     }
-
-    return nonSlicedChanged;
 }
 
 }  // namespace statsd
diff --git a/bin/src/condition/CombinationConditionTracker.h b/bin/src/condition/CombinationConditionTracker.h
index dbdb3b7..3d2c6bb 100644
--- a/bin/src/condition/CombinationConditionTracker.h
+++ b/bin/src/condition/CombinationConditionTracker.h
@@ -35,12 +35,11 @@
               const std::unordered_map<std::string, int>& conditionNameIndexMap,
               std::vector<bool>& stack) override;
 
-    bool evaluateCondition(const LogEvent& event,
+    void evaluateCondition(const LogEvent& event,
                            const std::vector<MatchingState>& eventMatcherValues,
                            const std::vector<sp<ConditionTracker>>& mAllConditions,
                            std::vector<ConditionState>& conditionCache,
-                           std::vector<bool>& changedCache,
-                           std::vector<bool>& slicedConditionMayChanged) override;
+                           std::vector<bool>& changedCache) override;
 
     void isConditionMet(const std::map<std::string, HashableDimensionKey>& conditionParameters,
                         const std::vector<sp<ConditionTracker>>& allConditions,
diff --git a/bin/src/condition/ConditionTracker.h b/bin/src/condition/ConditionTracker.h
index bb5ddeb..0ac7ef3 100644
--- a/bin/src/condition/ConditionTracker.h
+++ b/bin/src/condition/ConditionTracker.h
@@ -56,25 +56,20 @@
                       std::vector<bool>& stack) = 0;
 
     // evaluate current condition given the new event.
-    // return true if the condition state changed, false if the condition state is not changed.
     // event: the new log event
     // eventMatcherValues: the results of the LogMatcherTrackers. LogMatcherTrackers always process
     //                     event before ConditionTrackers, because ConditionTracker depends on
     //                     LogMatchingTrackers.
     // mAllConditions: the list of all ConditionTracker
     // conditionCache: the cached non-sliced condition of the ConditionTrackers for this new event.
-    // nonSlicedConditionChanged: the bit map to record whether non-sliced condition has changed.
-    // slicedConditionMayChanged: the bit map to record whether sliced condition may have changed.
-    //      Because sliced condition needs parameters to determine the value. So the sliced
-    //      condition is not pushed to metrics. We only inform the relevant metrics that the sliced
-    //      condition may have changed, and metrics should pull the conditions that they are
-    //      interested in.
-    virtual bool evaluateCondition(const LogEvent& event,
+    // conditionChanged: the bit map to record whether the condition has changed.
+    //                   If the condition has dimension, then any sub condition changes will report
+    //                   conditionChanged.
+    virtual void evaluateCondition(const LogEvent& event,
                                    const std::vector<MatchingState>& eventMatcherValues,
                                    const std::vector<sp<ConditionTracker>>& mAllConditions,
                                    std::vector<ConditionState>& conditionCache,
-                                   std::vector<bool>& nonSlicedConditionChanged,
-                                   std::vector<bool>& slicedConditionMayChanged) = 0;
+                                   std::vector<bool>& conditionChanged) = 0;
 
     // Return the current condition state.
     virtual ConditionState isConditionMet() {
diff --git a/bin/src/condition/SimpleConditionTracker.cpp b/bin/src/condition/SimpleConditionTracker.cpp
index b691fae..a694dbf 100644
--- a/bin/src/condition/SimpleConditionTracker.cpp
+++ b/bin/src/condition/SimpleConditionTracker.cpp
@@ -74,13 +74,21 @@
         mStopAllLogMatcherIndex = -1;
     }
 
-    mDimension.insert(mDimension.begin(), simpleCondition.dimension().begin(),
-                      simpleCondition.dimension().end());
+    mOutputDimension.insert(mOutputDimension.begin(), simpleCondition.dimension().begin(),
+                            simpleCondition.dimension().end());
 
-    if (mDimension.size() > 0) {
+    if (mOutputDimension.size() > 0) {
         mSliced = true;
     }
 
+    if (simpleCondition.initial_value() == SimpleCondition_InitialValue_FALSE) {
+        mInitialValue = ConditionState::kFalse;
+    } else {
+        mInitialValue = ConditionState::kUnknown;
+    }
+
+    mNonSlicedConditionState = mInitialValue;
+
     mInitialized = true;
 }
 
@@ -97,127 +105,166 @@
     return mInitialized;
 }
 
-void print(unordered_map<HashableDimensionKey, ConditionState>& conditions, const string& name) {
+void print(map<HashableDimensionKey, int>& conditions, const string& name) {
     VLOG("%s DUMP:", name.c_str());
-
     for (const auto& pair : conditions) {
-        VLOG("\t%s %d", pair.first.c_str(), pair.second);
+        VLOG("\t%s : %d", pair.first.c_str(), pair.second);
     }
 }
 
-bool SimpleConditionTracker::evaluateCondition(const LogEvent& event,
+void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditionCache,
+                                           std::vector<bool>& conditionChangedCache) {
+    // Unless the default condition is false, and there was nothing started, otherwise we have
+    // triggered a condition change.
+    conditionChangedCache[mIndex] =
+            (mInitialValue == ConditionState::kFalse && mSlicedConditionState.empty()) ? false
+                                                                                           : true;
+
+    // After StopAll, we know everything has stopped. From now on, default condition is false.
+    mInitialValue = ConditionState::kFalse;
+    mSlicedConditionState.clear();
+    conditionCache[mIndex] = ConditionState::kFalse;
+}
+
+void SimpleConditionTracker::handleConditionEvent(const HashableDimensionKey& outputKey,
+                                                  bool matchStart,
+                                                  std::vector<ConditionState>& conditionCache,
+                                                  std::vector<bool>& conditionChangedCache) {
+    bool changed = false;
+    auto outputIt = mSlicedConditionState.find(outputKey);
+    ConditionState newCondition;
+    if (outputIt == mSlicedConditionState.end()) {
+        // We get a new output key.
+        newCondition = matchStart ? ConditionState::kTrue : ConditionState::kFalse;
+        if (matchStart && mInitialValue != ConditionState::kTrue) {
+            mSlicedConditionState[outputKey] = 1;
+            changed = true;
+        } else if (mInitialValue != ConditionState::kFalse) {
+            // it's a stop and we don't have history about it.
+            // If the default condition is not false, it means this stop is valuable to us.
+            mSlicedConditionState[outputKey] = 0;
+            changed = true;
+        }
+    } else {
+        // we have history about this output key.
+        auto& startedCount = outputIt->second;
+        // assign the old value first.
+        newCondition = startedCount > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+        if (matchStart) {
+            if (startedCount == 0) {
+                // This condition for this output key will change from false -> true
+                changed = true;
+            }
+
+            // it's ok to do ++ here, even if we don't count nesting. The >1 counts will be treated
+            // as 1 if not counting nesting.
+            startedCount++;
+            newCondition = ConditionState::kTrue;
+        } else {
+            // This is a stop event.
+            if (startedCount > 0) {
+                if (mCountNesting) {
+                    startedCount--;
+                    if (startedCount == 0) {
+                        newCondition = ConditionState::kFalse;
+                    }
+                } else {
+                    // not counting nesting, so ignore the number of starts, stop now.
+                    startedCount = 0;
+                    newCondition = ConditionState::kFalse;
+                }
+                // if everything has stopped for this output key, condition true -> false;
+                if (startedCount == 0) {
+                    changed = true;
+                }
+            }
+
+            // if default condition is false, it means we don't need to keep the false values.
+            if (mInitialValue == ConditionState::kFalse && startedCount == 0) {
+                mSlicedConditionState.erase(outputIt);
+                VLOG("erase key %s", outputKey.c_str());
+            }
+        }
+    }
+
+    // dump all dimensions for debugging
+    if (DEBUG) {
+        print(mSlicedConditionState, mName);
+    }
+
+    conditionChangedCache[mIndex] = changed;
+    conditionCache[mIndex] = newCondition;
+
+    VLOG("SimpleCondition %s nonSlicedChange? %d", mName.c_str(),
+         conditionChangedCache[mIndex] == true);
+}
+
+void SimpleConditionTracker::evaluateCondition(const LogEvent& event,
                                                const vector<MatchingState>& eventMatcherValues,
                                                const vector<sp<ConditionTracker>>& mAllConditions,
                                                vector<ConditionState>& conditionCache,
-                                               vector<bool>& nonSlicedConditionChanged,
-                                               std::vector<bool>& slicedConditionChanged) {
+                                               vector<bool>& conditionChangedCache) {
     if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
         // it has been evaluated.
         VLOG("Yes, already evaluated, %s %d", mName.c_str(), mNonSlicedConditionState);
-        return false;
+        return;
     }
 
-    // Ignore nesting, because we know we cannot trust ourselves on tracking nesting conditions.
+    if (mStopAllLogMatcherIndex >= 0 &&
+        eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
+        handleStopAll(conditionCache, conditionChangedCache);
+        return;
+    }
 
-    ConditionState newCondition = mNonSlicedConditionState;
-    bool matched = false;
+    int matchedState = -1;
     // Note: The order to evaluate the following start, stop, stop_all matters.
     // The priority of overwrite is stop_all > stop > start.
     if (mStartLogMatcherIndex >= 0 &&
         eventMatcherValues[mStartLogMatcherIndex] == MatchingState::kMatched) {
-        matched = true;
-        newCondition = ConditionState::kTrue;
+        matchedState = 1;
     }
 
     if (mStopLogMatcherIndex >= 0 &&
         eventMatcherValues[mStopLogMatcherIndex] == MatchingState::kMatched) {
-        matched = true;
-        newCondition = ConditionState::kFalse;
+        matchedState = 0;
     }
 
-    bool stopAll = false;
-    if (mStopAllLogMatcherIndex >= 0 &&
-        eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
-        matched = true;
-        newCondition = ConditionState::kFalse;
-        stopAll = true;
-    }
-
-    if (matched == false) {
-        slicedConditionChanged[mIndex] = false;
-        nonSlicedConditionChanged[mIndex] = false;
+    if (matchedState < 0) {
+        conditionChangedCache[mIndex] = false;
         conditionCache[mIndex] = mNonSlicedConditionState;
-        return false;
+        return;
     }
 
-    bool nonSlicedChanged = mNonSlicedConditionState != newCondition;
-
-    bool slicedChanged = false;
-
-    if (stopAll) {
-        // TODO: handle stop all; all dimension should be cleared.
-    }
-
-
-    if (mDimension.size() > 0) {
-        HashableDimensionKey hashableKey = getHashableKey(getDimensionKey(event, mDimension));
-        if (mSlicedConditionState.find(hashableKey) == mSlicedConditionState.end() ||
-            mSlicedConditionState[hashableKey] != newCondition) {
-            slicedChanged = true;
-            mSlicedConditionState[hashableKey] = newCondition;
-        }
-        VLOG("key: %s %d", hashableKey.c_str(), newCondition);
-        // dump all dimensions for debugging
-        if (DEBUG) {
-            print(mSlicedConditionState, mName);
-        }
-    }
-
-    // even if this SimpleCondition is not sliced, it may be part of a sliced CombinationCondition
-    // if the nonSliced condition changed, it may affect the sliced condition in the parent node.
-    // so mark the slicedConditionChanged to be true.
-    // For example: APP_IN_BACKGROUND_OR_SCREEN_OFF
-    //     APP_IN_BACKGROUND is sliced [App_A->True, App_B->False].
-    //     SCREEN_OFF is not sliced, and it changes from False -> True;
-    //     We need to populate this change to parent condition. Because for App_B,
-    //     the APP_IN_BACKGROUND_OR_SCREEN_OFF condition would change from False->True.
-    slicedConditionChanged[mIndex] = mSliced ? slicedChanged : nonSlicedChanged;
-    nonSlicedConditionChanged[mIndex] = nonSlicedChanged;
-
-    VLOG("SimpleCondition %s nonSlicedChange? %d  SlicedChanged? %d", mName.c_str(),
-         nonSlicedConditionChanged[mIndex] == true, slicedConditionChanged[mIndex] == true);
-    mNonSlicedConditionState = newCondition;
-    conditionCache[mIndex] = mNonSlicedConditionState;
-
-    return nonSlicedConditionChanged[mIndex];
+    // outputKey is the output key values. e.g, uid:1234
+    const HashableDimensionKey outputKey = getHashableKey(getDimensionKey(event, mOutputDimension));
+    handleConditionEvent(outputKey, matchedState == 1, conditionCache, conditionChangedCache);
 }
 
 void SimpleConditionTracker::isConditionMet(
         const map<string, HashableDimensionKey>& conditionParameters,
         const vector<sp<ConditionTracker>>& allConditions, vector<ConditionState>& conditionCache) {
     const auto pair = conditionParameters.find(mName);
-    if (pair == conditionParameters.end()) {
-        // the query does not need my sliced condition. just return the non sliced condition.
-        conditionCache[mIndex] = mNonSlicedConditionState;
-        VLOG("Condition %s return %d", mName.c_str(), mNonSlicedConditionState);
+    HashableDimensionKey key =
+            (pair == conditionParameters.end()) ? DEFAULT_DIMENSION_KEY : pair->second;
+
+    if (pair == conditionParameters.end() && mOutputDimension.size() > 0) {
+        ALOGE("Condition %s output has dimension, but it's not specified in the query!",
+              mName.c_str());
+        conditionCache[mIndex] = mInitialValue;
         return;
     }
 
-    const HashableDimensionKey& key = pair->second;
     VLOG("simpleCondition %s query key: %s", mName.c_str(), key.c_str());
 
-    if (mSlicedConditionState.find(key) == mSlicedConditionState.end()) {
-        // never seen this key before. the condition is unknown to us.
-        conditionCache[mIndex] = ConditionState::kUnknown;
+    auto startedCountIt = mSlicedConditionState.find(key);
+    if (startedCountIt == mSlicedConditionState.end()) {
+        conditionCache[mIndex] = mInitialValue;
     } else {
-        conditionCache[mIndex] = mSlicedConditionState[key];
+        conditionCache[mIndex] =
+                startedCountIt->second > 0 ? ConditionState::kTrue : ConditionState::kFalse;
     }
 
     VLOG("Condition %s return %d", mName.c_str(), conditionCache[mIndex]);
-
-    if (DEBUG) {
-        print(mSlicedConditionState, mName);
-    }
 }
 
 }  // namespace statsd
diff --git a/bin/src/condition/SimpleConditionTracker.h b/bin/src/condition/SimpleConditionTracker.h
index b72157b..2eda0b1 100644
--- a/bin/src/condition/SimpleConditionTracker.h
+++ b/bin/src/condition/SimpleConditionTracker.h
@@ -17,6 +17,7 @@
 #ifndef SIMPLE_CONDITION_TRACKER_H
 #define SIMPLE_CONDITION_TRACKER_H
 
+#include <gtest/gtest_prod.h>
 #include "ConditionTracker.h"
 #include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
 #include "stats_util.h"
@@ -38,12 +39,11 @@
               const std::unordered_map<std::string, int>& conditionNameIndexMap,
               std::vector<bool>& stack) override;
 
-    bool evaluateCondition(const LogEvent& event,
+    void evaluateCondition(const LogEvent& event,
                            const std::vector<MatchingState>& eventMatcherValues,
                            const std::vector<sp<ConditionTracker>>& mAllConditions,
                            std::vector<ConditionState>& conditionCache,
-                           std::vector<bool>& changedCache,
-                           std::vector<bool>& slicedChangedCache) override;
+                           std::vector<bool>& changedCache) override;
 
     void isConditionMet(const std::map<std::string, HashableDimensionKey>& conditionParameters,
                         const std::vector<sp<ConditionTracker>>& allConditions,
@@ -62,15 +62,22 @@
     // The index of the LogEventMatcher which defines the stop all.
     int mStopAllLogMatcherIndex;
 
-    // The dimension defines at the atom level, how start and stop should match.
-    // e.g., APP_IN_FOREGROUND, the dimension should be the uid field. Each "start" and
-    // "stop" tells you the state change of a particular app. Without this dimension, this
-    // condition does not make sense.
-    std::vector<KeyMatcher> mDimension;
+    ConditionState mInitialValue;
 
-    // Keep the map from the internal HashableDimensionKey to std::vector<KeyValuePair>
-    // that StatsLogReport wants.
-    std::unordered_map<HashableDimensionKey, ConditionState> mSlicedConditionState;
+    std::vector<KeyMatcher> mOutputDimension;
+
+    std::map<HashableDimensionKey, int> mSlicedConditionState;
+
+    void handleStopAll(std::vector<ConditionState>& conditionCache,
+                       std::vector<bool>& changedCache);
+
+    void handleConditionEvent(const HashableDimensionKey& outputKey, bool matchStart,
+                              std::vector<ConditionState>& conditionCache,
+                              std::vector<bool>& changedCache);
+
+    FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition);
+    FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim);
+    FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll);
 };
 
 }  // namespace statsd
diff --git a/bin/src/config/ConfigManager.cpp b/bin/src/config/ConfigManager.cpp
index 94566ff..d86ab57 100644
--- a/bin/src/config/ConfigManager.cpp
+++ b/bin/src/config/ConfigManager.cpp
@@ -200,7 +200,7 @@
     durationMetric->set_type(DurationMetric_AggregationType_DURATION_SUM);
     keyMatcher = durationMetric->add_dimension();
     keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
-    durationMetric->set_what("WL_STATE_PER_APP_PER_NAME");
+    durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
     durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
     link = durationMetric->add_links();
     link->set_condition("APP_IS_BACKGROUND");
@@ -214,7 +214,7 @@
     durationMetric->set_type(DurationMetric_AggregationType_DURATION_MAX_SPARSE);
     keyMatcher = durationMetric->add_dimension();
     keyMatcher->set_key(WAKE_LOCK_UID_KEY_ID);
-    durationMetric->set_what("WL_STATE_PER_APP_PER_NAME");
+    durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
     durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
     link = durationMetric->add_links();
     link->set_condition("APP_IS_BACKGROUND");
@@ -226,7 +226,7 @@
     durationMetric->set_metric_id(7);
     durationMetric->mutable_bucket()->set_bucket_size_millis(30 * 1000L);
     durationMetric->set_type(DurationMetric_AggregationType_DURATION_MAX_SPARSE);
-    durationMetric->set_what("WL_STATE_PER_APP_PER_NAME");
+    durationMetric->set_what("WL_HELD_PER_APP_PER_NAME");
     durationMetric->set_predicate("APP_IS_BACKGROUND_AND_SCREEN_ON");
     link = durationMetric->add_links();
     link->set_condition("APP_IS_BACKGROUND");
@@ -334,12 +334,14 @@
     SimpleCondition* simpleCondition = condition->mutable_simple_condition();
     simpleCondition->set_start("SCREEN_TURNED_ON");
     simpleCondition->set_stop("SCREEN_TURNED_OFF");
+    simpleCondition->set_count_nesting(false);
 
     condition = config.add_condition();
     condition->set_name("SCREEN_IS_OFF");
     simpleCondition = condition->mutable_simple_condition();
     simpleCondition->set_start("SCREEN_TURNED_OFF");
     simpleCondition->set_stop("SCREEN_TURNED_ON");
+    simpleCondition->set_count_nesting(false);
 
     condition = config.add_condition();
     condition->set_name("APP_IS_BACKGROUND");
@@ -348,6 +350,7 @@
     simpleCondition->set_stop("APP_GOES_FOREGROUND");
     KeyMatcher* condition_dimension1 = simpleCondition->add_dimension();
     condition_dimension1->set_key(APP_USAGE_UID_KEY_ID);
+    simpleCondition->set_count_nesting(false);
 
     condition = config.add_condition();
     condition->set_name("APP_IS_BACKGROUND_AND_SCREEN_ON");
@@ -357,7 +360,7 @@
     combination_condition->add_condition("SCREEN_IS_ON");
 
     condition = config.add_condition();
-    condition->set_name("WL_STATE_PER_APP_PER_NAME");
+    condition->set_name("WL_HELD_PER_APP_PER_NAME");
     simpleCondition = condition->mutable_simple_condition();
     simpleCondition->set_start("APP_GET_WL");
     simpleCondition->set_stop("APP_RELEASE_WL");
@@ -365,6 +368,17 @@
     condition_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
     condition_dimension = simpleCondition->add_dimension();
     condition_dimension->set_key(WAKE_LOCK_NAME_KEY);
+    simpleCondition->set_count_nesting(true);
+
+    condition = config.add_condition();
+    condition->set_name("WL_HELD_PER_APP");
+    simpleCondition = condition->mutable_simple_condition();
+    simpleCondition->set_start("APP_GET_WL");
+    simpleCondition->set_stop("APP_RELEASE_WL");
+    simpleCondition->set_initial_value(SimpleCondition_InitialValue_FALSE);
+    condition_dimension = simpleCondition->add_dimension();
+    condition_dimension->set_key(WAKE_LOCK_UID_KEY_ID);
+    simpleCondition->set_count_nesting(true);
 
     return config;
 }
diff --git a/bin/src/metrics/MetricsManager.cpp b/bin/src/metrics/MetricsManager.cpp
index 80b325f..fca4771 100644
--- a/bin/src/metrics/MetricsManager.cpp
+++ b/bin/src/metrics/MetricsManager.cpp
@@ -104,18 +104,17 @@
                                           ConditionState::kNotEvaluated);
     // A bitmap to track if a condition has changed value.
     vector<bool> changedCache(mAllConditionTrackers.size(), false);
-    vector<bool> slicedChangedCache(mAllConditionTrackers.size(), false);
     for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
         if (conditionToBeEvaluated[i] == false) {
             continue;
         }
         sp<ConditionTracker>& condition = mAllConditionTrackers[i];
         condition->evaluateCondition(event, matcherCache, mAllConditionTrackers, conditionCache,
-                                     changedCache, slicedChangedCache);
+                                     changedCache);
     }
 
     for (size_t i = 0; i < mAllConditionTrackers.size(); i++) {
-        if (changedCache[i] == false && slicedChangedCache[i] == false) {
+        if (changedCache[i] == false) {
             continue;
         }
         auto pair = mConditionToMetricMap.find(i);
@@ -124,14 +123,13 @@
             for (auto metricIndex : metricList) {
                 // metric cares about non sliced condition, and it's changed.
                 // Push the new condition to it directly.
-                if (!mAllMetricProducers[metricIndex]->isConditionSliced() && changedCache[i]) {
+                if (!mAllMetricProducers[metricIndex]->isConditionSliced()) {
                     mAllMetricProducers[metricIndex]->onConditionChanged(conditionCache[i],
                                                                          eventTime);
                     // metric cares about sliced conditions, and it may have changed. Send
                     // notification, and the metric can query the sliced conditions that are
                     // interesting to it.
-                } else if (mAllMetricProducers[metricIndex]->isConditionSliced() &&
-                           slicedChangedCache[i]) {
+                } else if (mAllMetricProducers[metricIndex]->isConditionSliced()) {
                     mAllMetricProducers[metricIndex]->onSlicedConditionMayChange(eventTime);
                 }
             }
diff --git a/bin/src/statsd_config.proto b/bin/src/statsd_config.proto
index 3b8eeaf..a07f76a 100644
--- a/bin/src/statsd_config.proto
+++ b/bin/src/statsd_config.proto
@@ -83,7 +83,13 @@
 
   optional string stop_all = 4;
 
-  repeated KeyMatcher dimension = 5;
+  enum InitialValue {
+    UNKNOWN = 0;
+    FALSE = 1;
+  }
+  optional InitialValue initial_value = 5 [default = UNKNOWN];
+
+  repeated KeyMatcher dimension = 6;
 }
 
 message Condition {
diff --git a/bin/tests/ConditionTracker_test.cpp b/bin/tests/condition/CombinationConditionTracker_test.cpp
similarity index 98%
rename from bin/tests/ConditionTracker_test.cpp
rename to bin/tests/condition/CombinationConditionTracker_test.cpp
index 2935ac7..23d6926 100644
--- a/bin/tests/ConditionTracker_test.cpp
+++ b/bin/tests/condition/CombinationConditionTracker_test.cpp
@@ -23,7 +23,6 @@
 using namespace android::os::statsd;
 using std::vector;
 
-
 #ifdef __ANDROID__
 TEST(ConditionTrackerTest, TestUnknownCondition) {
     LogicalOperation operation = LogicalOperation::AND;
@@ -39,7 +38,7 @@
     conditionResults.push_back(ConditionState::kTrue);
 
     EXPECT_EQ(evaluateCombinationCondition(children, operation, conditionResults),
-            ConditionState::kUnknown);
+              ConditionState::kUnknown);
 }
 TEST(ConditionTrackerTest, TestAndCondition) {
     // Set up the matcher
diff --git a/bin/tests/condition/SimpleConditionTracker_test.cpp b/bin/tests/condition/SimpleConditionTracker_test.cpp
new file mode 100644
index 0000000..05aad29
--- /dev/null
+++ b/bin/tests/condition/SimpleConditionTracker_test.cpp
@@ -0,0 +1,469 @@
+// Copyright (C) 2017 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#include "src/condition/SimpleConditionTracker.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <vector>
+
+using std::map;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+
+namespace android {
+namespace os {
+namespace statsd {
+
+SimpleCondition getWakeLockHeldCondition(bool countNesting, bool defaultFalse,
+                                         bool outputSlicedUid) {
+    SimpleCondition simpleCondition;
+    simpleCondition.set_start("WAKE_LOCK_ACQUIRE");
+    simpleCondition.set_stop("WAKE_LOCK_RELEASE");
+    simpleCondition.set_stop_all("RELEASE_ALL");
+    if (outputSlicedUid) {
+        KeyMatcher* keyMatcher = simpleCondition.add_dimension();
+        keyMatcher->set_key(1);
+    }
+
+    simpleCondition.set_count_nesting(countNesting);
+    simpleCondition.set_initial_value(defaultFalse ? SimpleCondition_InitialValue_FALSE
+                                                       : SimpleCondition_InitialValue_UNKNOWN);
+    return simpleCondition;
+}
+
+void makeWakeLockEvent(LogEvent* event, int uid, const string& wl, int acquire) {
+    auto list = event->GetAndroidLogEventList();
+    *list << uid;  // uid
+    *list << wl;
+    *list << acquire;
+    event->init();
+}
+
+map<string, HashableDimensionKey> getWakeLockQueryKey(int key, int uid,
+                                                      const string& conditionName) {
+    // test query
+    KeyValuePair kv1;
+    kv1.set_key(key);
+    kv1.set_value_int(uid);
+    vector<KeyValuePair> kv_list;
+    kv_list.push_back(kv1);
+    map<string, HashableDimensionKey> queryKey;
+    queryKey[conditionName] = getHashableKey(kv_list);
+    return queryKey;
+}
+
+TEST(SimpleConditionTrackerTest, TestNonSlicedCondition) {
+    SimpleCondition simpleCondition;
+    simpleCondition.set_start("SCREEN_TURNED_ON");
+    simpleCondition.set_stop("SCREEN_TURNED_OFF");
+    simpleCondition.set_count_nesting(false);
+
+    unordered_map<string, int> trackerNameIndexMap;
+    trackerNameIndexMap["SCREEN_TURNED_ON"] = 0;
+    trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1;
+
+    SimpleConditionTracker conditionTracker("SCREEN_IS_ON", 0 /*tracker index*/, simpleCondition,
+                                            trackerNameIndexMap);
+
+    LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+
+    vector<MatchingState> matcherState;
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+
+    vector<sp<ConditionTracker>> allConditions;
+    vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+    vector<bool> changedCache(1, false);
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    // not matched start or stop. condition doesn't change
+    EXPECT_EQ(ConditionState::kUnknown, conditionCache[0]);
+    EXPECT_FALSE(changedCache[0]);
+
+    // prepare a case for match start.
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    // now condition should change to true.
+    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+    EXPECT_TRUE(changedCache[0]);
+
+    // the case for match stop.
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+
+    // condition changes to false.
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+    EXPECT_TRUE(changedCache[0]);
+
+    // match stop again.
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    // condition should still be false. not changed.
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+    EXPECT_FALSE(changedCache[0]);
+}
+
+TEST(SimpleConditionTrackerTest, TestNonSlicedConditionNestCounting) {
+    SimpleCondition simpleCondition;
+    simpleCondition.set_start("SCREEN_TURNED_ON");
+    simpleCondition.set_stop("SCREEN_TURNED_OFF");
+    simpleCondition.set_count_nesting(true);
+
+    unordered_map<string, int> trackerNameIndexMap;
+    trackerNameIndexMap["SCREEN_TURNED_ON"] = 0;
+    trackerNameIndexMap["SCREEN_TURNED_OFF"] = 1;
+
+    SimpleConditionTracker conditionTracker("SCREEN_IS_ON", 0 /*condition tracker index*/,
+                                            simpleCondition, trackerNameIndexMap);
+
+    LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+
+    // one matched start
+    vector<MatchingState> matcherState;
+    matcherState.push_back(MatchingState::kMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    vector<sp<ConditionTracker>> allConditions;
+    vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+    vector<bool> changedCache(1, false);
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+
+    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+    EXPECT_TRUE(changedCache[0]);
+
+    // prepare for another matched start.
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+
+    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+    EXPECT_FALSE(changedCache[0]);
+
+    // ONE MATCHED STOP
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    // result should still be true
+    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+    EXPECT_FALSE(changedCache[0]);
+
+    // ANOTHER MATCHED STOP
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    // result should still be true
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+    EXPECT_TRUE(changedCache[0]);
+}
+
+TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
+    SimpleCondition simpleCondition = getWakeLockHeldCondition(
+            true /*nesting*/, true /*default to false*/, true /*output slice by uid*/);
+    string conditionName = "WL_HELD_BY_UID2";
+
+    unordered_map<string, int> trackerNameIndexMap;
+    trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
+    trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
+    trackerNameIndexMap["RELEASE_ALL"] = 2;
+
+    SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
+                                            simpleCondition, trackerNameIndexMap);
+    int uid = 111;
+
+    LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+    makeWakeLockEvent(&event, uid, "wl1", 1);
+
+    // one matched start
+    vector<MatchingState> matcherState;
+    matcherState.push_back(MatchingState::kMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    vector<sp<ConditionTracker>> allConditions;
+    vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+    vector<bool> changedCache(1, false);
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+
+    EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+    EXPECT_TRUE(changedCache[0]);
+
+    // Now test query
+    const auto queryKey = getWakeLockQueryKey(1, uid, conditionName);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+
+    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+    // another wake lock acquired by this uid
+    LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
+    makeWakeLockEvent(&event2, uid, "wl2", 1);
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+    conditionTracker.evaluateCondition(event2, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    EXPECT_FALSE(changedCache[0]);
+
+    // wake lock 1 release
+    LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
+    makeWakeLockEvent(&event3, uid, "wl1", 0);  // now release it.
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+    conditionTracker.evaluateCondition(event3, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    // nothing changes, because wake lock 2 is still held for this uid
+    EXPECT_FALSE(changedCache[0]);
+
+    LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
+    makeWakeLockEvent(&event4, uid, "wl2", 0);  // now release it.
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+    conditionTracker.evaluateCondition(event4, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+    EXPECT_TRUE(changedCache[0]);
+
+    // query again
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+}
+
+TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
+    SimpleCondition simpleCondition = getWakeLockHeldCondition(
+            true /*nesting*/, true /*default to false*/, false /*slice output by uid*/);
+    string conditionName = "WL_HELD";
+
+    unordered_map<string, int> trackerNameIndexMap;
+    trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
+    trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
+    trackerNameIndexMap["RELEASE_ALL"] = 2;
+
+    SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
+                                            simpleCondition, trackerNameIndexMap);
+    int uid1 = 111;
+    string uid1_wl1 = "wl1_1";
+    int uid2 = 222;
+    string uid2_wl1 = "wl2_1";
+
+    LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+    makeWakeLockEvent(&event, uid1, uid1_wl1, 1);
+
+    // one matched start for uid1
+    vector<MatchingState> matcherState;
+    matcherState.push_back(MatchingState::kMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    vector<sp<ConditionTracker>> allConditions;
+    vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+    vector<bool> changedCache(1, false);
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+
+    EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+    EXPECT_TRUE(changedCache[0]);
+
+    // Now test query
+    map<string, HashableDimensionKey> queryKey;
+    conditionCache[0] = ConditionState::kNotEvaluated;
+
+    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+    // another wake lock acquired by this uid
+    LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
+    makeWakeLockEvent(&event2, uid2, uid2_wl1, 1);
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+    conditionTracker.evaluateCondition(event2, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    EXPECT_FALSE(changedCache[0]);
+
+    // uid1 wake lock 1 release
+    LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
+    makeWakeLockEvent(&event3, uid1, uid1_wl1, 0);  // now release it.
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+    conditionTracker.evaluateCondition(event3, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    // nothing changes, because uid2 is still holding wl.
+    EXPECT_FALSE(changedCache[0]);
+
+    LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
+    makeWakeLockEvent(&event4, uid2, uid2_wl1, 0);  // now release it.
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+    conditionTracker.evaluateCondition(event4, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+    EXPECT_TRUE(changedCache[0]);
+
+    // query again
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+}
+
+TEST(SimpleConditionTrackerTest, TestStopAll) {
+    SimpleCondition simpleCondition = getWakeLockHeldCondition(
+            true /*nesting*/, true /*default to false*/, true /*output slice by uid*/);
+    string conditionName = "WL_HELD_BY_UID3";
+
+    unordered_map<string, int> trackerNameIndexMap;
+    trackerNameIndexMap["WAKE_LOCK_ACQUIRE"] = 0;
+    trackerNameIndexMap["WAKE_LOCK_RELEASE"] = 1;
+    trackerNameIndexMap["RELEASE_ALL"] = 2;
+
+    SimpleConditionTracker conditionTracker(conditionName, 0 /*condition tracker index*/,
+                                            simpleCondition, trackerNameIndexMap);
+    int uid1 = 111;
+    int uid2 = 222;
+
+    LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
+    makeWakeLockEvent(&event, uid1, "wl1", 1);
+
+    // one matched start
+    vector<MatchingState> matcherState;
+    matcherState.push_back(MatchingState::kMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    vector<sp<ConditionTracker>> allConditions;
+    vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+    vector<bool> changedCache(1, false);
+
+    conditionTracker.evaluateCondition(event, matcherState, allConditions, conditionCache,
+                                       changedCache);
+
+    EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+    EXPECT_TRUE(changedCache[0]);
+
+    // Now test query
+    const auto queryKey = getWakeLockQueryKey(1, uid1, conditionName);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+
+    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+    // another wake lock acquired by uid2
+    LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
+    makeWakeLockEvent(&event2, uid2, "wl2", 1);
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+    conditionTracker.evaluateCondition(event2, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    EXPECT_EQ(2UL, conditionTracker.mSlicedConditionState.size());
+    EXPECT_TRUE(changedCache[0]);
+
+    // TEST QUERY
+    const auto queryKey2 = getWakeLockQueryKey(1, uid2, conditionName);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+
+    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+
+    // stop all event
+    LogEvent event3(2 /*tagId*/, 0 /*timestamp*/);
+    matcherState.clear();
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kNotMatched);
+    matcherState.push_back(MatchingState::kMatched);
+
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    changedCache[0] = false;
+    conditionTracker.evaluateCondition(event3, matcherState, allConditions, conditionCache,
+                                       changedCache);
+    EXPECT_TRUE(changedCache[0]);
+    EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+
+    // TEST QUERY
+    const auto queryKey3 = getWakeLockQueryKey(1, uid1, conditionName);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+
+    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+
+    // TEST QUERY
+    const auto queryKey4 = getWakeLockQueryKey(1, uid2, conditionName);
+    conditionCache[0] = ConditionState::kNotEvaluated;
+
+    conditionTracker.isConditionMet(queryKey, allConditions, conditionCache);
+    EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif