Use new socket schema within TestSlicedCondition

This CL serves as a prototype for future efforts to move statsd tests to
the new socket schema.

We also significantly refactor the LogEvent class by introducing two new
functions:
    - LogEvent::LogEvent(int32_t uid, int32_t pid)
    - bool LogEvent::parseBuffer(uint8_t* buf, size_t size)

The goal is that these two functions will be the only two entry points
into the class. Separating these two is helpful because (a) it allows
test and source code to use the same codepath and (b) it allows the
calling function to see if the atom encoding was successfully parsed
(note that constructors can't return values).

P.S. This CL breaks LogEventQueueTests, but that will be resolved once
all tests are moved to the new constructors.

Test: m statsd
Test: bit statsd_test:SimpleConditionTrackerTest#TestSlicedCondition
Bug: 149590301
Change-Id: Id3c9522c4467c4869ec97226734a556dd9dfb169
Merged-In: Id3c9522c4467c4869ec97226734a556dd9dfb169
(cherry picked from commit 14f56f6bc4ee7d0b734c9be0f39ddf2d2e41cae4)
diff --git a/cmds/statsd/src/external/StatsCallbackPuller.cpp b/cmds/statsd/src/external/StatsCallbackPuller.cpp
index 0edf40b..933f48d 100644
--- a/cmds/statsd/src/external/StatsCallbackPuller.cpp
+++ b/cmds/statsd/src/external/StatsCallbackPuller.cpp
@@ -66,10 +66,8 @@
                 {
                     lock_guard<mutex> lk(*cv_mutex);
                     for (const StatsEventParcel& parcel: output) {
-                        uint8_t* buf = reinterpret_cast<uint8_t*>(
-                                const_cast<int8_t*>(parcel.buffer.data()));
-                        shared_ptr<LogEvent> event = make_shared<LogEvent>(
-                                buf, parcel.buffer.size(), /*uid=*/-1, /*pid=*/-1);
+                        shared_ptr<LogEvent> event = make_shared<LogEvent>(/*uid=*/-1, /*pid=*/-1);
+                        event->parseBuffer((uint8_t*)parcel.buffer.data(), parcel.buffer.size());
                         sharedData->push_back(event);
                     }
                     *pullSuccess = success;
diff --git a/cmds/statsd/src/logd/LogEvent.cpp b/cmds/statsd/src/logd/LogEvent.cpp
index 3e46d13..974e203c 100644
--- a/cmds/statsd/src/logd/LogEvent.cpp
+++ b/cmds/statsd/src/logd/LogEvent.cpp
@@ -65,18 +65,6 @@
 #define ATTRIBUTION_CHAIN_TYPE 0x09
 #define ERROR_TYPE 0x0F
 
-// Msg is expected to begin at the start of the serialized atom -- it should not
-// include the android_log_header_t or the StatsEventTag.
-LogEvent::LogEvent(uint8_t* msg, uint32_t len, int32_t uid, int32_t pid)
-    : mBuf(msg),
-      mRemainingLen(len),
-      mLogdTimestampNs(time(nullptr)),
-      mLogUid(uid),
-      mLogPid(pid)
-{
-    initNew();
-}
-
 LogEvent::LogEvent(const LogEvent& event) {
     mTagId = event.mTagId;
     mLogUid = event.mLogUid;
@@ -86,6 +74,12 @@
     mValues = event.mValues;
 }
 
+LogEvent::LogEvent(int32_t uid, int32_t pid)
+    : mLogdTimestampNs(time(nullptr)),
+      mLogUid(uid),
+      mLogPid(pid) {
+}
+
 LogEvent::LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs) {
     mLogdTimestampNs = wallClockTimestampNs;
     mElapsedTimestampNs = elapsedTimestampNs;
@@ -189,9 +183,6 @@
     mValues.push_back(FieldValue(Field(mTagId, getSimpleField(4)), Value(trainInfo.status)));
 }
 
-LogEvent::LogEvent(int32_t tagId, int64_t timestampNs) : LogEvent(tagId, timestampNs, timestampNs) {
-}
-
 LogEvent::LogEvent(int32_t tagId, int64_t timestampNs, int32_t uid) {
     mLogdTimestampNs = timestampNs;
     mTagId = tagId;
@@ -467,7 +458,10 @@
 
 // This parsing logic is tied to the encoding scheme used in StatsEvent.java and
 // stats_event.c
-void LogEvent::initNew() {
+bool LogEvent::parseBuffer(uint8_t* buf, size_t len) {
+    mBuf = buf;
+    mRemainingLen = (uint32_t)len;
+
     int32_t pos[] = {1, 1, 1};
     bool last[] = {false, false, false};
 
@@ -529,6 +523,7 @@
 
     if (mRemainingLen != 0) mValid = false;
     mBuf = nullptr;
+    return mValid;
 }
 
 uint8_t LogEvent::getTypeId(uint8_t typeInfo) {
diff --git a/cmds/statsd/src/logd/LogEvent.h b/cmds/statsd/src/logd/LogEvent.h
index e167e67..3940aa8e 100644
--- a/cmds/statsd/src/logd/LogEvent.h
+++ b/cmds/statsd/src/logd/LogEvent.h
@@ -20,7 +20,6 @@
 
 #include <android/util/ProtoOutputStream.h>
 #include <private/android_logger.h>
-#include <stats_event.h>
 
 #include <string>
 #include <vector>
@@ -61,14 +60,32 @@
 };
 
 /**
- * Wrapper for the log_msg structure.
+ * This class decodes the structured, serialized encoding of an atom into a
+ * vector of FieldValues.
  */
 class LogEvent {
 public:
     /**
-     * Read a LogEvent from the socket
+     * \param uid user id of the logging caller
+     * \param pid process id of the logging caller
      */
-    explicit LogEvent(uint8_t* msg, uint32_t len, int32_t uid, int32_t pid);
+    explicit LogEvent(int32_t uid, int32_t pid);
+
+    /**
+     * Parses the atomId, timestamp, and vector of values from a buffer
+     * containing the StatsEvent/AStatsEvent encoding of an atom.
+     *
+     * \param buf a buffer that begins at the start of the serialized atom (it
+     * should not include the android_log_header_t or the StatsEventTag)
+     * \param len size of the buffer
+     *
+     * \return success of the initialization
+     */
+    bool parseBuffer(uint8_t* buf, size_t len);
+
+    // TODO(b/149590301): delete unused functions below once LogEvent uses the
+    // new socket schema within test code. Really we would like the only entry
+    // points into LogEvent to be the above constructor and parseBuffer functions.
 
     /**
      * Constructs a LogEvent with synthetic data for testing. Must call init() before reading.
@@ -76,9 +93,6 @@
     explicit LogEvent(int32_t tagId, int64_t wallClockTimestampNs, int64_t elapsedTimestampNs);
 
     // For testing. The timestamp is used as both elapsed real time and logd timestamp.
-    explicit LogEvent(int32_t tagId, int64_t timestampNs);
-
-    // For testing. The timestamp is used as both elapsed real time and logd timestamp.
     explicit LogEvent(int32_t tagId, int64_t timestampNs, int32_t uid);
 
     /**
@@ -192,10 +206,6 @@
         return &mValues;
     }
 
-    bool isValid() {
-          return mValid;
-    }
-
     inline LogEvent makeCopy() {
         return LogEvent(*this);
     }
@@ -222,12 +232,6 @@
      */
     LogEvent(const LogEvent&);
 
-
-    /**
-     * Parsing function for new encoding scheme.
-     */
-    void initNew();
-
     void parseInt32(int32_t* pos, int32_t depth, bool* last);
     void parseInt64(int32_t* pos, int32_t depth, bool* last);
     void parseString(int32_t* pos, int32_t depth, bool* last);
@@ -238,13 +242,14 @@
     void parseAttributionChain(int32_t* pos, int32_t depth, bool* last);
 
     /**
-     * mBuf is a pointer to the current location in the buffer being parsed.
-     * Because the buffer lives  on the StatsSocketListener stack, this pointer
-     * is only valid during the LogEvent constructor. It will be set to null at
-     * the end of initNew.
+     * The below three variables are only valid during the execution of
+     * parseBuffer. There are no guarantees about the state of these variables
+     * before/after.
+     *
+     * TODO (b/150312423): These shouldn't be member variables. We should pass
+     * them around as parameters.
      */
     uint8_t* mBuf;
-
     uint32_t mRemainingLen; // number of valid bytes left in the buffer being parsed
     bool mValid = true; // stores whether the event we received from the socket is valid
 
diff --git a/cmds/statsd/src/socket/StatsSocketListener.cpp b/cmds/statsd/src/socket/StatsSocketListener.cpp
index 8f0f480..b877cc9 100755
--- a/cmds/statsd/src/socket/StatsSocketListener.cpp
+++ b/cmds/statsd/src/socket/StatsSocketListener.cpp
@@ -126,7 +126,10 @@
     uint32_t pid = cred->pid;
 
     int64_t oldestTimestamp;
-    if (!mQueue->push(std::make_unique<LogEvent>(msg, len, uid, pid), &oldestTimestamp)) {
+    std::unique_ptr<LogEvent> logEvent = std::make_unique<LogEvent>(uid, pid);
+    logEvent->parseBuffer(msg, len);
+
+    if (!mQueue->push(std::move(logEvent), &oldestTimestamp)) {
         StatsdStats::getInstance().noteEventQueueOverflow(oldestTimestamp);
     }
 
diff --git a/cmds/statsd/tests/LogEvent_test.cpp b/cmds/statsd/tests/LogEvent_test.cpp
index 7542faf..7458cbf 100644
--- a/cmds/statsd/tests/LogEvent_test.cpp
+++ b/cmds/statsd/tests/LogEvent_test.cpp
@@ -54,8 +54,9 @@
     size_t size;
     uint8_t* buf = AStatsEvent_getBuffer(event, &size);
 
-    LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
-    EXPECT_TRUE(logEvent.isValid());
+    LogEvent logEvent(/*uid=*/1000, /*pid=*/1001);
+    EXPECT_TRUE(logEvent.parseBuffer(buf, size));
+
     EXPECT_EQ(100, logEvent.GetTagId());
     EXPECT_EQ(1000, logEvent.GetUid());
     EXPECT_EQ(1001, logEvent.GetPid());
@@ -102,8 +103,9 @@
     size_t size;
     uint8_t* buf = AStatsEvent_getBuffer(event, &size);
 
-    LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
-    EXPECT_TRUE(logEvent.isValid());
+    LogEvent logEvent(/*uid=*/ 1000, /*pid=*/ 1001);
+    EXPECT_TRUE(logEvent.parseBuffer(buf, size));
+
     EXPECT_EQ(100, logEvent.GetTagId());
     EXPECT_EQ(1000, logEvent.GetUid());
     EXPECT_EQ(1001, logEvent.GetPid());
@@ -137,8 +139,9 @@
     size_t size;
     uint8_t* buf = AStatsEvent_getBuffer(event, &size);
 
-    LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
-    EXPECT_TRUE(logEvent.isValid());
+    LogEvent logEvent(/*uid=*/ 1000, /*pid=*/ 1001);
+    EXPECT_TRUE(logEvent.parseBuffer(buf, size));
+
     EXPECT_EQ(100, logEvent.GetTagId());
     EXPECT_EQ(1000, logEvent.GetUid());
     EXPECT_EQ(1001, logEvent.GetPid());
@@ -165,8 +168,9 @@
     size_t size;
     uint8_t* buf = AStatsEvent_getBuffer(event, &size);
 
-    LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
-    EXPECT_TRUE(logEvent.isValid());
+    LogEvent logEvent(/*uid=*/ 1000, /*pid=*/ 1001);
+    EXPECT_TRUE(logEvent.parseBuffer(buf, size));
+
     EXPECT_EQ(100, logEvent.GetTagId());
     EXPECT_EQ(1000, logEvent.GetUid());
     EXPECT_EQ(1001, logEvent.GetPid());
@@ -200,8 +204,9 @@
     size_t size;
     uint8_t* buf = AStatsEvent_getBuffer(event, &size);
 
-    LogEvent logEvent(buf, size, /*uid=*/ 1000, /*pid=*/ 1001);
-    EXPECT_TRUE(logEvent.isValid());
+    LogEvent logEvent(/*uid=*/ 1000, /*pid=*/ 1001);
+    EXPECT_TRUE(logEvent.parseBuffer(buf, size));
+
     EXPECT_EQ(100, logEvent.GetTagId());
     EXPECT_EQ(1000, logEvent.GetUid());
     EXPECT_EQ(1001, logEvent.GetPid());
diff --git a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
index 36094b2..8701e17 100644
--- a/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
+++ b/cmds/statsd/tests/condition/SimpleConditionTracker_test.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "src/condition/SimpleConditionTracker.h"
+#include "stats_event.h"
 #include "tests/statsd_test_util.h"
 
 #include <gmock/gmock.h>
@@ -31,6 +32,8 @@
 namespace os {
 namespace statsd {
 
+namespace {
+
 const ConfigKey kConfigKey(0, 12345);
 
 const int ATTRIBUTION_NODE_FIELD_ID = 1;
@@ -57,24 +60,33 @@
     return simplePredicate;
 }
 
-void writeAttributionNodesToEvent(LogEvent* event, const std::vector<int> &uids) {
-    std::vector<AttributionNodeInternal> nodes;
-    for (size_t i = 0; i < uids.size(); ++i) {
-        AttributionNodeInternal node;
-        node.set_uid(uids[i]);
-        nodes.push_back(node);
+void makeWakeLockEvent(LogEvent* logEvent, uint32_t atomId, uint64_t timestamp,
+                       const vector<int>& uids, const string& wl, int acquire) {
+    AStatsEvent* statsEvent = AStatsEvent_obtain();
+    AStatsEvent_setAtomId(statsEvent, atomId);
+    AStatsEvent_overwriteTimestamp(statsEvent, timestamp);
+
+    vector<std::string> tags(uids.size()); // vector of empty strings
+    vector<const char*> cTags(uids.size());
+    for (int i = 0; i < cTags.size(); i++) {
+        cTags[i] = tags[i].c_str();
     }
-    event->write(nodes);  // attribution chain.
+    AStatsEvent_writeAttributionChain(statsEvent, reinterpret_cast<const uint32_t*>(uids.data()),
+                                      cTags.data(), uids.size());
+
+    AStatsEvent_writeString(statsEvent, wl.c_str());
+    AStatsEvent_writeInt32(statsEvent, acquire);
+    AStatsEvent_build(statsEvent);
+
+    size_t size;
+    uint8_t* buf = AStatsEvent_getBuffer(statsEvent, &size);
+    logEvent->parseBuffer(buf, size);
+
+    AStatsEvent_release(statsEvent);
 }
 
-// TODO(b/149590301): Update this helper to use new socket schema.
-//void makeWakeLockEvent(
-//        LogEvent* event, const std::vector<int> &uids, const string& wl, int acquire) {
-//    writeAttributionNodesToEvent(event, uids);
-//    event->write(wl);
-//    event->write(acquire);
-//    event->init();
-//}
+} // anonymous namespace
+
 
 std::map<int64_t, HashableDimensionKey> getWakeLockQueryKey(
     const Position position,
@@ -265,138 +277,128 @@
     EXPECT_TRUE(changedCache[0]);
 }
 
-// TODO(b/149590301): Update these tests to use new socket schema.
-//TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
-//    std::vector<sp<ConditionTracker>> allConditions;
-//    for (Position position :
-//            { Position::FIRST, Position::LAST}) {
-//
-//        SimplePredicate simplePredicate = getWakeLockHeldCondition(
-//                true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
-//                position);
-//        string conditionName = "WL_HELD_BY_UID2";
-//
-//        unordered_map<int64_t, int> trackerNameIndexMap;
-//        trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0;
-//        trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
-//        trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
-//
-//        SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
-//                                                0 /*condition tracker index*/, simplePredicate,
-//                                                trackerNameIndexMap);
-//
-//        std::vector<int> uids = {111, 222, 333};
-//
-//        LogEvent event(1 /*tagId*/, 0 /*timestamp*/);
-//        makeWakeLockEvent(&event, uids, "wl1", 1);
-//
-//        // one matched start
-//        vector<MatchingState> matcherState;
-//        matcherState.push_back(MatchingState::kMatched);
-//        matcherState.push_back(MatchingState::kNotMatched);
-//        matcherState.push_back(MatchingState::kNotMatched);
-//        vector<sp<ConditionTracker>> allPredicates;
-//        vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
-//        vector<bool> changedCache(1, false);
-//
-//        conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
-//                                           changedCache);
-//
-//        if (position == Position::FIRST ||
-//            position == Position::LAST) {
-//            EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
-//        } else {
-//            EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
-//        }
-//        EXPECT_TRUE(changedCache[0]);
-//        if (position == Position::FIRST ||
-//            position == Position::LAST) {
-//            EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), 1u);
-//            EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
-//        } else {
-//            EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), uids.size());
-//        }
-//
-//        // Now test query
-//        const auto queryKey = getWakeLockQueryKey(position, uids, conditionName);
-//        conditionCache[0] = ConditionState::kNotEvaluated;
-//
-//        conditionTracker.isConditionMet(queryKey, allPredicates,
-//                                        false,
-//                                        conditionCache);
-//        EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
-//
-//        // another wake lock acquired by this uid
-//        LogEvent event2(1 /*tagId*/, 0 /*timestamp*/);
-//        makeWakeLockEvent(&event2, uids, "wl2", 1);
-//        matcherState.clear();
-//        matcherState.push_back(MatchingState::kMatched);
-//        matcherState.push_back(MatchingState::kNotMatched);
-//        conditionCache[0] = ConditionState::kNotEvaluated;
-//        changedCache[0] = false;
-//        conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
-//                                           changedCache);
-//        EXPECT_FALSE(changedCache[0]);
-//        if (position == Position::FIRST ||
-//            position == Position::LAST) {
-//            EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
-//        } else {
-//            EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
-//        }
-//        EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
-//        EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
-//
-//
-//        // wake lock 1 release
-//        LogEvent event3(1 /*tagId*/, 0 /*timestamp*/);
-//        makeWakeLockEvent(&event3, uids, "wl1", 0);  // now release it.
-//        matcherState.clear();
-//        matcherState.push_back(MatchingState::kNotMatched);
-//        matcherState.push_back(MatchingState::kMatched);
-//        conditionCache[0] = ConditionState::kNotEvaluated;
-//        changedCache[0] = false;
-//        conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
-//                                           changedCache);
-//        // nothing changes, because wake lock 2 is still held for this uid
-//        EXPECT_FALSE(changedCache[0]);
-//        if (position == Position::FIRST ||
-//            position == Position::LAST) {
-//            EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
-//        } else {
-//            EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
-//        }
-//        EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
-//        EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
-//
-//        LogEvent event4(1 /*tagId*/, 0 /*timestamp*/);
-//        makeWakeLockEvent(&event4, uids, "wl2", 0);  // now release it.
-//        matcherState.clear();
-//        matcherState.push_back(MatchingState::kNotMatched);
-//        matcherState.push_back(MatchingState::kMatched);
-//        conditionCache[0] = ConditionState::kNotEvaluated;
-//        changedCache[0] = false;
-//        conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache,
-//                                           changedCache);
-//        EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
-//        EXPECT_TRUE(changedCache[0]);
-//        if (position == Position::FIRST ||
-//            position == Position::LAST) {
-//            EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), 1u);
-//            EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
-//        } else {
-//            EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), uids.size());
-//        }
-//
-//        // query again
-//        conditionCache[0] = ConditionState::kNotEvaluated;
-//        conditionTracker.isConditionMet(queryKey, allPredicates,
-//                                        false,
-//                                        conditionCache);
-//        EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
-//    }
-//
-//}
-//
+TEST(SimpleConditionTrackerTest, TestSlicedCondition) {
+    std::vector<sp<ConditionTracker>> allConditions;
+    for (Position position : {Position::FIRST, Position::LAST}) {
+        SimplePredicate simplePredicate = getWakeLockHeldCondition(
+                true /*nesting*/, true /*default to false*/, true /*output slice by uid*/,
+                position);
+        string conditionName = "WL_HELD_BY_UID2";
+
+        unordered_map<int64_t, int> trackerNameIndexMap;
+        trackerNameIndexMap[StringToId("WAKE_LOCK_ACQUIRE")] = 0;
+        trackerNameIndexMap[StringToId("WAKE_LOCK_RELEASE")] = 1;
+        trackerNameIndexMap[StringToId("RELEASE_ALL")] = 2;
+
+        SimpleConditionTracker conditionTracker(kConfigKey, StringToId(conditionName),
+                                                0 /*condition tracker index*/, simplePredicate,
+                                                trackerNameIndexMap);
+
+        std::vector<int> uids = {111, 222, 333};
+
+        LogEvent event(/*uid=*/-1, /*pid=*/-1);
+        makeWakeLockEvent(&event, /*atomId=*/ 1, /*timestamp=*/ 0, uids, "wl1", /*acquire=*/ 1);
+
+        // one matched start
+        vector<MatchingState> matcherState;
+        matcherState.push_back(MatchingState::kMatched);
+        matcherState.push_back(MatchingState::kNotMatched);
+        matcherState.push_back(MatchingState::kNotMatched);
+        vector<sp<ConditionTracker>> allPredicates;
+        vector<ConditionState> conditionCache(1, ConditionState::kNotEvaluated);
+        vector<bool> changedCache(1, false);
+
+        conditionTracker.evaluateCondition(event, matcherState, allPredicates, conditionCache,
+                                           changedCache);
+
+        if (position == Position::FIRST || position == Position::LAST) {
+            EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+        } else {
+            EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
+        }
+        EXPECT_TRUE(changedCache[0]);
+        if (position == Position::FIRST || position == Position::LAST) {
+            EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(), 1u);
+            EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
+        } else {
+            EXPECT_EQ(conditionTracker.getChangedToTrueDimensions(allConditions)->size(),
+                      uids.size());
+        }
+
+        // Now test query
+        const auto queryKey = getWakeLockQueryKey(position, uids, conditionName);
+        conditionCache[0] = ConditionState::kNotEvaluated;
+
+        conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
+        EXPECT_EQ(ConditionState::kTrue, conditionCache[0]);
+
+        // another wake lock acquired by this uid
+        LogEvent event2(/*uid=*/-1, /*pid=*/-1);
+        makeWakeLockEvent(&event2, /*atomId=*/ 1, /*timestamp=*/ 0, uids, "wl2", /*acquire=*/ 1);
+        matcherState.clear();
+        matcherState.push_back(MatchingState::kMatched);
+        matcherState.push_back(MatchingState::kNotMatched);
+        conditionCache[0] = ConditionState::kNotEvaluated;
+        changedCache[0] = false;
+        conditionTracker.evaluateCondition(event2, matcherState, allPredicates, conditionCache,
+                                           changedCache);
+        EXPECT_FALSE(changedCache[0]);
+        if (position == Position::FIRST || position == Position::LAST) {
+            EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+        } else {
+            EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
+        }
+        EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
+        EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
+
+
+        // wake lock 1 release
+        LogEvent event3(/*uid=*/-1, /*pid=*/-1);
+        makeWakeLockEvent(&event3, /*atomId=*/1, /*timestamp=*/0, uids, "wl1", /*acquire=*/0);
+        matcherState.clear();
+        matcherState.push_back(MatchingState::kNotMatched);
+        matcherState.push_back(MatchingState::kMatched);
+        conditionCache[0] = ConditionState::kNotEvaluated;
+        changedCache[0] = false;
+        conditionTracker.evaluateCondition(event3, matcherState, allPredicates, conditionCache,
+                                           changedCache);
+        // nothing changes, because wake lock 2 is still held for this uid
+        EXPECT_FALSE(changedCache[0]);
+        if (position == Position::FIRST || position == Position::LAST) {
+            EXPECT_EQ(1UL, conditionTracker.mSlicedConditionState.size());
+        } else {
+            EXPECT_EQ(uids.size(), conditionTracker.mSlicedConditionState.size());
+        }
+        EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
+        EXPECT_TRUE(conditionTracker.getChangedToFalseDimensions(allConditions)->empty());
+
+        LogEvent event4(/*uid=*/-1, /*pid=*/-1);
+        makeWakeLockEvent(&event, /*atomId=*/1, /*timestamp=*/ 0, uids, "wl2", /*acquire=*/0);
+        matcherState.clear();
+        matcherState.push_back(MatchingState::kNotMatched);
+        matcherState.push_back(MatchingState::kMatched);
+        conditionCache[0] = ConditionState::kNotEvaluated;
+        changedCache[0] = false;
+        conditionTracker.evaluateCondition(event4, matcherState, allPredicates, conditionCache,
+                                           changedCache);
+        EXPECT_EQ(0UL, conditionTracker.mSlicedConditionState.size());
+        EXPECT_TRUE(changedCache[0]);
+        if (position == Position::FIRST || position == Position::LAST) {
+            EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(), 1u);
+            EXPECT_TRUE(conditionTracker.getChangedToTrueDimensions(allConditions)->empty());
+        } else {
+            EXPECT_EQ(conditionTracker.getChangedToFalseDimensions(allConditions)->size(),
+                      uids.size());
+        }
+
+        // query again
+        conditionCache[0] = ConditionState::kNotEvaluated;
+        conditionTracker.isConditionMet(queryKey, allPredicates, false, conditionCache);
+        EXPECT_EQ(ConditionState::kFalse, conditionCache[0]);
+    }
+
+}
+
 //TEST(SimpleConditionTrackerTest, TestSlicedWithNoOutputDim) {
 //    std::vector<sp<ConditionTracker>> allConditions;
 //
diff --git a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
index e416b4c..1ff6621 100644
--- a/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
+++ b/cmds/statsd/tests/external/StatsCallbackPuller_test.cpp
@@ -28,6 +28,7 @@
 
 #include "../metrics/metrics_test_helper.h"
 #include "src/stats_log_util.h"
+#include "stats_event.h"
 #include "tests/statsd_test_util.h"
 
 #ifdef __ANDROID__
diff --git a/cmds/statsd/tests/statsd_test_util.cpp b/cmds/statsd/tests/statsd_test_util.cpp
index 2bfce9b..d416f13 100644
--- a/cmds/statsd/tests/statsd_test_util.cpp
+++ b/cmds/statsd/tests/statsd_test_util.cpp
@@ -15,6 +15,7 @@
 #include "statsd_test_util.h"
 
 #include <aidl/android/util/StatsEventParcel.h>
+#include "stats_event.h"
 
 using aidl::android::util::StatsEventParcel;
 using std::shared_ptr;