InputTracer: Trace events derived from other events separately
InputDispatcher goes through multiple "phases" when dispatching
new event, roughly corresponding to the following:
1. Inbound event processing (e.g. InputFilter, policy filtering,
generating key repeats, etc.)
2. Target finding (finding the touched/focused window(s) that should
receive the event)
3. Event modification (generating new events based on the original, such
as for split motions)
4. Publishing
When an event is modified in step 3, we always create a new EventEntry
with a new event ID to distinguish it from the original. These derived
events need to be traced separately, but need to share the same trace
context as the original event. For example, an event is split across
windows A and B, and the whole event is sent to spy window C. In this
case, windows A and B receive a derived event, and C receives the whole
event. If B is a trace-sensitive window, we must not leak the sensitive
info from the event by tracing the original event through C.
Since event modification (step 3) always happens after target finding
(step 2) for a dispatch entry, we will trace the derived (modified)
events separately. The modified event will never affect
target-finding, so the derived events are more limited, but they share
the same context as the original event by using the same State under the
hood.
Bug: 210460522
Test: atest inputflinger_tests
Change-Id: I772a04b7dfd0322357dd4dfa95387244ca6230e9
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.cpp b/services/inputflinger/dispatcher/trace/InputTracer.cpp
index 10f6b0f..3b5a096 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.cpp
+++ b/services/inputflinger/dispatcher/trace/InputTracer.cpp
@@ -59,6 +59,12 @@
e.downTime, e.flags, e.repeatCount};
}
+void writeEventToBackend(const TracedEvent& event, InputTracingBackendInterface& backend) {
+ std::visit(Visitor{[&](const TracedMotionEvent& e) { backend.traceMotionEvent(e); },
+ [&](const TracedKeyEvent& e) { backend.traceKeyEvent(e); }},
+ event);
+}
+
} // namespace
// --- InputTracer ---
@@ -67,41 +73,74 @@
: mBackend(std::move(backend)) {}
std::unique_ptr<EventTrackerInterface> InputTracer::traceInboundEvent(const EventEntry& entry) {
- TracedEvent traced;
+ // This is a newly traced inbound event. Create a new state to track it and its derived events.
+ auto eventState = std::make_shared<EventState>(*this);
if (entry.type == EventEntry::Type::MOTION) {
const auto& motion = static_cast<const MotionEntry&>(entry);
- traced = createTracedEvent(motion);
+ eventState->events.emplace_back(createTracedEvent(motion));
} else if (entry.type == EventEntry::Type::KEY) {
const auto& key = static_cast<const KeyEntry&>(entry);
- traced = createTracedEvent(key);
+ eventState->events.emplace_back(createTracedEvent(key));
} else {
LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type);
}
- return std::make_unique<EventTrackerImpl>(*this, std::move(traced));
+ return std::make_unique<EventTrackerImpl>(std::move(eventState), /*isDerived=*/false);
}
void InputTracer::dispatchToTargetHint(const EventTrackerInterface& cookie,
const InputTarget& target) {
+ if (isDerivedCookie(cookie)) {
+ LOG(FATAL) << "Event target cannot be updated from a derived cookie.";
+ }
auto& eventState = getState(cookie);
- if (eventState.isEventProcessingComplete) {
- LOG(FATAL) << "dispatchToTargetHint() should not be called after eventProcessingComplete()";
+ if (eventState->isEventProcessingComplete) {
+ // TODO(b/210460522): Disallow adding new targets after eventProcessingComplete() is called.
+ return;
}
// TODO(b/210460522): Determine if the event is sensitive based on the target.
}
void InputTracer::eventProcessingComplete(const EventTrackerInterface& cookie) {
+ if (isDerivedCookie(cookie)) {
+ LOG(FATAL) << "Event processing cannot be set from a derived cookie.";
+ }
auto& eventState = getState(cookie);
- if (eventState.isEventProcessingComplete) {
+ if (eventState->isEventProcessingComplete) {
LOG(FATAL) << "Traced event was already logged. "
"eventProcessingComplete() was likely called more than once.";
}
- std::visit(Visitor{[&](const TracedMotionEvent& e) { mBackend->traceMotionEvent(e); },
- [&](const TracedKeyEvent& e) { mBackend->traceKeyEvent(e); }},
- eventState.event);
- eventState.isEventProcessingComplete = true;
+ for (const auto& event : eventState->events) {
+ writeEventToBackend(event, *mBackend);
+ }
+ eventState->isEventProcessingComplete = true;
+}
+
+std::unique_ptr<EventTrackerInterface> InputTracer::traceDerivedEvent(
+ const EventEntry& entry, const EventTrackerInterface& originalEventCookie) {
+ // This is an event derived from an already-established event. Use the same state to track
+ // this event too.
+ auto eventState = getState(originalEventCookie);
+
+ if (entry.type == EventEntry::Type::MOTION) {
+ const auto& motion = static_cast<const MotionEntry&>(entry);
+ eventState->events.emplace_back(createTracedEvent(motion));
+ } else if (entry.type == EventEntry::Type::KEY) {
+ const auto& key = static_cast<const KeyEntry&>(entry);
+ eventState->events.emplace_back(createTracedEvent(key));
+ } else {
+ LOG(FATAL) << "Cannot trace EventEntry of type: " << ftl::enum_string(entry.type);
+ }
+
+ if (eventState->isEventProcessingComplete) {
+ // It is possible for a derived event to be dispatched some time after the original event
+ // is dispatched, such as in the case of key fallback events. To account for these cases,
+ // derived events can be traced after the processing is complete for the original event.
+ writeEventToBackend(eventState->events.back(), *mBackend);
+ }
+ return std::make_unique<EventTrackerImpl>(std::move(eventState), /*isDerived=*/true);
}
void InputTracer::traceEventDispatch(const DispatchEntry& dispatchEntry,
@@ -126,9 +165,7 @@
if (!cookie) {
// This event was not tracked as an inbound event, so trace it now.
- std::visit(Visitor{[&](const TracedMotionEvent& e) { mBackend->traceMotionEvent(e); },
- [&](const TracedKeyEvent& e) { mBackend->traceKeyEvent(e); }},
- traced);
+ writeEventToBackend(traced, *mBackend);
}
// The vsyncId only has meaning if the event is targeting a window.
@@ -141,27 +178,29 @@
/*hmac=*/{}, resolvedKeyRepeatCount});
}
-InputTracer::EventState& InputTracer::getState(const EventTrackerInterface& cookie) {
+std::shared_ptr<InputTracer::EventState>& InputTracer::getState(
+ const EventTrackerInterface& cookie) {
return static_cast<const EventTrackerImpl&>(cookie).mState;
}
-// --- InputTracer::EventTrackerImpl ---
+bool InputTracer::isDerivedCookie(const EventTrackerInterface& cookie) {
+ return static_cast<const EventTrackerImpl&>(cookie).mIsDerived;
+}
-InputTracer::EventTrackerImpl::EventTrackerImpl(InputTracer& tracer, TracedEvent&& event)
- : mTracer(tracer), mState(event) {}
+// --- InputTracer::EventState ---
-InputTracer::EventTrackerImpl::~EventTrackerImpl() {
- if (mState.isEventProcessingComplete) {
+InputTracer::EventState::~EventState() {
+ if (isEventProcessingComplete) {
// This event has already been written to the trace as expected.
return;
}
// The event processing was never marked as complete, so do it now.
// TODO(b/210460522): Determine why/where the event is being destroyed before
// eventProcessingComplete() is called.
- std::visit(Visitor{[&](const TracedMotionEvent& e) { mTracer.mBackend->traceMotionEvent(e); },
- [&](const TracedKeyEvent& e) { mTracer.mBackend->traceKeyEvent(e); }},
- mState.event);
- mState.isEventProcessingComplete = true;
+ for (const auto& event : events) {
+ writeEventToBackend(event, *tracer.mBackend);
+ }
+ isEventProcessingComplete = true;
}
} // namespace android::inputdispatcher::trace::impl
diff --git a/services/inputflinger/dispatcher/trace/InputTracer.h b/services/inputflinger/dispatcher/trace/InputTracer.h
index 1acac6d..ccff30e 100644
--- a/services/inputflinger/dispatcher/trace/InputTracer.h
+++ b/services/inputflinger/dispatcher/trace/InputTracer.h
@@ -44,36 +44,43 @@
std::unique_ptr<EventTrackerInterface> traceInboundEvent(const EventEntry&) override;
void dispatchToTargetHint(const EventTrackerInterface&, const InputTarget&) override;
void eventProcessingComplete(const EventTrackerInterface&) override;
+ std::unique_ptr<EventTrackerInterface> traceDerivedEvent(const EventEntry&,
+ const EventTrackerInterface&) override;
void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface*) override;
private:
std::unique_ptr<InputTracingBackendInterface> mBackend;
- // The state of a tracked event.
+ // The state of a tracked event, shared across all events derived from the original event.
struct EventState {
- explicit inline EventState(TracedEvent event) : event(std::move(event)){};
+ explicit inline EventState(InputTracer& tracer) : tracer(tracer){};
+ ~EventState();
- const TracedEvent event;
+ InputTracer& tracer;
+ std::vector<const TracedEvent> events;
bool isEventProcessingComplete{false};
// TODO(b/210460522): Add additional args for tracking event sensitivity and
// dispatch target UIDs.
};
// Get the event state associated with a tracking cookie.
- EventState& getState(const EventTrackerInterface&);
+ std::shared_ptr<EventState>& getState(const EventTrackerInterface&);
+ bool isDerivedCookie(const EventTrackerInterface&);
// Implementation of the event tracker cookie. The cookie holds the event state directly for
// convenience to avoid the overhead of tracking the state separately in InputTracer.
class EventTrackerImpl : public EventTrackerInterface {
public:
- explicit EventTrackerImpl(InputTracer&, TracedEvent&& entry);
- virtual ~EventTrackerImpl() override;
+ inline EventTrackerImpl(const std::shared_ptr<EventState>& state, bool isDerivedEvent)
+ : mState(state), mIsDerived(isDerivedEvent) {}
+ EventTrackerImpl(const EventTrackerImpl&) = default;
private:
- InputTracer& mTracer;
- mutable EventState mState;
+ mutable std::shared_ptr<EventState> mState;
+ const bool mIsDerived;
- friend EventState& InputTracer::getState(const EventTrackerInterface&);
+ friend std::shared_ptr<EventState>& InputTracer::getState(const EventTrackerInterface&);
+ friend bool InputTracer::isDerivedCookie(const EventTrackerInterface&);
};
};
diff --git a/services/inputflinger/dispatcher/trace/InputTracerInterface.h b/services/inputflinger/dispatcher/trace/InputTracerInterface.h
index c6cd7de..daf716f 100644
--- a/services/inputflinger/dispatcher/trace/InputTracerInterface.h
+++ b/services/inputflinger/dispatcher/trace/InputTracerInterface.h
@@ -52,7 +52,6 @@
* to track the event's lifecycle inside InputDispatcher.
*/
virtual std::unique_ptr<EventTrackerInterface> traceInboundEvent(const EventEntry&) = 0;
-
/**
* Notify the tracer that the traced event will be sent to the given InputTarget.
* The tracer may change how the event is logged depending on the target. For example,
@@ -76,6 +75,19 @@
virtual void eventProcessingComplete(const EventTrackerInterface&) = 0;
/**
+ * Trace an input event that is derived from another event. This is used in cases where an event
+ * is modified from the original, such as when a touch is split across multiple windows, or
+ * when a HOVER_MOVE event is modified to be a HOVER_EXIT, etc. The original event's tracker
+ * must be provided, and a new EventTracker is returned that should be used to track the event's
+ * lifecycle.
+ *
+ * NOTE: The derived tracker cannot be used to change the targets of the original event, meaning
+ * it cannot be used with {@link #dispatchToTargetHint} or {@link eventProcessingComplete}.
+ */
+ virtual std::unique_ptr<EventTrackerInterface> traceDerivedEvent(
+ const EventEntry&, const EventTrackerInterface& originalEventTracker) = 0;
+
+ /**
* Trace an input event being successfully dispatched to a window. The dispatched event may
* be a previously traced inbound event, or it may be a synthesized event that has not been
* previously traced. For inbound events that were previously traced, the EventTracker cookie