Add StateTracker.

StateTracker is a special condition tracker that's based on a state atom.
State atoms are annotated in atoms.proto.

The rules for StateTracker:
 1. must not have "stop". must have "dimension"
 2. must be based on a state atom.
 3. it must have the all primary fields and the exclusive state field in its dimension.

 For example UidProcessStateTracker, will have output dimension {uid, state}.

Test: unit tests added.
Change-Id: I6b77e58e9fabe61f7326daf929577d8b2cfbf27b
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index 7f76ab1..5873942 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -30,6 +30,7 @@
     src/condition/condition_util.cpp \
     src/condition/SimpleConditionTracker.cpp \
     src/condition/ConditionWizard.cpp \
+    src/condition/StateTracker.cpp \
     src/config/ConfigKey.cpp \
     src/config/ConfigListener.cpp \
     src/config/ConfigManager.cpp \
@@ -189,6 +190,7 @@
     tests/FieldValue_test.cpp \
     tests/condition/CombinationConditionTracker_test.cpp \
     tests/condition/SimpleConditionTracker_test.cpp \
+    tests/condition/StateTracker_test.cpp \
     tests/metrics/OringDurationTracker_test.cpp \
     tests/metrics/MaxDurationTracker_test.cpp \
     tests/metrics/CountMetricProducer_test.cpp \
diff --git a/cmds/statsd/src/FieldValue.h b/cmds/statsd/src/FieldValue.h
index b0e2c43..621d0be9 100644
--- a/cmds/statsd/src/FieldValue.h
+++ b/cmds/statsd/src/FieldValue.h
@@ -41,6 +41,7 @@
 inline int32_t getSimpleField(size_t field) {
     return ((int32_t)field << 8 * 2);
 }
