Fully delete duplicate entries inside LatencyTracker

Inside LatencyTracker, we use 2 collections that must always be in sync:
mTimelines and mEventTimes.

However, when duplicate input events are encountered, we do not treat
these collections equally. In one of them, we fully remove all data
associated with the duplicate inputEventId. In the other one, however,
we only remove the entry that matches both the inputEventId and the
eventTime. This means that the two collections will get out of sync,
because the entry with the mismatching eventTime will be kept in
mEventTimes.

To fix this, fully remove all entries with the duplicate inputEventId,
no matter what the eventTime is. Unfortunately, it means that we have to
traverse the entire collection of mEventTimes. However, since this event
should not occur often, it should not impact performance.

This issue was discovered via the use of the newly added fuzzer for
LatencyTracker. The fuzzer would hit this condition in about 2 seconds
after the run has started. This issue could have been identified the
fuzzer was in place from the start.

The removal of api 'reportNow':
The api 'reportNow' was added for convenience of writing tests. However,
this api has reduced the code coverage, since we would now short-circuit
the code around 'trackListener' that was responsible for event
reporting. This is why, when the issue was first being worked on, the
tests for duplicate events did not yield any crashes. To avoid such
problems in the future, fully remove this api and replace with a
'triggerEventReporting' method that's defined in the tests. This way,
the api's that are used by production code and by tests are identical.

Bug: 169866723
Test: m inputflinger_latencytracker_fuzzer && adb sync data && adb shell /data/fuzz/arm64/inputflinger_latencytracker_fuzzer/inputflinger_latencytracker_fuzzer
Test: atest inputflinger_tests:LatencyTrackerTest
Change-Id: I04069cf2c9eb1154e3324b25c8c4bdff32d84d3f
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index d8af48e..d16c991 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -3863,6 +3863,13 @@
                                               args->downTime, args->pointerCount,
                                               args->pointerProperties, args->pointerCoords, 0, 0);
 
+        if (args->id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
+            IdGenerator::getSource(args->id) == IdGenerator::Source::INPUT_READER &&
+            !mInputFilterEnabled) {
+            const bool isDown = args->action == AMOTION_EVENT_ACTION_DOWN;
+            mLatencyTracker.trackListener(args->id, isDown, args->eventTime, args->readTime);
+        }
+
         needWake = enqueueInboundEventLocked(std::move(newEntry));
         mLock.unlock();
     } // release lock
diff --git a/services/inputflinger/dispatcher/LatencyTracker.cpp b/services/inputflinger/dispatcher/LatencyTracker.cpp
index d634dcd..52f189c 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.cpp
+++ b/services/inputflinger/dispatcher/LatencyTracker.cpp
@@ -50,13 +50,12 @@
  * key-value pair. Equivalent to the imaginary std api std::multimap::erase(key, value).
  */
 template <typename K, typename V>
-static void eraseByKeyAndValue(std::multimap<K, V>& map, K key, V value) {
-    auto iterpair = map.equal_range(key);
-
-    for (auto it = iterpair.first; it != iterpair.second; ++it) {
+static void eraseByValue(std::multimap<K, V>& map, const V& value) {
+    for (auto it = map.begin(); it != map.end();) {
         if (it->second == value) {
-            map.erase(it);
-            break;
+            it = map.erase(it);
+        } else {
+            it++;
         }
     }
 }
@@ -76,9 +75,7 @@
         // confuse us by reporting the rest of the timeline for one of them. This should happen
         // rarely, so we won't lose much data
         mTimelines.erase(it);
-        // In case we have another input event with a different id and at the same eventTime,
-        // only erase this specific inputEventId.
-        eraseByKeyAndValue(mEventTimes, eventTime, inputEventId);
+        eraseByValue(mEventTimes, inputEventId);
         return;
     }
     mTimelines.emplace(inputEventId, InputEventTimeline(isDown, eventTime, readTime));
@@ -90,7 +87,8 @@
                                         nsecs_t finishTime) {
     const auto it = mTimelines.find(inputEventId);
     if (it == mTimelines.end()) {
-        // It's possible that an app sends a bad (or late)'Finish' signal, since it's free to do
+        // This could happen if we erased this event when duplicate events were detected. It's
+        // also possible that an app sent a bad (or late) 'Finish' signal, since it's free to do
         // anything in its process. Just drop the report and move on.
         return;
     }
@@ -120,7 +118,8 @@
         std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline) {
     const auto it = mTimelines.find(inputEventId);
     if (it == mTimelines.end()) {
-        // It's possible that an app sends a bad (or late) 'Timeline' signal, since it's free to do
+        // This could happen if we erased this event when duplicate events were detected. It's
+        // also possible that an app sent a bad (or late) 'Timeline' signal, since it's free to do
         // anything in its process. Just drop the report and move on.
         return;
     }
@@ -166,14 +165,6 @@
     }
 }
 
