Merge "Adds new utility functions for evaluating log entry matching. Includes matching for both simple and compound matchers."
diff --git a/cmds/statsd/Android.mk b/cmds/statsd/Android.mk
index b9ee7ff..3f12016 100644
--- a/cmds/statsd/Android.mk
+++ b/cmds/statsd/Android.mk
@@ -54,6 +54,7 @@
     src/statsd_config.proto \
     src/stats_constants.proto \
     src/DropboxReader.cpp \
+    src/matchers/LogEntryMatcherManager.cpp \
 
 
 LOCAL_CFLAGS += \
@@ -107,6 +108,9 @@
 LOCAL_COMPATIBILITY_SUITE := device-tests
 LOCAL_MODULE_TAGS := tests
 
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/src \
+	STATSD_PROTO_INCLUDES
+
 LOCAL_CFLAGS += \
     -Wall \
     -Werror \
@@ -115,21 +119,24 @@
     -Wno-unused-function \
     -Wno-unused-parameter
 
-LOCAL_C_INCLUDES += $(LOCAL_PATH)/src \
-	STATSD_PROTO_INCLUDES
-
 LOCAL_SRC_FILES := \
+    src/stats_log.proto \
+    src/statsd_config.proto \
+    src/stats_constants.proto \
     ../../core/java/android/os/IStatsCompanionService.aidl \
     ../../core/java/android/os/IStatsManager.aidl \
     src/StatsService.cpp \
     tests/indexed_priority_queue_test.cpp \
+    src/parse_util.cpp \
     src/LogEntryPrinter.cpp \
     src/LogReader.cpp \
+    src/matchers/LogEntryMatcherManager.cpp \
     tests/LogReader_test.cpp \
+    tests/LogEntryMatcher_test.cpp \
 
 LOCAL_STATIC_LIBRARIES := \
     libgmock \
-    statsd_proto
+    statsd_proto \
 
 LOCAL_SHARED_LIBRARIES := \
     libbase \