+
 /**
  * Field is a wrapper class for 2 integers that represents the field of a log element in its Atom
  * proto.
@@ -201,9 +202,9 @@
  *     }
  *
  * We translate the FieldMatcher into a Field, and mask
- * First: [Matcher Field] 0x02010101  [Mask]0xffff7fff
- * Last:  [Matcher Field] 0x02018001  [Mask]0xffff80ff
- * Any:   [Matcher Field] 0x02010001  [Mask]0xffff00ff
+ * First: [Matcher Field] 0x02010101  [Mask]0xff7f7f7f
+ * Last:  [Matcher Field] 0x02018001  [Mask]0xff7f807f
+ * Any:   [Matcher Field] 0x02010001  [Mask]0xff7f007f
  *
  * [To match a log Field with a Matcher] we apply the bit mask to the log Field and check if
  * the result is equal to the Matcher Field. That's a bit wise AND operation + check if 2 ints are
@@ -235,9 +236,17 @@
 
     inline bool operator!=(const Matcher& that) const {
         return mMatcher != that.getMatcher() || mMask != that.getMask();
-    };
+    }
+
+    inline bool operator==(const Matcher& that) const {
+        return mMatcher == that.mMatcher && mMask == that.mMask;
+    }
 };
 
+inline Matcher getSimpleMatcher(int32_t tag, size_t field) {
+    return Matcher(Field(tag, getSimpleField(field)), 0xff7f0000);
+}
+
 /**
  * A wrapper for a union type to contain multiple types of values.
  *
diff --git a/cmds/statsd/src/condition/CombinationConditionTracker.h b/cmds/statsd/src/condition/CombinationConditionTracker.h
index ba185f6..7b8dc6b 100644
--- a/cmds/statsd/src/condition/CombinationConditionTracker.h
+++ b/cmds/statsd/src/condition/CombinationConditionTracker.h
@@ -52,6 +52,29 @@
             const vector<Matcher>& dimensionFields,
             std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
 
+    // Only one child predicate can have dimension.
+    const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
+            const std::vector<sp<ConditionTracker>>& allConditions) const override {
+        for (const auto& child : mChildren) {
+            auto result = allConditions[child]->getChangedToTrueDimensions(allConditions);
+            if (result != nullptr) {
+                return result;
+            }
+        }
+        return nullptr;
+    }
+    // Only one child predicate can have dimension.
+    const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
+            const std::vector<sp<ConditionTracker>>& allConditions) const override {
+        for (const auto& child : mChildren) {
+            auto result = allConditions[child]->getChangedToFalseDimensions(allConditions);
+            if (result != nullptr) {
+                return result;
+            }
+        }
+        return nullptr;
+    }
+
 private:
     LogicalOperation mLogicalOperation;
 
diff --git a/cmds/statsd/src/condition/ConditionTracker.h b/cmds/statsd/src/condition/ConditionTracker.h
index 2612a9a..856a3a0 100644
--- a/cmds/statsd/src/condition/ConditionTracker.h
+++ b/cmds/statsd/src/condition/ConditionTracker.h
@@ -111,6 +111,11 @@
         return mSliced;
     }
 
+    virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
+            const std::vector<sp<ConditionTracker>>& allConditions) const = 0;
+    virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
+            const std::vector<sp<ConditionTracker>>& allConditions) const = 0;
+
 protected:
     const int64_t mConditionId;
 
diff --git a/cmds/statsd/src/condition/ConditionWizard.cpp b/cmds/statsd/src/condition/ConditionWizard.cpp
index c8722c3..952b0cc 100644
--- a/cmds/statsd/src/condition/ConditionWizard.cpp
+++ b/cmds/statsd/src/condition/ConditionWizard.cpp
@@ -41,6 +41,16 @@
                                  *dimensionsKeySet);
 }
 
+const set<HashableDimensionKey>* ConditionWizard::getChangedToTrueDimensions(
+        const int index) const {
+    return mAllConditions[index]->getChangedToTrueDimensions(mAllConditions);
+}
+
+const set<HashableDimensionKey>* ConditionWizard::getChangedToFalseDimensions(
+        const int index) const {
+    return mAllConditions[index]->getChangedToFalseDimensions(mAllConditions);
+}
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/condition/ConditionWizard.h b/cmds/statsd/src/condition/ConditionWizard.h
index 4831d56..fcfdc2a 100644
--- a/cmds/statsd/src/condition/ConditionWizard.h
+++ b/cmds/statsd/src/condition/ConditionWizard.h
@@ -47,6 +47,10 @@
             const int index, const vector<Matcher>& dimensionFields,
             std::unordered_set<HashableDimensionKey>* dimensionsKeySet) const;
 
+    virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(const int index) const;
+    virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
+            const int index) const;
+
 private:
     std::vector<sp<ConditionTracker>> mAllConditions;
 };
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.cpp b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
index 624119f3..9e27a8b 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.cpp
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.cpp
@@ -20,8 +20,6 @@
 #include "SimpleConditionTracker.h"
 #include "guardrail/StatsdStats.h"
 
-#include <log/logprint.h>
-
 namespace android {
 namespace os {
 namespace statsd {
@@ -108,11 +106,20 @@
     return mInitialized;
 }
 
-void print(const map<HashableDimensionKey, int>& conditions, const int64_t& id) {
-    VLOG("%lld DUMP:", (long long)id);
-    for (const auto& pair : conditions) {
+void SimpleConditionTracker::dumpState() {
+    VLOG("%lld DUMP:", (long long)mConditionId);
+    for (const auto& pair : mSlicedConditionState) {
         VLOG("\t%s : %d", pair.first.c_str(), pair.second);
     }
+
+    VLOG("Changed to true keys: \n");
+    for (const auto& key : mLastChangedToTrueDimensions) {
+        VLOG("%s", key.toString().c_str());
+    }
+    VLOG("Changed to false keys: \n");
+    for (const auto& key : mLastChangedToFalseDimensions) {
+        VLOG("%s", key.toString().c_str());
+    }
 }
 
 void SimpleConditionTracker::handleStopAll(std::vector<ConditionState>& conditionCache,
@@ -123,6 +130,12 @@
             (mInitialValue == ConditionState::kFalse && mSlicedConditionState.empty()) ? false
                                                                                            : true;
 
+    for (const auto& cond : mSlicedConditionState) {
+        if (cond.second > 0) {
+            mLastChangedToFalseDimensions.insert(cond.first);
+        }
+    }
+
     // After StopAll, we know everything has stopped. From now on, default condition is false.
     mInitialValue = ConditionState::kFalse;
     mSlicedConditionState.clear();
@@ -166,10 +179,12 @@
         if (matchStart && mInitialValue != ConditionState::kTrue) {
             mSlicedConditionState.insert(std::make_pair(outputKey, 1));
             changed = true;
+            mLastChangedToTrueDimensions.insert(outputKey);
         } 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.insert(std::make_pair(outputKey, 0));
+            mLastChangedToFalseDimensions.insert(outputKey);
             changed = true;
         }
     } else {
@@ -179,6 +194,7 @@
         newCondition = startedCount > 0 ? ConditionState::kTrue : ConditionState::kFalse;
         if (matchStart) {
             if (startedCount == 0) {
+                mLastChangedToTrueDimensions.insert(outputKey);
                 // This condition for this output key will change from false -> true
                 changed = true;
             }
@@ -202,6 +218,7 @@
                 }
                 // if everything has stopped for this output key, condition true -> false;
                 if (startedCount == 0) {
+                    mLastChangedToFalseDimensions.insert(outputKey);
                     changed = true;
                 }
             }
@@ -216,7 +233,7 @@
 
     // dump all dimensions for debugging
     if (DEBUG) {
-        print(mSlicedConditionState, mConditionId);
+        dumpState();
     }
 
     (*conditionChangedCache) = changed;
@@ -237,6 +254,8 @@
             (long long)mConditionId, conditionCache[mIndex]);
         return;
     }
+    mLastChangedToTrueDimensions.clear();
+    mLastChangedToFalseDimensions.clear();
 
     if (mStopAllLogMatcherIndex >= 0 && mStopAllLogMatcherIndex < int(eventMatcherValues.size()) &&
         eventMatcherValues[mStopAllLogMatcherIndex] == MatchingState::kMatched) {
@@ -297,14 +316,14 @@
         // A high level assumption is that a predicate is either sliced or unsliced. We will never
         // have both sliced and unsliced version of a predicate.
         for (const HashableDimensionKey& outputValue : outputValues) {
-            // For sliced conditions, the value in the cache is not used. We don't need to update
-            // the overall condition state.
-            ConditionState tempState = ConditionState::kUnknown;
+            ConditionState tempState;
             bool tempChanged = false;
             handleConditionEvent(outputValue, matchedState == 1, &tempState, &tempChanged);
             if (tempChanged) {
                 overallChanged = true;
             }
+            // ConditionState's | operator is overridden
+            overallState = overallState | tempState;
         }
     }
     conditionCache[mIndex] = overallState;
diff --git a/cmds/statsd/src/condition/SimpleConditionTracker.h b/cmds/statsd/src/condition/SimpleConditionTracker.h
index c565129..e4b72b8 100644
--- a/cmds/statsd/src/condition/SimpleConditionTracker.h
+++ b/cmds/statsd/src/condition/SimpleConditionTracker.h
@@ -57,6 +57,23 @@
             const vector<Matcher>& dimensionFields,
             std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
 
+    virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
+            const std::vector<sp<ConditionTracker>>& allConditions) const {
+        if (mSliced) {
+            return &mLastChangedToTrueDimensions;
+        } else {
+            return nullptr;
+        }
+    }
+    virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
+            const std::vector<sp<ConditionTracker>>& allConditions) const {
+        if (mSliced) {
+            return &mLastChangedToFalseDimensions;
+        } else {
+            return nullptr;
+        }
+    }
+
 private:
     const ConfigKey mConfigKey;
     // The index of the LogEventMatcher which defines the start.
@@ -75,6 +92,9 @@
 
     std::vector<Matcher> mOutputDimensions;
 
+    std::set<HashableDimensionKey> mLastChangedToTrueDimensions;
+    std::set<HashableDimensionKey> mLastChangedToFalseDimensions;
+
     int mDimensionTag;
 
     std::map<HashableDimensionKey, int> mSlicedConditionState;
@@ -87,6 +107,8 @@
 
     bool hitGuardRail(const HashableDimensionKey& newKey);
 
+    void dumpState();
+
     FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedCondition);
     FRIEND_TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim);
     FRIEND_TEST(SimpleConditionTrackerTest, TestStopAll);
diff --git a/cmds/statsd/src/condition/StateTracker.cpp b/cmds/statsd/src/condition/StateTracker.cpp
new file mode 100644
index 0000000..e479f93
--- /dev/null
+++ b/cmds/statsd/src/condition/StateTracker.cpp
@@ -0,0 +1,233 @@
+/*
+ * Copyright 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#define DEBUG false  // STOPSHIP if true
+#include "Log.h"
+
+#include "StateTracker.h"
+#include "guardrail/StatsdStats.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+using std::string;
+using std::unordered_set;
+using std::vector;
+
+StateTracker::StateTracker(const ConfigKey& key, const int64_t& id, const int index,
+                           const SimplePredicate& simplePredicate,
+                           const unordered_map<int64_t, int>& trackerNameIndexMap,
+                           const vector<Matcher> primaryKeys)
+    : ConditionTracker(id, index), mConfigKey(key), mPrimaryKeys(primaryKeys) {
+    if (simplePredicate.has_start()) {
+        auto pair = trackerNameIndexMap.find(simplePredicate.start());
+        if (pair == trackerNameIndexMap.end()) {
+            ALOGW("Start matcher %lld not found in the config", (long long)simplePredicate.start());
+            return;
+        }
+        mStartLogMatcherIndex = pair->second;
+        mTrackerIndex.insert(mStartLogMatcherIndex);
+    } else {
+        ALOGW("Condition %lld must have a start matcher", (long long)id);
+        return;
+    }
+
+    if (simplePredicate.has_dimensions()) {
+        translateFieldMatcher(simplePredicate.dimensions(), &mOutputDimensions);
+        if (mOutputDimensions.size() > 0) {
+            mSliced = true;
+            mDimensionTag = mOutputDimensions[0].mMatcher.getTag();
+        } else {
+            ALOGW("Condition %lld has invalid dimensions", (long long)id);
+            return;
+        }
+    } else {
+        ALOGW("Condition %lld being a state tracker, but has no dimension", (long long)id);
+        return;
+    }
+
+    if (simplePredicate.initial_value() == SimplePredicate_InitialValue_FALSE) {
+        mInitialValue = ConditionState::kFalse;
+    } else {
+        mInitialValue = ConditionState::kUnknown;
+    }
+
+    mNonSlicedConditionState = mInitialValue;
+    mInitialized = true;
+}
+
+StateTracker::~StateTracker() {
+    VLOG("~StateTracker()");
+}
+
+bool StateTracker::init(const vector<Predicate>& allConditionConfig,
+                        const vector<sp<ConditionTracker>>& allConditionTrackers,
+                        const unordered_map<int64_t, int>& conditionIdIndexMap,
+                        vector<bool>& stack) {
+    return mInitialized;
+}
+
+void StateTracker::dumpState() {
+    VLOG("StateTracker %lld DUMP:", (long long)mConditionId);
+    for (const auto& value : mSlicedState) {
+        VLOG("\t%s -> %s", value.first.toString().c_str(), value.second.toString().c_str());
+    }
+    VLOG("Last Changed to True: ");
+    for (const auto& value : mLastChangedToTrueDimensions) {
+        VLOG("%s", value.toString().c_str());
+    }
+    VLOG("Last Changed to False: ");
+    for (const auto& value : mLastChangedToFalseDimensions) {
+        VLOG("%s", value.toString().c_str());
+    }
+}
+
+bool StateTracker::hitGuardRail(const HashableDimensionKey& newKey) {
+    if (mSlicedState.find(newKey) != mSlicedState.end()) {
+        // if the condition is not sliced or the key is not new, we are good!
+        return false;
+    }
+    // 1. Report the tuple count if the tuple count > soft limit
+    if (mSlicedState.size() > StatsdStats::kDimensionKeySizeSoftLimit - 1) {
+        size_t newTupleCount = mSlicedState.size() + 1;
+        StatsdStats::getInstance().noteConditionDimensionSize(mConfigKey, mConditionId, newTupleCount);
+        // 2. Don't add more tuples, we are above the allowed threshold. Drop the data.
+        if (newTupleCount > StatsdStats::kDimensionKeySizeHardLimit) {
+            ALOGE("Predicate %lld dropping data for dimension key %s",
+                (long long)mConditionId, newKey.c_str());
+            return true;
+        }
+    }
+    return false;
+}
+
+void StateTracker::evaluateCondition(const LogEvent& event,
+                                     const vector<MatchingState>& eventMatcherValues,
+                                     const vector<sp<ConditionTracker>>& mAllConditions,
+                                     vector<ConditionState>& conditionCache,
+                                     vector<bool>& conditionChangedCache) {
+    mLastChangedToTrueDimensions.clear();
+    mLastChangedToFalseDimensions.clear();
+    if (conditionCache[mIndex] != ConditionState::kNotEvaluated) {
+        // it has been evaluated.
+        VLOG("Yes, already evaluated, %lld %d", (long long)mConditionId, conditionCache[mIndex]);
+        return;
+    }
+
+    if (mStartLogMatcherIndex >= 0 &&
+        eventMatcherValues[mStartLogMatcherIndex] != MatchingState::kMatched) {
+        conditionCache[mIndex] =
+                mSlicedState.size() > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+        conditionChangedCache[mIndex] = false;
+        return;
+    }
+
+    VLOG("StateTracker evaluate event %s", event.ToString().c_str());
+
+    vector<HashableDimensionKey> keys;
+    vector<HashableDimensionKey> outputs;
+    filterValues(mPrimaryKeys, event.getValues(), &keys);
+    filterValues(mOutputDimensions, event.getValues(), &outputs);
+    if (keys.size() != 1 || outputs.size() != 1) {
+        ALOGE("More than 1 states in the event?? panic now!");
+        conditionCache[mIndex] =
+                mSlicedState.size() > 0 ? ConditionState::kTrue : ConditionState::kFalse;
+        conditionChangedCache[mIndex] = false;
+        return;
+    }
+    // Primary key can exclusive fields must be simple fields. so there won't be more than
+    // one keys matched.
+    const auto& primaryKey = keys[0];
+    const auto& state = outputs[0];
+    hitGuardRail(primaryKey);
+
+    VLOG("StateTracker: key %s state %s", primaryKey.toString().c_str(), state.toString().c_str());
+
+    auto it = mSlicedState.find(primaryKey);
+    if (it == mSlicedState.end()) {
+        mSlicedState[primaryKey] = state;
+        conditionCache[mIndex] = ConditionState::kTrue;
+        mLastChangedToTrueDimensions.insert(state);
+        conditionChangedCache[mIndex] = true;
+    } else if (!(it->second == state)) {
+        mLastChangedToFalseDimensions.insert(it->second);
+        mLastChangedToTrueDimensions.insert(state);
+        mSlicedState[primaryKey] = state;
+        conditionCache[mIndex] = ConditionState::kTrue;
+        conditionChangedCache[mIndex] = true;
+    } else {
+        conditionCache[mIndex] = ConditionState::kTrue;
+        conditionChangedCache[mIndex] = false;
+    }
+
+    if (DEBUG) {
+        dumpState();
+    }
+    return;
+}
+
+void StateTracker::isConditionMet(
+        const ConditionKey& conditionParameters, const vector<sp<ConditionTracker>>& allConditions,
+        const vector<Matcher>& 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;
+    }
+
+    const auto pair = conditionParameters.find(mConditionId);
+    if (pair == conditionParameters.end()) {
+        if (mSlicedState.size() > 0) {
+            conditionCache[mIndex] = ConditionState::kTrue;
+
+            for (const auto& state : mSlicedState) {
+                dimensionsKeySet.insert(state.second);
+            }
+        } else {
+            conditionCache[mIndex] = ConditionState::kUnknown;
+        }
+        return;
+    }
+
+    const auto& primaryKeys = pair->second;
+    conditionCache[mIndex] = mInitialValue;
+    for (const auto& primaryKey : primaryKeys) {
+        auto it = mSlicedState.find(primaryKey);
+        if (it != mSlicedState.end()) {
+            conditionCache[mIndex] = ConditionState::kTrue;
+            dimensionsKeySet.insert(it->second);
+        }
+    }
+}
+
+ConditionState StateTracker::getMetConditionDimension(
+        const std::vector<sp<ConditionTracker>>& allConditions,
+        const vector<Matcher>& dimensionFields,
+        std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const {
+    if (mSlicedState.size() > 0) {
+        for (const auto& state : mSlicedState) {
+            dimensionsKeySet.insert(state.second);
+        }
+        return ConditionState::kTrue;
+    }
+
+    return mInitialValue;
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/condition/StateTracker.h b/cmds/statsd/src/condition/StateTracker.h
new file mode 100644
index 0000000..3fe6e60
--- /dev/null
+++ b/cmds/statsd/src/condition/StateTracker.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2018, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#pragma once
+
+#include <gtest/gtest_prod.h>
+#include "ConditionTracker.h"
+#include "config/ConfigKey.h"
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "stats_util.h"
+
+namespace android {
+namespace os {
+namespace statsd {
+
+class StateTracker : public virtual ConditionTracker {
+public:
+    StateTracker(const ConfigKey& key, const int64_t& id, const int index,
+                 const SimplePredicate& simplePredicate,
+                 const std::unordered_map<int64_t, int>& trackerNameIndexMap,
+                 const vector<Matcher> primaryKeys);
+
+    ~StateTracker();
+
+    bool init(const std::vector<Predicate>& allConditionConfig,
+              const std::vector<sp<ConditionTracker>>& allConditionTrackers,
+              const std::unordered_map<int64_t, int>& conditionIdIndexMap,
+              std::vector<bool>& stack) override;
+
+    void evaluateCondition(const LogEvent& event,
+                           const std::vector<MatchingState>& eventMatcherValues,
+                           const std::vector<sp<ConditionTracker>>& mAllConditions,
+                           std::vector<ConditionState>& conditionCache,
+                           std::vector<bool>& changedCache) override;
+
+    /**
+     * Note: dimensionFields will be ignored in StateTracker, because we demand metrics
+     * must take the entire dimension fields from StateTracker. This is to make implementation
+     * simple and efficient.
+     *
+     * For example: wakelock duration by uid process states:
+     *              dimension in condition must be {uid, process state}.
+     */
+    void isConditionMet(const ConditionKey& conditionParameters,
+                        const std::vector<sp<ConditionTracker>>& allConditions,
+                        const vector<Matcher>& dimensionFields,
+                        std::vector<ConditionState>& conditionCache,
+                        std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
+
+    /**
+     * Note: dimensionFields will be ignored in StateTracker, because we demand metrics
+     * must take the entire dimension fields from StateTracker. This is to make implementation
+     * simple and efficient.
+     */
+    ConditionState getMetConditionDimension(
+            const std::vector<sp<ConditionTracker>>& allConditions,
+            const vector<Matcher>& dimensionFields,
+            std::unordered_set<HashableDimensionKey>& dimensionsKeySet) const override;
+
+    virtual const std::set<HashableDimensionKey>* getChangedToTrueDimensions(
+            const std::vector<sp<ConditionTracker>>& allConditions) const {
+        return &mLastChangedToTrueDimensions;
+    }
+
+    virtual const std::set<HashableDimensionKey>* getChangedToFalseDimensions(
+            const std::vector<sp<ConditionTracker>>& allConditions) const {
+        return &mLastChangedToFalseDimensions;
+    }
+
+private:
+    const ConfigKey mConfigKey;
+
+    // The index of the LogEventMatcher which defines the start.
+    int mStartLogMatcherIndex;
+
+    std::set<HashableDimensionKey> mLastChangedToTrueDimensions;
+    std::set<HashableDimensionKey> mLastChangedToFalseDimensions;
+
+    std::vector<Matcher> mOutputDimensions;
+    std::vector<Matcher> mPrimaryKeys;
+
+    ConditionState mInitialValue;
+
+    int mDimensionTag;
+
+    void dumpState();
+
+    bool hitGuardRail(const HashableDimensionKey& newKey);
+
+    // maps from [primary_key] to [primary_key, exclusive_state].
+    std::unordered_map<HashableDimensionKey, HashableDimensionKey> mSlicedState;
+
+    FRIEND_TEST(StateTrackerTest, TestStateChange);
+};
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
\ No newline at end of file
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.cpp b/cmds/statsd/src/metrics/metrics_manager_util.cpp
index 9912afa..b5afef2 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.cpp
+++ b/cmds/statsd/src/metrics/metrics_manager_util.cpp
@@ -21,6 +21,7 @@
 
 #include "../condition/CombinationConditionTracker.h"
 #include "../condition/SimpleConditionTracker.h"