-void LatencyTracker::reportNow() {
-    for (const auto& [inputEventId, timeline] : mTimelines) {
-        mTimelineProcessor->processTimeline(timeline);
-    }
-    mTimelines.clear();
-    mEventTimes.clear();
-}
-
 std::string LatencyTracker::dump(const char* prefix) {
     return StringPrintf("%sLatencyTracker:\n", prefix) +
             StringPrintf("%s  mTimelines.size() = %zu\n", prefix, mTimelines.size()) +
diff --git a/services/inputflinger/dispatcher/LatencyTracker.h b/services/inputflinger/dispatcher/LatencyTracker.h
index 289b8ed..4b0c618 100644
--- a/services/inputflinger/dispatcher/LatencyTracker.h
+++ b/services/inputflinger/dispatcher/LatencyTracker.h
@@ -43,6 +43,12 @@
     LatencyTracker(InputEventTimelineProcessor* processor);
     /**
      * Start keeping track of an event identified by inputEventId. This must be called first.
+     * If duplicate events are encountered (events that have the same eventId), none of them will be
+     * tracked. This is because there is not enough information to correctly track them. The api's
+     * 'trackFinishedEvent' and 'trackGraphicsLatency' only contain the inputEventId, and not the
+     * eventTime. Even if eventTime was provided, there would still be a possibility of having
+     * duplicate events that happen to have the same eventTime and inputEventId. Therefore, we
+     * must drop all duplicate data.
      */
     void trackListener(int32_t inputEventId, bool isDown, nsecs_t eventTime, nsecs_t readTime);
     void trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& connectionToken,
@@ -50,14 +56,6 @@
     void trackGraphicsLatency(int32_t inputEventId, const sp<IBinder>& connectionToken,
                               std::array<nsecs_t, GraphicsTimeline::SIZE> timeline);
 
-    /**
-     * Report all collected events immediately, even if some of them are currently incomplete
-     * and may receive 'trackFinishedEvent' or 'trackGraphicsLatency' calls in the future.
-     * This is useful for tests. Otherwise, tests would have to inject additional "future" events,
-     * which is not convenient.
-     */
-    void reportNow();
-
     std::string dump(const char* prefix);
 
 private:
diff --git a/services/inputflinger/tests/LatencyTracker_test.cpp b/services/inputflinger/tests/LatencyTracker_test.cpp
index e7e1937..89c0741 100644
--- a/services/inputflinger/tests/LatencyTracker_test.cpp
+++ b/services/inputflinger/tests/LatencyTracker_test.cpp
@@ -16,6 +16,7 @@
 
 #include "../dispatcher/LatencyTracker.h"
 
+#include <android-base/properties.h>
 #include <binder/Binder.h>
 #include <gtest/gtest.h>
 #include <inttypes.h>
@@ -23,11 +24,16 @@
 
 #define TAG "LatencyTracker_test"
 
+using android::base::HwTimeoutMultiplier;
 using android::inputdispatcher::InputEventTimeline;
 using android::inputdispatcher::LatencyTracker;
 
 namespace android::inputdispatcher {
 
+const std::chrono::duration ANR_TIMEOUT = std::chrono::milliseconds(
+        android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
+        HwTimeoutMultiplier());
+
 InputEventTimeline getTestTimeline() {
     InputEventTimeline t(
             /*isDown*/ true,
@@ -57,6 +63,8 @@
     }
     void TearDown() override {}
 
+    void triggerEventReporting(nsecs_t lastEventTime);
+
     void assertReceivedTimeline(const InputEventTimeline& timeline);
     /**
      * Timelines can be received in any order (order is not guaranteed). So if we are expecting more
@@ -72,8 +80,17 @@
     std::deque<InputEventTimeline> mReceivedTimelines;
 };
 
+/**
+ * Send an event that would trigger the reporting of all of the events that are at least as old as
+ * the provided 'lastEventTime'.
+ */
+void LatencyTrackerTest::triggerEventReporting(nsecs_t lastEventTime) {
+    const nsecs_t triggerEventTime =
+            lastEventTime + std::chrono::nanoseconds(ANR_TIMEOUT).count() + 1;
+    mTracker->trackListener(1 /*inputEventId*/, true /*isDown*/, triggerEventTime, 3 /*readTime*/);
+}
+
 void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& timeline) {
-    mTracker->reportNow();
     ASSERT_FALSE(mReceivedTimelines.empty());
     const InputEventTimeline& t = mReceivedTimelines.front();
     ASSERT_EQ(timeline, t);
@@ -88,7 +105,6 @@
  * equal element in B, and for every element in B there is an equal element in A.
  */
 void LatencyTrackerTest::assertReceivedTimelines(const std::vector<InputEventTimeline>& timelines) {
-    mTracker->reportNow();
     ASSERT_EQ(timelines.size(), mReceivedTimelines.size());
     for (const InputEventTimeline& expectedTimeline : timelines) {
         bool found = false;
@@ -121,6 +137,7 @@
  */
 TEST_F(LatencyTrackerTest, TrackListener_DoesNotTriggerReporting) {
     mTracker->trackListener(1 /*inputEventId*/, false /*isDown*/, 2 /*eventTime*/, 3 /*readTime*/);
+    triggerEventReporting(2 /*eventTime*/);
     assertReceivedTimeline(InputEventTimeline{false, 2, 3});
 }
 
@@ -130,6 +147,7 @@
 TEST_F(LatencyTrackerTest, TrackFinishedEvent_DoesNotTriggerReporting) {
     mTracker->trackFinishedEvent(1 /*inputEventId*/, connection1, 2 /*deliveryTime*/,
                                  3 /*consumeTime*/, 4 /*finishTime*/);
+    triggerEventReporting(4 /*eventTime*/);
     assertReceivedTimelines({});
 }
 
@@ -141,6 +159,7 @@
     graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME] = 2;
     graphicsTimeline[GraphicsTimeline::PRESENT_TIME] = 3;
     mTracker->trackGraphicsLatency(1 /*inputEventId*/, connection2, graphicsTimeline);
+    triggerEventReporting(3 /*eventTime*/);
     assertReceivedTimelines({});
 }
 
@@ -155,9 +174,30 @@
                                  expectedCT.consumeTime, expectedCT.finishTime);
     mTracker->trackGraphicsLatency(inputEventId, connectionToken, expectedCT.graphicsTimeline);
 
+    triggerEventReporting(expected.eventTime);
     assertReceivedTimeline(expected);
 }
 
+/**
+ * Send 2 events with the same inputEventId, but different eventTime's. Ensure that no crash occurs,
+ * and that the tracker drops such events completely.
+ */
+TEST_F(LatencyTrackerTest, WhenDuplicateEventsAreReported_DoesNotCrash) {
+    constexpr nsecs_t inputEventId = 1;
+    constexpr nsecs_t readTime = 3; // does not matter for this test
+    constexpr bool isDown = true;   // does not matter for this test
+
+    // In the following 2 calls to trackListener, the inputEventId's are the same, but event times
+    // are different.
+    mTracker->trackListener(inputEventId, isDown, 1 /*eventTime*/, readTime);
+    mTracker->trackListener(inputEventId, isDown, 2 /*eventTime*/, readTime);
+
+    triggerEventReporting(2 /*eventTime*/);
+    // Since we sent duplicate input events, the tracker should just delete all of them, because it
+    // does not have enough information to properly track them.
+    assertReceivedTimelines({});
+}
+
 TEST_F(LatencyTrackerTest, MultipleEvents_AreReportedConsistently) {
     constexpr int32_t inputEventId1 = 1;
     InputEventTimeline timeline1(
@@ -204,6 +244,7 @@
     mTracker->trackGraphicsLatency(inputEventId2, connection2,
                                    connectionTimeline2.graphicsTimeline);
     // Now both events should be completed
+    triggerEventReporting(timeline2.eventTime);
     assertReceivedTimelines({timeline1, timeline2});
 }
 
@@ -228,6 +269,7 @@
     mTracker->trackGraphicsLatency(1 /*inputEventId*/, token, expectedCT.graphicsTimeline);
 
     expectedTimelines[0].connectionTimelines.emplace(token, std::move(expectedCT));
+    triggerEventReporting(timeline.eventTime);
     assertReceivedTimelines(expectedTimelines);
 }
 
@@ -246,6 +288,7 @@
     mTracker->trackGraphicsLatency(inputEventId, connection1, expectedCT.graphicsTimeline);
 
     mTracker->trackListener(inputEventId, expected.isDown, expected.eventTime, expected.readTime);
+    triggerEventReporting(expected.eventTime);
     assertReceivedTimeline(
             InputEventTimeline{expected.isDown, expected.eventTime, expected.readTime});
 }