diff --git a/cmds/statsd/src/matchers/LogEntryMatcherManager.cpp b/cmds/statsd/src/matchers/LogEntryMatcherManager.cpp
new file mode 100644
index 0000000..bb0951c
--- /dev/null
+++ b/cmds/statsd/src/matchers/LogEntryMatcherManager.cpp
@@ -0,0 +1,182 @@
+/*
+ * 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 "LogEntryMatcherManager.h"
+#include <log/event_tag_map.h>
+#include <log/logprint.h>
+#include <utils/Errors.h>
+#include <cutils/log.h>
+#include <unordered_map>
+#include <frameworks/base/cmds/statsd/src/statsd_config.pb.h>
+
+using std::unordered_map;
+using std::string;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+bool LogEntryMatcherManager::matches(const LogEntryMatcher &matcher, const int tagId,
+                                     const unordered_map<int, long> &intMap,
+                                     const unordered_map<int, string> &strMap,
+                                     const unordered_map<int, float> &floatMap,
+                                     const unordered_map<int, bool> &boolMap) {
+    if (matcher.has_combination()) { // Need to evaluate composite matching
+        switch (matcher.combination().operation()) {
+            case LogicalOperation::AND:
+                for (auto nestedMatcher : matcher.combination().matcher()) {
+                    if (!matches(nestedMatcher, tagId, intMap, strMap, floatMap, boolMap)) {
+                        return false; // return false if any nested matcher is false;
+                    }
+                }
+                return true; // Otherwise, return true.
+            case LogicalOperation::OR:
+                for (auto nestedMatcher : matcher.combination().matcher()) {
+                    if (matches(nestedMatcher, tagId, intMap, strMap, floatMap, boolMap)) {
+                        return true; // return true if any nested matcher is true;
+                    }
+                }
+                return false;
+            case LogicalOperation::NOT:
+                return !matches(matcher.combination().matcher(0),  tagId, intMap, strMap, floatMap,
+                                boolMap);
+
+            // Case NAND is just inverting the return statement of AND
+            case LogicalOperation::NAND:
+                for (auto nestedMatcher : matcher.combination().matcher()) {
+                    auto simple = nestedMatcher.simple_log_entry_matcher();
+                    if (!matches(nestedMatcher, tagId, intMap, strMap, floatMap, boolMap)) {
+                        return true; // return false if any nested matcher is false;
+                    }
+                }
+                return false; // Otherwise, return true.
+            case LogicalOperation::NOR:
+                for (auto nestedMatcher : matcher.combination().matcher()) {
+                    if (matches(nestedMatcher, tagId, intMap, strMap, floatMap, boolMap)) {
+                        return false; // return true if any nested matcher is true;
+                    }
+                }
+                return true;
+        }
+        return false;
+    } else {
+        return matchesSimple(matcher.simple_log_entry_matcher(), tagId, intMap, strMap, floatMap,
+                             boolMap);
+    }
+}
+
+bool LogEntryMatcherManager::matchesSimple(const SimpleLogEntryMatcher &simpleMatcher,
+                                           const int tagId,
+                                           const unordered_map<int, long> &intMap,
+                                           const unordered_map<int, string> &strMap,
+                                           const unordered_map<int, float> &floatMap,
+                                           const unordered_map<int, bool> &boolMap) {
+    for (int i = 0; i < simpleMatcher.tag_size(); i++) {
+        if (simpleMatcher.tag(i) != tagId) {
+            continue;
+        }
+
+        // now see if this event is interesting to us -- matches ALL the matchers
+        // defined in the metrics.
+        bool allMatched = true;
+        for (int j = 0; j < simpleMatcher.key_value_matcher_size(); j++) {
+            auto cur = simpleMatcher.key_value_matcher(j);
+
+            // TODO: Check if this key is a magic key (eg package name).
+            int key = cur.key_matcher().key();
+
+            switch (cur.value_matcher_case()) {
+                case KeyValueMatcher::ValueMatcherCase::kEqString: {
+                    auto it = strMap.find(key);
+                    if (it == strMap.end() || cur.eq_string().compare(it->second) != 0) {
+                        allMatched = false;
+                    }
+                    break;
+                }
+                case KeyValueMatcher::ValueMatcherCase::kEqInt: {
+                    auto it = intMap.find(key);
+                    if (it == intMap.end() || cur.eq_int() != it->second) {
+                        allMatched = false;
+                    }
+                    break;
+                }
+                case KeyValueMatcher::ValueMatcherCase::kEqBool: {
+                    auto it = boolMap.find(key);
+                    if (it == boolMap.end() || cur.eq_bool() != it->second) {
+                        allMatched = false;
+                    }
+                    break;
+                }
+                    // Begin numeric comparisons
+                case KeyValueMatcher::ValueMatcherCase::kLtInt: {
+                    auto it = intMap.find(key);
+                    if (it == intMap.end() || cur.lt_int() <= it->second) {
+                        allMatched = false;
+                    }
+                    break;
+                }
+                case KeyValueMatcher::ValueMatcherCase::kGtInt: {
+                    auto it = intMap.find(key);
+                    if (it == intMap.end() || cur.gt_int() >= it->second) {
+                        allMatched = false;
+                    }
+                    break;
+                }
+                case KeyValueMatcher::ValueMatcherCase::kLtFloat: {
+                    auto it = floatMap.find(key);
+                    if (it == floatMap.end() || cur.lt_float() <= it->second) {
+                        allMatched = false;
+                    }
+                    break;
+                }
+                case KeyValueMatcher::ValueMatcherCase::kGtFloat: {
+                    auto it = floatMap.find(key);
+                    if (it == floatMap.end() || cur.gt_float() >= it->second) {
+                        allMatched = false;
+                    }
+                    break;
+                }
+                // Begin comparisons with equality
+                case KeyValueMatcher::ValueMatcherCase::kLteInt: {
+                    auto it = intMap.find(key);
+                    if (it == intMap.end() || cur.lte_int() < it->second) {
+                        allMatched = false;
+                    }
+                    break;
+                }
+                case KeyValueMatcher::ValueMatcherCase::kGteInt: {
+                    auto it = intMap.find(key);
+                    if (it == intMap.end() || cur.gte_int() > it->second) {
+                        allMatched = false;
+                    }
+                    break;
+                }
+                case KeyValueMatcher::ValueMatcherCase::VALUE_MATCHER_NOT_SET:
+                    // If value matcher is not present, assume that we match.
+                    break;
+            }
+        }
+
+        if (allMatched) {
+            return true;
+        }
+    }
+    return false;
+}
+
+} // namespace statsd
+} // namespace os
+} // namespace android
diff --git a/cmds/statsd/src/matchers/LogEntryMatcherManager.h b/cmds/statsd/src/matchers/LogEntryMatcherManager.h
new file mode 100644
index 0000000..10ac0e2
--- /dev/null
+++ b/cmds/statsd/src/matchers/LogEntryMatcherManager.h
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#ifndef LOG_ENTRY_MATCHER_MANAGER_H
+#define LOG_ENTRY_MATCHER_MANAGER_H
+
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include <log/logprint.h>
+#include <log/log_read.h>
+#include <set>
+#include <vector>
+#include <unordered_map>
+
+using std::unordered_map;
+using std::string;
+
+namespace android {
+namespace os {
+namespace statsd {
+
+/**
+ * Keeps track per log entry which simple log entry matchers match.
+ */
+class LogEntryMatcherManager {
+public:
+    LogEntryMatcherManager();
+
+    ~LogEntryMatcherManager() {};
+
+    static bool matches(const LogEntryMatcher &matcher, const int tagId,
+                        const unordered_map<int, long> &intMap,
+                        const unordered_map<int, string> &strMap,
+                        const unordered_map<int, float> &floatMap,
+                        const unordered_map<int, bool> &boolMap);
+
+    static bool matchesSimple(const SimpleLogEntryMatcher &simpleMatcher,
+                              const int tagId,
+                              const unordered_map<int, long> &intMap,
+                              const unordered_map<int, string> &strMap,
+                              const unordered_map<int, float> &floatMap,
+                              const unordered_map<int, bool> &boolMap);
+};
+
+} // namespace statsd
+} // namespace os
+} // namespace android
+#endif //LOG_ENTRY_MATCHER_MANAGER_H
diff --git a/cmds/statsd/src/statsd_config.proto b/cmds/statsd/src/statsd_config.proto
index c6119df..c7f0305 100644
--- a/cmds/statsd/src/statsd_config.proto
+++ b/cmds/statsd/src/statsd_config.proto
@@ -19,14 +19,17 @@
   oneof value_matcher {
     bool eq_bool = 2;
     string eq_string = 3;
-    int32 eq_int32 = 4;
-    int64 eq_int64 = 5;
-    int32 lt_int32 = 6;
-    int32 gt_int32 = 7;
-    int64 lt_int64 = 8;
-    int64 gt_int64 = 9;
-    float lt_float = 10;
-    float gt_float = 11;
+    int32 eq_int = 4;
+
+    // Numeric comparisons. Lt means strictly less than.
+    int64 lt_int = 5;
+    int64 gt_int = 6;
+    float lt_float = 7;
+    float gt_float = 8;
+
+    // Numeric comparisons with equality. Lte means less than or equal.
+    int64 lte_int = 9;
+    int64 gte_int = 10;
   }
 }
 