+#include "../condition/StateTracker.h"
 #include "../external/StatsPullerManager.h"
 #include "../matchers/CombinationLogMatchingTracker.h"
 #include "../matchers/SimpleLogMatchingTracker.h"
@@ -31,6 +32,7 @@
 #include "../metrics/ValueMetricProducer.h"
 
 #include "stats_util.h"
+#include "statslog.h"
 
 using std::set;
 using std::string;
@@ -157,6 +159,49 @@
     return true;
 }
 
+/**
+ * A StateTracker is built from a SimplePredicate which has only "start", and no "stop"
+ * or "stop_all". The start must be an atom matcher that matches a state atom. It must
+ * have dimension, the dimension must be the state atom's primary fields plus exclusive state
+ * field. For example, the StateTracker is used in tracking UidProcessState and ScreenState.
+ *
+ */
+bool isStateTracker(const SimplePredicate& simplePredicate, vector<Matcher>* primaryKeys) {
+    // 1. must not have "stop". must have "dimension"
+    if (!simplePredicate.has_stop() && simplePredicate.has_dimensions()) {
+        // TODO: need to check the start atom matcher too.
+        auto it = android::util::kStateAtomsFieldOptions.find(simplePredicate.dimensions().field());
+        // 2. must be based on a state atom.
+        if (it != android::util::kStateAtomsFieldOptions.end()) {
+            // 3. dimension must be primary fields + state field IN ORDER
+            size_t expectedDimensionCount = it->second.primaryFields.size() + 1;
+            vector<Matcher> dimensions;
+            translateFieldMatcher(simplePredicate.dimensions(), &dimensions);
+            if (dimensions.size() != expectedDimensionCount) {
+                return false;
+            }
+            // 3.1 check the primary fields first.
+            size_t index = 0;
+            for (const auto& field : it->second.primaryFields) {
+                Matcher matcher = getSimpleMatcher(it->first, field);
+                if (!(matcher == dimensions[index])) {
+                    return false;
+                }
+                primaryKeys->push_back(matcher);
+                index++;
+            }
+            Matcher stateFieldMatcher =
+                    getSimpleMatcher(it->first, it->second.exclusiveField);
+            // 3.2 last dimension should be the exclusive field.
+            if (!(dimensions.back() == stateFieldMatcher)) {
+                return false;
+            }
+            return true;
+        }
+    }
+    return false;
+}  // namespace statsd
+
 bool initConditions(const ConfigKey& key, const StatsdConfig& config,
                     const unordered_map<int64_t, int>& logTrackerMap,
                     unordered_map<int64_t, int>& conditionTrackerMap,
@@ -172,8 +217,16 @@
         int index = allConditionTrackers.size();
         switch (condition.contents_case()) {
             case Predicate::ContentsCase::kSimplePredicate: {
-                allConditionTrackers.push_back(new SimpleConditionTracker(
-                        key, condition.id(), index, condition.simple_predicate(), logTrackerMap));
+                vector<Matcher> primaryKeys;
+                if (isStateTracker(condition.simple_predicate(), &primaryKeys)) {
+                    allConditionTrackers.push_back(new StateTracker(key, condition.id(), index,
+                                                                    condition.simple_predicate(),
+                                                                    logTrackerMap, primaryKeys));
+                } else {
+                    allConditionTrackers.push_back(new SimpleConditionTracker(
+                            key, condition.id(), index, condition.simple_predicate(),
+                            logTrackerMap));
+                }
                 break;
             }
             case Predicate::ContentsCase::kCombination: {
diff --git a/cmds/statsd/src/metrics/metrics_manager_util.h b/cmds/statsd/src/metrics/metrics_manager_util.h
index edda53d..386de0b 100644
--- a/cmds/statsd/src/metrics/metrics_manager_util.h
+++ b/cmds/statsd/src/metrics/metrics_manager_util.h
@@ -110,6 +110,8 @@
                       std::unordered_map<int, std::vector<int>>& trackerToConditionMap,
                       std::set<int64_t> &noReportMetricIds);
 
+bool isStateTracker(const SimplePredicate& simplePredicate, std::vector<Matcher>* primaryKeys);
+
 }  // namespace statsd
 }  // namespace os
 }  // namespace android
