diff options
6 files changed, 209 insertions, 29 deletions
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp index a51978627f..c2a2794eb9 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -3899,6 +3899,13 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs* args) { 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 d634dcda1d..52f189c5c5 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.cpp +++ b/services/inputflinger/dispatcher/LatencyTracker.cpp @@ -50,13 +50,12 @@ static bool isMatureEvent(nsecs_t eventTime, nsecs_t now) { * 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 @@ void LatencyTracker::trackListener(int32_t inputEventId, bool isDown, nsecs_t ev // 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 @@ void LatencyTracker::trackFinishedEvent(int32_t inputEventId, const sp<IBinder>& 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 @@ void LatencyTracker::trackGraphicsLatency( 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::reportAndPruneMatureRecords(nsecs_t newEventTime) { } } -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 289b8ed6c4..4b0c618590 100644 --- a/services/inputflinger/dispatcher/LatencyTracker.h +++ b/services/inputflinger/dispatcher/LatencyTracker.h @@ -43,6 +43,12 @@ public: 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 @@ public: 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 e7e1937235..89c0741dfd 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 @@ protected: } 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 @@ private: 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 @@ void LatencyTrackerTest::assertReceivedTimeline(const InputEventTimeline& timeli * 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 @@ void LatencyTrackerTest::assertReceivedTimelines(const std::vector<InputEventTim */ 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, TrackListener_DoesNotTriggerReporting) { TEST_F(LatencyTrackerTest, TrackFinishedEvent_DoesNotTriggerReporting) { mTracker->trackFinishedEvent(1 /*inputEventId*/, connection1, 2 /*deliveryTime*/, 3 /*consumeTime*/, 4 /*finishTime*/); + triggerEventReporting(4 /*eventTime*/); assertReceivedTimelines({}); } @@ -141,6 +159,7 @@ TEST_F(LatencyTrackerTest, TrackGraphicsLatency_DoesNotTriggerReporting) { 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 @@ TEST_F(LatencyTrackerTest, TrackAllParameters_ReportsFullTimeline) { 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 @@ TEST_F(LatencyTrackerTest, MultipleEvents_AreReportedConsistently) { mTracker->trackGraphicsLatency(inputEventId2, connection2, connectionTimeline2.graphicsTimeline); // Now both events should be completed + triggerEventReporting(timeline2.eventTime); assertReceivedTimelines({timeline1, timeline2}); } @@ -228,6 +269,7 @@ TEST_F(LatencyTrackerTest, IncompleteEvents_AreHandledConsistently) { mTracker->trackGraphicsLatency(1 /*inputEventId*/, token, expectedCT.graphicsTimeline); expectedTimelines[0].connectionTimelines.emplace(token, std::move(expectedCT)); + triggerEventReporting(timeline.eventTime); assertReceivedTimelines(expectedTimelines); } @@ -246,6 +288,7 @@ TEST_F(LatencyTrackerTest, EventsAreTracked_WhenTrackListenerIsCalledFirst) { 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 0000000000..df4db199f8 --- /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 0000000000..9f756ebdad --- /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 |