diff --git a/cmds/statsd/tests/LogEntryMatcher_test.cpp b/cmds/statsd/tests/LogEntryMatcher_test.cpp
new file mode 100644
index 0000000..eb807ca
--- /dev/null
+++ b/cmds/statsd/tests/LogEntryMatcher_test.cpp
@@ -0,0 +1,366 @@
+// 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.
+
+#define LOG_TAG "statsd_test"
+
+#include "frameworks/base/cmds/statsd/src/statsd_config.pb.h"
+#include "../src/matchers/LogEntryMatcherManager.h"
+#include "../src/parse_util.h"
+#include <log/logprint.h>
+#include <log/log_read.h>
+#include <log/log_event_list.h>
+#include <gtest/gtest.h>
+
+#include <stdio.h>
+
+using namespace android::os::statsd;
+using std::unordered_map;
+
+#ifdef __ANDROID__
+TEST(LogEntryMatcherTest, TestSimpleMatcher) {
+    // Set up the matcher
+    LogEntryMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
+    simpleMatcher->add_tag(TagId::WAKELOCK);
+
+    unordered_map<int, long> intMap;
+    unordered_map<int, string> strMap;
+    unordered_map<int, float> floatMap;
+    unordered_map<int, bool> boolMap;
+
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap, boolMap));
+}
+
+TEST(LogEntryMatcherTest, TestBoolMatcher) {
+    // Set up the matcher
+    LogEntryMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
+    simpleMatcher->add_tag(TagId::WAKELOCK);
+    auto keyValue = simpleMatcher->add_key_value_matcher();
+    keyValue->mutable_key_matcher()->set_key(KeyId::STATE);
+
+
+    unordered_map<int, long> intMap;
+    unordered_map<int, string> strMap;
+    unordered_map<int, float> floatMap;
+    unordered_map<int, bool> boolMap;
+
+    keyValue->set_eq_bool(true);
+    boolMap[KeyId::STATE] = true;
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap,
+        boolMap));
+
+    keyValue->set_eq_bool(false);
+    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap,
+        floatMap, boolMap));
+
+    boolMap[TagId::WAKELOCK] = false;
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap,
+        boolMap));
+}
+
+TEST(LogEntryMatcherTest, TestStringMatcher) {
+    // Set up the matcher
+    LogEntryMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
+    simpleMatcher->add_tag(TagId::WAKELOCK);
+    auto keyValue = simpleMatcher->add_key_value_matcher();
+    keyValue->mutable_key_matcher()->set_key(KeyId::STATE);
+    keyValue->set_eq_string("wakelock_name");
+
+    unordered_map<int, long> intMap;
+    unordered_map<int, string> strMap;
+    unordered_map<int, float> floatMap;
+    unordered_map<int, bool> boolMap;
+
+    strMap[KeyId::STATE] = "wakelock_name";
+
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap, boolMap));
+}
+
+TEST(LogEntryMatcherTest, TestIntComparisonMatcher) {
+    // Set up the matcher
+    LogEntryMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
+    simpleMatcher->add_tag(TagId::WAKELOCK);
+    auto keyValue = simpleMatcher->add_key_value_matcher();
+    keyValue->mutable_key_matcher()->set_key(KeyId::STATE);
+
+    unordered_map<int, long> intMap;
+    unordered_map<int, string> strMap;
+    unordered_map<int, float> floatMap;
+    unordered_map<int, bool> boolMap;
+
+    keyValue->set_lt_int(10);
+    intMap[KeyId::STATE] = 11;
+    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap,
+        floatMap, boolMap));
+    intMap[KeyId::STATE] = 10;
+    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap,
+        floatMap, boolMap));
+    intMap[KeyId::STATE] = 9;
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap,
+        floatMap, boolMap));
+
+    keyValue->set_gt_int(10);
+    intMap[KeyId::STATE] = 11;
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap,
+        floatMap, boolMap));
+    intMap[KeyId::STATE] = 10;
+    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap,
+        floatMap, boolMap));
+    intMap[KeyId::STATE] = 9;
+    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap,
+        floatMap, boolMap));
+}
+
+TEST(LogEntryMatcherTest, TestIntWithEqualityComparisonMatcher) {
+    // Set up the matcher
+    LogEntryMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
+    simpleMatcher->add_tag(TagId::WAKELOCK);
+    auto keyValue = simpleMatcher->add_key_value_matcher();
+    keyValue->mutable_key_matcher()->set_key(KeyId::STATE);
+
+    unordered_map<int, long> intMap;
+    unordered_map<int, string> strMap;
+    unordered_map<int, float> floatMap;
+    unordered_map<int, bool> boolMap;
+
+    keyValue->set_lte_int(10);
+    intMap[KeyId::STATE] = 11;
+    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap,
+        floatMap, boolMap));
+    intMap[KeyId::STATE] = 10;
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap,
+        floatMap, boolMap));
+    intMap[KeyId::STATE] = 9;
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap,
+        floatMap, boolMap));
+
+    keyValue->set_gte_int(10);
+    intMap[KeyId::STATE] = 11;
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap,
+        floatMap, boolMap));
+    intMap[KeyId::STATE] = 10;
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap,
+        floatMap, boolMap));
+    intMap[KeyId::STATE] = 9;
+    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap,
+        floatMap, boolMap));
+}
+
+TEST(LogEntryMatcherTest, TestFloatComparisonMatcher) {
+    // Set up the matcher
+    LogEntryMatcher matcher;
+    auto simpleMatcher = matcher.mutable_simple_log_entry_matcher();
+    simpleMatcher->add_tag(TagId::WAKELOCK);
+    auto keyValue = simpleMatcher->add_key_value_matcher();
+    keyValue->mutable_key_matcher()->set_key(KeyId::STATE);
+
+    unordered_map<int, long> intMap;
+    unordered_map<int, string> strMap;
+    unordered_map<int, float> floatMap;
+    unordered_map<int, bool> boolMap;
+
+    keyValue->set_lt_float(10.0);
+    floatMap[KeyId::STATE] = 10.1;
+    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap,
+        floatMap, boolMap));
+    floatMap[KeyId::STATE] = 9.9;
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap,
+        floatMap, boolMap));
+
+    keyValue->set_gt_float(10.0);
+    floatMap[KeyId::STATE] = 10.1;
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap,
+        floatMap, boolMap));
+    floatMap[KeyId::STATE] = 9.9;
+    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap,
+        floatMap, boolMap));
+}
+
+// Helper for the composite matchers.
+void addSimpleMatcher(SimpleLogEntryMatcher* simpleMatcher, TagId tag, KeyId key, int val) {
+    simpleMatcher->add_tag(tag);
+    auto keyValue = simpleMatcher->add_key_value_matcher();
+    keyValue->mutable_key_matcher()->set_key(key);
+    keyValue->set_eq_int(val);
+}
+
+TEST(LogEntryMatcherTest, TestAndMatcher) {
+    // Set up the matcher
+    LogEntryMatcher matcher;
+    auto combination = matcher.mutable_combination();
+    combination->set_operation(LogicalOperation::AND);
+
+    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(), TagId::WAKELOCK, KeyId::STATE, 3);
+    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(), TagId::WAKELOCK, KeyId::PACKAGE_VERSION, 4);
+
+    unordered_map<int, long> intMap;
+    unordered_map<int, string> strMap;
+    unordered_map<int, float> floatMap;
+    unordered_map<int, bool> boolMap;
+
+    intMap[1003] = 4;
+    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap, boolMap));
+    intMap.clear();
+    intMap[1] = 3;
+    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap, boolMap));
+    intMap.clear();
+    intMap[1] = 3;
+    intMap[1003] = 4;
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap, boolMap));
+}
+
+TEST(LogEntryMatcherTest, TestOrMatcher) {
+    // Set up the matcher
+    LogEntryMatcher matcher;
+    auto combination = matcher.mutable_combination();
+    combination->set_operation(LogicalOperation::OR);
+
+    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
+        TagId::WAKELOCK, KeyId::STATE, 3);
+    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
+        TagId::WAKELOCK, KeyId::PACKAGE_VERSION, 4);
+
+    unordered_map<int, long> intMap;
+    unordered_map<int, string> strMap;
+    unordered_map<int, float> floatMap;
+    unordered_map<int, bool> boolMap;
+
+    // Don't set any key-value pairs.
+    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap,
+        boolMap));
+    intMap[1003] = 4;
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap,
+        boolMap));
+    intMap.clear();
+    intMap[1] = 3;
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap,
+        boolMap));
+    intMap.clear();
+    intMap[1] = 3;
+    intMap[1003] = 4;
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap,
+        boolMap));
+}
+
+TEST(LogEntryMatcherTest, TestNotMatcher) {
+    // Set up the matcher
+    LogEntryMatcher matcher;
+    auto combination = matcher.mutable_combination();
+    combination->set_operation(LogicalOperation::NOT);
+
+    // Define first simpleMatcher
+    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
+        TagId::WAKELOCK, KeyId::STATE, 3);
+
+    unordered_map<int, long> intMap;
+    unordered_map<int, string> strMap;
+    unordered_map<int, float> floatMap;
+    unordered_map<int, bool> boolMap;
+
+    // Don't set any key-value pairs.
+    intMap[KeyId::STATE] = 3;
+    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap,
+        boolMap));
+}
+
+TEST(LogEntryMatcherTest, TestNANDMatcher) {
+    // Set up the matcher
+    LogEntryMatcher matcher;
+    auto combination = matcher.mutable_combination();
+    combination->set_operation(LogicalOperation::NAND);
+
+    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
+        TagId::WAKELOCK, KeyId::STATE, 3);
+    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
+        TagId::WAKELOCK, KeyId::PACKAGE_VERSION, 4);
+
+    unordered_map<int, long> intMap;
+    unordered_map<int, string> strMap;
+    unordered_map<int, float> floatMap;
+    unordered_map<int, bool> boolMap;
+
+    // Don't set any key-value pairs.
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap,
+        boolMap));
+    intMap[KeyId::STATE] = 3;
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap,
+        boolMap));
+    intMap[KeyId::PACKAGE_VERSION] = 4;
+    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap,
+        boolMap));
+}
+
+TEST(LogEntryMatcherTest, TestNORMatcher) {
+    // Set up the matcher
+    LogEntryMatcher matcher;
+    auto combination = matcher.mutable_combination();
+    combination->set_operation(LogicalOperation::NOR);
+
+    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
+        TagId::WAKELOCK, KeyId::STATE, 3);
+    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
+        TagId::WAKELOCK, KeyId::PACKAGE_VERSION, 4);
+
+    unordered_map<int, long> intMap;
+    unordered_map<int, string> strMap;
+    unordered_map<int, float> floatMap;
+    unordered_map<int, bool> boolMap;
+
+    // Don't set any key-value pairs.
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap,
+        boolMap));
+    intMap[KeyId::STATE] = 3;
+    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap,
+        boolMap));
+    intMap[KeyId::PACKAGE_VERSION] = 4;
+    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap,
+        boolMap));
+}
+
+// Tests that a NOT on top of AND is the same as NAND
+TEST(LogEntryMatcherTest, TestMultipleLayerMatcher) {
+    LogEntryMatcher matcher;
+    auto not_combination = matcher.mutable_combination();
+    not_combination->set_operation(LogicalOperation::NOT);
+
+    // Now add the AND
+    auto combination = not_combination->add_matcher()->mutable_combination();
+    combination->set_operation(LogicalOperation::AND);
+    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
+        TagId::WAKELOCK, KeyId::STATE, 3);
+    addSimpleMatcher(combination->add_matcher()->mutable_simple_log_entry_matcher(),
+        TagId::WAKELOCK, KeyId::PACKAGE_VERSION, 4);
+
+    unordered_map<int, long> intMap;
+    unordered_map<int, string> strMap;
+    unordered_map<int, float> floatMap;
+    unordered_map<int, bool> boolMap;
+
+    // Don't set any key-value pairs.
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap,
+        boolMap));
+    intMap[KeyId::STATE] = 3;
+    EXPECT_TRUE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap,
+        boolMap));
+    intMap[KeyId::PACKAGE_VERSION] = 4;
+    EXPECT_FALSE(LogEntryMatcherManager::matches(matcher, TagId::WAKELOCK, intMap, strMap, floatMap,
+        boolMap));
+}
+#else
+            GTEST_LOG_(INFO) << "This test does nothing.\n";
+#endif