diff --git a/cmds/statsd/tests/condition/StateTracker_test.cpp b/cmds/statsd/tests/condition/StateTracker_test.cpp
new file mode 100644
index 0000000..9a66254
--- /dev/null
+++ b/cmds/statsd/tests/condition/StateTracker_test.cpp
@@ -0,0 +1,112 @@
+// 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/StateTracker.h"
+#include "tests/statsd_test_util.h"
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdio.h>
+#include <numeric>
+#include <vector>
+
+using std::map;
+using std::unordered_map;
+using std::vector;
+
+#ifdef __ANDROID__
+namespace android {
+namespace os {
+namespace statsd {
+
+const int kUidProcTag = 27;
+
+SimplePredicate getUidProcStatePredicate() {
+    SimplePredicate simplePredicate;
+    simplePredicate.set_start(StringToId("UidProcState"));
+
+    simplePredicate.mutable_dimensions()->set_field(kUidProcTag);
+    simplePredicate.mutable_dimensions()->add_child()->set_field(1);
+    simplePredicate.mutable_dimensions()->add_child()->set_field(2);
+
+    simplePredicate.set_count_nesting(false);
+    return simplePredicate;
+}
+
+void makeUidProcStateEvent(int32_t uid, int32_t state, LogEvent* event) {
+    event->write(uid);
+    event->write(state);
+    event->init();
+}
+
+TEST(StateTrackerTest, TestStateChange) {
+    int uid1 = 111;
+    int uid2 = 222;
+
+    int state1 = 1001;
+    int state2 = 1002;
+    unordered_map<int64_t, int> trackerNameIndexMap;
+    trackerNameIndexMap[StringToId("UidProcState")] = 0;
+    vector<Matcher> primaryFields;
+    primaryFields.push_back(getSimpleMatcher(kUidProcTag, 1));
+    StateTracker tracker(ConfigKey(12, 123), 123, 0, getUidProcStatePredicate(),
+                         trackerNameIndexMap, primaryFields);
+
+    LogEvent event(kUidProcTag, 0 /*timestamp*/);
+    makeUidProcStateEvent(uid1, state1, &event);
+
+    vector<MatchingState> matcherState;
+    matcherState.push_back(MatchingState::kMatched);
+    vector<sp<ConditionTracker>> allPredicates;
+    vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+    vector<bool> changedCache(1, false);
+
+    tracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache);
+    EXPECT_EQ(1ULL, tracker.mLastChangedToTrueDimensions.size());
+    EXPECT_EQ(0ULL, tracker.mLastChangedToFalseDimensions.size());
+    EXPECT_TRUE(changedCache[0]);
+
+    changedCache[0] = false;
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    tracker.evaluateCondition(event, matcherState, allPredicates, conditionCache, changedCache);
+    EXPECT_EQ(0ULL, tracker.mLastChangedToTrueDimensions.size());
+    EXPECT_EQ(0ULL, tracker.mLastChangedToFalseDimensions.size());
+    EXPECT_FALSE(changedCache[0]);
+
+    LogEvent event2(kUidProcTag, 0 /*timestamp*/);
+    makeUidProcStateEvent(uid1, state2, &event2);
+
+    changedCache[0] = false;
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    tracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache, changedCache);
+    EXPECT_EQ(1ULL, tracker.mLastChangedToTrueDimensions.size());
+    EXPECT_EQ(1ULL, tracker.mLastChangedToFalseDimensions.size());
+    EXPECT_TRUE(changedCache[0]);
+
+    LogEvent event3(kUidProcTag, 0 /*timestamp*/);
+    makeUidProcStateEvent(uid2, state1, &event3);
+    changedCache[0] = false;
+    conditionCache[0] = ConditionState::kNotEvaluated;
+    tracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache, changedCache);
+    EXPECT_EQ(1ULL, tracker.mLastChangedToTrueDimensions.size());
+    EXPECT_EQ(0ULL, tracker.mLastChangedToFalseDimensions.size());
+    EXPECT_TRUE(changedCache[0]);
+}
+
+}  // namespace statsd
+}  // namespace os
+}  // namespace android
+#else
+GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif