Add .filter option to event queries.

Test: atest EventLibTest
Bug: 170458200
Change-Id: Ia88323722ef5d4275096439eb5f8da5bbd684b35
diff --git a/common/device-side/eventlib/Android.bp b/common/device-side/eventlib/Android.bp
index a98bdaa..03add1f 100644
--- a/common/device-side/eventlib/Android.bp
+++ b/common/device-side/eventlib/Android.bp
@@ -14,6 +14,9 @@
     srcs: [
         "src/test/java/**/*.java"
     ],
+    test_suites: [
+        "general-tests",
+    ],
     static_libs: ["EventLib", "androidx.test.ext.junit", "ctstestrunner-axt", "truth-prebuilt"],
     manifest: "src/test/AndroidManifest.xml",
 }
\ No newline at end of file
diff --git a/common/device-side/eventlib/TEST_MAPPING b/common/device-side/eventlib/TEST_MAPPING
new file mode 100644
index 0000000..c19ccac
--- /dev/null
+++ b/common/device-side/eventlib/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+  "presubmit": [
+    {
+      "name": "EventLibTest"
+    }
+  ]
+}
diff --git a/common/device-side/eventlib/src/main/java/com/android/eventlib/EventLogs.java b/common/device-side/eventlib/src/main/java/com/android/eventlib/EventLogs.java
index 29cfd5b..cd2c766 100644
--- a/common/device-side/eventlib/src/main/java/com/android/eventlib/EventLogs.java
+++ b/common/device-side/eventlib/src/main/java/com/android/eventlib/EventLogs.java
@@ -27,12 +27,6 @@
 
     private static Instant sEarliestLogTime = null;
 
-    /** Returns the specific implementation of {@link Event} being queried for. */
-    protected abstract Class<E> eventClass();
-
-    /** Returns true if {@code E} matches the custom filters for this {@link Event} subclass. */
-    protected abstract boolean filter(E event);
-
     /**
      * Returns the {@link EventQuerier} to be used to interact with the
      * appropriate {@link Event} store.
diff --git a/common/device-side/eventlib/src/main/java/com/android/eventlib/EventLogsQuery.java b/common/device-side/eventlib/src/main/java/com/android/eventlib/EventLogsQuery.java
index fc16085..dad7a6e 100644
--- a/common/device-side/eventlib/src/main/java/com/android/eventlib/EventLogsQuery.java
+++ b/common/device-side/eventlib/src/main/java/com/android/eventlib/EventLogsQuery.java
@@ -20,6 +20,10 @@
 
 import androidx.test.platform.app.InstrumentationRegistry;
 
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Function;
+
 /**
  * Interface to provide additional restrictions on an {@link Event} query.
  */
@@ -47,6 +51,7 @@
 
     private final Class<E> mEventClass;
     private final String mPackageName;
+    private final Set<Function<E, Boolean>> filters = new HashSet<>();
 
     protected EventLogsQuery(Class<E> eventClass, String packageName) {
         if (eventClass == null || packageName == null) {
@@ -64,7 +69,6 @@
         return mPackageName;
     }
 
-    @Override
     protected Class<E> eventClass() {
         return mEventClass;
     }
@@ -76,4 +80,24 @@
     protected EventQuerier<E> getQuerier() {
         return mQuerier;
     }
+
+    public F filter(Function<E, Boolean> filter) {
+        filters.add(filter);
+        return (F) this;
+    }
+
+    /**
+     * Returns true if {@code E} matches custom and default filters for this {@link Event} subclass.
+     */
+    protected final boolean filterAll(E event) {
+        for (Function<E, Boolean> filter : filters) {
+            if (!filter.apply(event)) {
+                return false;
+            }
+        }
+        return filter(event);
+    }
+
+    /** Returns true if {@code E} matches the custom filters for this {@link Event} subclass. */
+    protected abstract boolean filter(E event);
 }
diff --git a/common/device-side/eventlib/src/main/java/com/android/eventlib/LocalEventQuerier.java b/common/device-side/eventlib/src/main/java/com/android/eventlib/LocalEventQuerier.java
index 8780563..b98d250 100644
--- a/common/device-side/eventlib/src/main/java/com/android/eventlib/LocalEventQuerier.java
+++ b/common/device-side/eventlib/src/main/java/com/android/eventlib/LocalEventQuerier.java
@@ -28,12 +28,12 @@
 /**
  * Implementation of {@link EventQuerier} which queries data about the current package.
  */