diff --git a/services/inputflinger/tests/fuzzers/Android.bp b/services/inputflinger/tests/fuzzers/Android.bp
new file mode 100644
index 0000000..df4db19
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/Android.bp
@@ -0,0 +1,45 @@
+// Copyright (C) 2021 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.
+
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "frameworks_native_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["frameworks_native_license"],
+}
+
+
+cc_fuzz {
+    name: "inputflinger_latencytracker_fuzzer",
+    defaults: [
+        "inputflinger_defaults",
+    ],
+    include_dirs: [
+        "frameworks/native/services/inputflinger",
+    ],
+    shared_libs: [
+        "libbase",
+        "libbinder",
+        "liblog",
+        "libui",
+        "libutils",
+        "libinput",
+        "libinputflinger",
+    ],
+    srcs: [
+        "LatencyTrackerFuzzer.cpp",
+    ],
+}
diff --git a/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
new file mode 100644
index 0000000..9f756eb
--- /dev/null
+++ b/services/inputflinger/tests/fuzzers/LatencyTrackerFuzzer.cpp
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2021 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 <fuzzer/FuzzedDataProvider.h>
+#include "dispatcher/LatencyTracker.h"
+
+namespace android {
+
+namespace inputdispatcher {
+
+/**
+ * A processor of InputEventTimelines that does nothing with the provided data.
+ */
+class EmptyProcessor : public InputEventTimelineProcessor {
+public:
+    /**
+     * Just ignore the provided timeline
+     */
+    void processTimeline(const InputEventTimeline& timeline) override {
+        for (const auto& [token, connectionTimeline] : timeline.connectionTimelines) {
+            connectionTimeline.isComplete();
+        }
+    };
+};
+
+static sp<IBinder> getConnectionToken(FuzzedDataProvider& fdp,
+                                      std::array<sp<IBinder>, 10>& tokens) {
+    const bool useExistingToken = fdp.ConsumeBool();
+    if (useExistingToken) {
+        return tokens[fdp.ConsumeIntegralInRange(0ul, tokens.size() - 1)];
+    }
+    return new BBinder();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
+    FuzzedDataProvider fdp(data, size);
+
+    EmptyProcessor emptyProcessor;
+    LatencyTracker tracker(&emptyProcessor);
+
+    // Make some pre-defined tokens to ensure that some timelines are complete.
+    std::array<sp<IBinder> /*token*/, 10> predefinedTokens;
+    for (size_t i = 0; i < predefinedTokens.size(); i++) {
+        predefinedTokens[i] = new BBinder();
+    }
+
+    // Randomly invoke LatencyTracker api's until randomness is exhausted.
+    while (fdp.remaining_bytes() > 0) {
+        fdp.PickValueInArray<std::function<void()>>({
+                [&]() -> void {
+                    int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
+                    int32_t isDown = fdp.ConsumeBool();
+                    nsecs_t eventTime = fdp.ConsumeIntegral<nsecs_t>();
+                    nsecs_t readTime = fdp.ConsumeIntegral<nsecs_t>();
+                    tracker.trackListener(inputEventId, isDown, eventTime, readTime);
+                },
+                [&]() -> void {
+                    int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
+                    sp<IBinder> connectionToken = getConnectionToken(fdp, predefinedTokens);
+                    nsecs_t deliveryTime = fdp.ConsumeIntegral<nsecs_t>();
+                    nsecs_t consumeTime = fdp.ConsumeIntegral<nsecs_t>();
+                    nsecs_t finishTime = fdp.ConsumeIntegral<nsecs_t>();
+                    tracker.trackFinishedEvent(inputEventId, connectionToken, deliveryTime,
+                                               consumeTime, finishTime);
+                },
+                [&]() -> void {
+                    int32_t inputEventId = fdp.ConsumeIntegral<int32_t>();
+                    sp<IBinder> connectionToken = getConnectionToken(fdp, predefinedTokens);
+                    std::array<nsecs_t, GraphicsTimeline::SIZE> graphicsTimeline;
+                    for (size_t i = 0; i < graphicsTimeline.size(); i++) {
+                        graphicsTimeline[i] = fdp.ConsumeIntegral<nsecs_t>();
+                    }
+                    tracker.trackGraphicsLatency(inputEventId, connectionToken, graphicsTimeline);
+                },
+        })();
+    }
+
+    return 0;
+}
+
+} // namespace inputdispatcher
+
+} // namespace android
\ No newline at end of file