-public class LocalEventQuerier<E extends Event> implements EventQuerier<E>, Events.EventListener {
-    private final EventLogs<E> mEventLogs;
+public class LocalEventQuerier<E extends Event, F extends EventLogsQuery> implements EventQuerier<E>, Events.EventListener {
+    private final EventLogsQuery<E, F> mEventLogsQuery;
     private final BlockingDeque<Event> mEvents;
 
-    LocalEventQuerier(EventLogs<E> eventLogs) {
-        mEventLogs = eventLogs;
+    LocalEventQuerier(EventLogsQuery<E, F> eventLogsQuery) {
+        mEventLogsQuery = eventLogsQuery;
         mEvents = new LinkedBlockingDeque<>(Events.EVENTS.getEvents());
         Events.EVENTS.registerEventListener(this);
     }
@@ -41,13 +41,13 @@
     @Override
     public E get(Instant earliestLogTime) {
         for (Event event : Events.EVENTS.getEvents()) {
-            if (mEventLogs.eventClass().isInstance(event)) {
+            if (mEventLogsQuery.eventClass().isInstance(event)) {
                 if (event.mTimestamp.isBefore(earliestLogTime)) {
                     continue;
                 }
 
                 E typedEvent = (E) event;
-                if (mEventLogs.filter(typedEvent)) {
+                if (mEventLogsQuery.filterAll(typedEvent)) {
                     return typedEvent;
                 }
             }
@@ -60,13 +60,13 @@
         while (!mEvents.isEmpty()) {
             Event event = mEvents.removeFirst();
 
-            if (mEventLogs.eventClass().isInstance(event)) {
+            if (mEventLogsQuery.eventClass().isInstance(event)) {
                 if (event.mTimestamp.isBefore(earliestLogTime)) {
                     continue;
                 }
 
                 E typedEvent = (E) event;
-                if (mEventLogs.filter(typedEvent)) {
+                if (mEventLogsQuery.filterAll(typedEvent)) {
                     return typedEvent;
                 }
             }
@@ -92,13 +92,13 @@
                 return null;
             }
 
-            if (mEventLogs.eventClass().isInstance(event)) {
+            if (mEventLogsQuery.eventClass().isInstance(event)) {
                 if (event.mTimestamp.isBefore(earliestLogTime)) {
                     continue;
                 }
 
                 E typedEvent = (E) event;
-                if (mEventLogs.filter(typedEvent)) {
+                if (mEventLogsQuery.filterAll(typedEvent)) {
                     return typedEvent;
                 }
             }
diff --git a/common/device-side/eventlib/src/test/java/com/android/eventlib/EventLogsTest.java b/common/device-side/eventlib/src/test/java/com/android/eventlib/EventLogsTest.java
index afc9ecd..7c5361e 100644
--- a/common/device-side/eventlib/src/test/java/com/android/eventlib/EventLogsTest.java
+++ b/common/device-side/eventlib/src/test/java/com/android/eventlib/EventLogsTest.java
@@ -417,6 +417,121 @@
         assertThat(eventLogs1.poll()).isEqualTo(eventLogs2.poll());
     }
 
+    @Test
+    public void get_obeysLambdaFilter() {
+        CustomEvent.logger(CONTEXT)
+                .setTag(TEST_TAG1)
+                .log();
+        CustomEvent.logger(CONTEXT)
+                .setTag(TEST_TAG2)
+                .log();
+
+        EventLogs<CustomEvent> eventLogs = CustomEvent.queryPackage(CONTEXT.getPackageName())
+                .filter(e -> TEST_TAG2.equals(e.tag()));
+
+        assertThat(eventLogs.get().tag()).isEqualTo(TEST_TAG2);
+    }
+
+    @Test
+    public void poll_obeysLambdaFilter() {
+        CustomEvent.logger(CONTEXT)
+                .setTag(TEST_TAG1)
+                .log();
+        CustomEvent.logger(CONTEXT)
+                .setTag(TEST_TAG2)
+                .log();
+
+        EventLogs<CustomEvent> eventLogs = CustomEvent.queryPackage(CONTEXT.getPackageName())
+                .filter(e -> TEST_TAG2.equals(e.tag()));
+
+        assertThat(eventLogs.poll().tag()).isEqualTo(TEST_TAG2);
+        assertThat(eventLogs.poll(VERY_SHORT_POLL_WAIT)).isNull();
+    }
+
+    @Test
+    public void next_obeysLambdaFilter() {
+        CustomEvent.logger(CONTEXT)
+                .setTag(TEST_TAG1)
+                .log();
+        CustomEvent.logger(CONTEXT)
+                .setTag(TEST_TAG2)
+                .log();
+
+        EventLogs<CustomEvent> eventLogs = CustomEvent.queryPackage(CONTEXT.getPackageName())
+                .filter(e -> TEST_TAG2.equals(e.tag()));
+
+        assertThat(eventLogs.next().tag()).isEqualTo(TEST_TAG2);
+        assertThat(eventLogs.next()).isNull();
+    }
+
+    @Test
+    public void get_obeysMultipleLambdaFilters() {
+        CustomEvent.logger(CONTEXT)
+                .setTag(TEST_TAG1)
+                .log();
+        CustomEvent.logger(CONTEXT)
+                .setTag(TEST_TAG2)
+                .log();
+        CustomEvent.logger(CONTEXT)
+                .setTag(TEST_TAG2)
+                .setData(DATA_1)
+                .log();
+
+        EventLogs<CustomEvent> eventLogs = CustomEvent.queryPackage(CONTEXT.getPackageName())
+                .filter(e -> TEST_TAG2.equals(e.tag()))
+                .filter(e -> DATA_1.equals(e.data()));
+
+        CustomEvent event = eventLogs.get();
+        assertThat(event.tag()).isEqualTo(TEST_TAG2);
+        assertThat(event.data()).isEqualTo(DATA_1);
+    }
+
+    @Test
+    public void poll_obeysMultipleLambdaFilters() {
+        CustomEvent.logger(CONTEXT)
+                .setTag(TEST_TAG1)
+                .log();
+        CustomEvent.logger(CONTEXT)
+                .setTag(TEST_TAG2)
+                .log();
+        CustomEvent.logger(CONTEXT)
+                .setTag(TEST_TAG2)
+                .setData(DATA_1)
+                .log();
+
+        EventLogs<CustomEvent> eventLogs = CustomEvent.queryPackage(CONTEXT.getPackageName())
+                .filter(e -> TEST_TAG2.equals(e.tag()))
+                .filter(e -> DATA_1.equals(e.data()));
+
+        CustomEvent event = eventLogs.poll();
+        assertThat(event.tag()).isEqualTo(TEST_TAG2);
+        assertThat(event.data()).isEqualTo(DATA_1);
+        assertThat(eventLogs.poll(VERY_SHORT_POLL_WAIT)).isNull();
+    }
+
+    @Test
+    public void next_obeysMultipleLambdaFilters() {
+        CustomEvent.logger(CONTEXT)
+                .setTag(TEST_TAG1)
+                .log();
+        CustomEvent.logger(CONTEXT)
+                .setTag(TEST_TAG2)
+                .log();
+        CustomEvent.logger(CONTEXT)
+                .setTag(TEST_TAG2)
+                .setData(DATA_1)
+                .log();
+
+        EventLogs<CustomEvent> eventLogs = CustomEvent.queryPackage(CONTEXT.getPackageName())
+                .filter(e -> TEST_TAG2.equals(e.tag()))
+                .filter(e -> DATA_1.equals(e.data()));
+
+        CustomEvent event = eventLogs.next();
+        assertThat(event.tag()).isEqualTo(TEST_TAG2);
+        assertThat(event.data()).isEqualTo(DATA_1);
+        assertThat(eventLogs.next()).isNull();
+    }
+
     private void scheduleCustomEventInOneSecond() {
         hasScheduledEvents = true;
         new Handler(Looper.getMainLooper()).postDelayed(() ->