InputDispatcher: Add InputTracerInterface for tracing input events
The interface documents how the tracer will interact with
InputDispatcher.
Bug: 210460522
Test: Build
Change-Id: Id97f9b0851f1248956b95e8b18b2302ecff1bff5
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp
index cc0d49c..2153d8a 100644
--- a/services/inputflinger/dispatcher/Entry.cpp
+++ b/services/inputflinger/dispatcher/Entry.cpp
@@ -164,6 +164,11 @@
keyCode, scanCode, metaState, repeatCount, policyFlags);
}
+std::ostream& operator<<(std::ostream& out, const KeyEntry& keyEntry) {
+ out << keyEntry.getDescription();
+ return out;
+}
+
// --- TouchModeEntry ---
TouchModeEntry::TouchModeEntry(int32_t id, nsecs_t eventTime, bool inTouchMode, int displayId)
diff --git a/services/inputflinger/dispatcher/Entry.h b/services/inputflinger/dispatcher/Entry.h
index e2e13c3..a915805 100644
--- a/services/inputflinger/dispatcher/Entry.h
+++ b/services/inputflinger/dispatcher/Entry.h
@@ -18,6 +18,7 @@
#include "InjectionState.h"
#include "InputTarget.h"
+#include "trace/EventTrackerInterface.h"
#include <gui/InputApplication.h>
#include <input/Input.h>
@@ -125,6 +126,7 @@
int32_t scanCode;
int32_t metaState;
nsecs_t downTime;
+ std::unique_ptr<trace::EventTrackerInterface> traceTracker;
bool syntheticRepeat; // set to true for synthetic key repeats
@@ -147,6 +149,8 @@
std::string getDescription() const override;
};
+std::ostream& operator<<(std::ostream& out, const KeyEntry& motionEntry);
+
struct MotionEntry : EventEntry {
int32_t deviceId;
uint32_t source;
@@ -165,6 +169,7 @@
nsecs_t downTime;
std::vector<PointerProperties> pointerProperties;
std::vector<PointerCoords> pointerCoords;
+ std::unique_ptr<trace::EventTrackerInterface> traceTracker;
size_t getPointerCount() const { return pointerProperties.size(); }
diff --git a/services/inputflinger/dispatcher/InputDispatcher.cpp b/services/inputflinger/dispatcher/InputDispatcher.cpp
index e998d91..0ddea71 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.cpp
+++ b/services/inputflinger/dispatcher/InputDispatcher.cpp
@@ -78,6 +78,14 @@
namespace android::inputdispatcher {
namespace {
+
+template <class Entry>
+void ensureEventTraced(const Entry& entry) {
+ if (!entry.traceTracker) {
+ LOG(FATAL) << "Expected event entry to be traced, but it wasn't: " << entry;
+ }
+}
+
// Temporarily releases a held mutex for the lifetime of the instance.
// Named to match std::scoped_lock
class scoped_unlock {
@@ -783,6 +791,10 @@
// --- InputDispatcher ---
InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy)
+ : InputDispatcher(policy, nullptr) {}
+
+InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy,
+ std::unique_ptr<trace::InputTracingBackendInterface> traceBackend)
: mPolicy(policy),
mPendingEvent(nullptr),
mLastDropReason(DropReason::NOT_DROPPED),
@@ -807,6 +819,10 @@
SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener);
#endif
mKeyRepeatState.lastKeyEntry = nullptr;
+
+ if (traceBackend) {
+ // TODO: Create input tracer instance.
+ }
}
InputDispatcher::~InputDispatcher() {
@@ -1108,6 +1124,10 @@
dropReason = DropReason::BLOCKED;
}
done = dispatchKeyLocked(currentTime, keyEntry, &dropReason, nextWakeupTime);
+ if (done && mTracer) {
+ ensureEventTraced(*keyEntry);
+ mTracer->eventProcessingComplete(*keyEntry->traceTracker);
+ }
break;
}
@@ -1138,6 +1158,10 @@
}
}
done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime);
+ if (done && mTracer) {
+ ensureEventTraced(*motionEntry);
+ mTracer->eventProcessingComplete(*motionEntry->traceTracker);
+ }
break;
}
@@ -1249,6 +1273,9 @@
// If the application takes too long to catch up then we drop all events preceding
// the app switch key.
const KeyEntry& keyEntry = static_cast<const KeyEntry&>(entry);
+ if (mTracer) {
+ ensureEventTraced(keyEntry);
+ }
if (!REMOVE_APP_SWITCH_DROPS) {
if (isAppSwitchKeyEvent(keyEntry)) {
@@ -1286,7 +1313,11 @@
case EventEntry::Type::MOTION: {
LOG_ALWAYS_FATAL_IF((entry.policyFlags & POLICY_FLAG_TRUSTED) == 0,
"Unexpected untrusted event.");
- if (shouldPruneInboundQueueLocked(static_cast<const MotionEntry&>(entry))) {
+ const auto& motionEntry = static_cast<const MotionEntry&>(entry);
+ if (mTracer) {
+ ensureEventTraced(motionEntry);
+ }
+ if (shouldPruneInboundQueueLocked(motionEntry)) {
mNextUnblockedEvent = mInboundQueue.back();
needWake = true;
}
@@ -1567,6 +1598,10 @@
entry->repeatCount + 1, entry->downTime);
newEntry->syntheticRepeat = true;
+ if (mTracer) {
+ newEntry->traceTracker = mTracer->traceInboundEvent(*newEntry);
+ }
+
mKeyRepeatState.lastKeyEntry = newEntry;
mKeyRepeatState.nextRepeatTime = currentTime + mConfig.keyRepeatDelay;
return newEntry;
@@ -1873,6 +1908,13 @@
// Add monitor channels from event's or focused display.
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
+ if (mTracer) {
+ ensureEventTraced(*entry);
+ for (const auto& target : inputTargets) {
+ mTracer->dispatchToTargetHint(*entry->traceTracker, target);
+ }
+ }
+
// Dispatch the key.
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
@@ -1998,6 +2040,13 @@
// Add monitor channels from event's or focused display.
addGlobalMonitoringTargetsLocked(inputTargets, getTargetDisplayId(*entry));
+ if (mTracer) {
+ ensureEventTraced(*entry);
+ for (const auto& target : inputTargets) {
+ mTracer->dispatchToTargetHint(*entry->traceTracker, target);
+ }
+ }
+
// Dispatch the motion.
dispatchEventLocked(currentTime, entry, inputTargets);
return true;
@@ -3635,6 +3684,7 @@
PointerCoords scaledCoords[MAX_POINTERS];
const PointerCoords* usingCoords = motionEntry.pointerCoords.data();
+ // TODO(b/316355518): Do not modify coords before dispatch.
// Set the X and Y offset and X and Y scale depending on the input source.
if ((motionEntry.source & AINPUT_SOURCE_CLASS_POINTER) &&
!(dispatchEntry.targetFlags.test(InputTarget::Flags::ZERO_COORDS))) {
@@ -3710,6 +3760,9 @@
keyEntry.keyCode, keyEntry.scanCode,
keyEntry.metaState, keyEntry.repeatCount,
keyEntry.downTime, keyEntry.eventTime);
+ if (mTracer) {
+ mTracer->traceEventDispatch(*dispatchEntry, keyEntry.traceTracker.get());
+ }
break;
}
@@ -3718,7 +3771,11 @@
LOG(INFO) << "Publishing " << *dispatchEntry << " to "
<< connection->getInputChannelName();
}
+ const MotionEntry& motionEntry = static_cast<const MotionEntry&>(eventEntry);
status = publishMotionEvent(*connection, *dispatchEntry);
+ if (mTracer) {
+ mTracer->traceEventDispatch(*dispatchEntry, motionEntry.traceTracker.get());
+ }
break;
}
@@ -4400,6 +4457,9 @@
args.deviceId, args.source, args.displayId, policyFlags,
args.action, flags, keyCode, args.scanCode, metaState,
repeatCount, args.downTime);
+ if (mTracer) {
+ newEntry->traceTracker = mTracer->traceInboundEvent(*newEntry);
+ }
needWake = enqueueInboundEventLocked(std::move(newEntry));
mLock.unlock();
@@ -4526,6 +4586,9 @@
args.yPrecision, args.xCursorPosition,
args.yCursorPosition, args.downTime,
args.pointerProperties, args.pointerCoords);
+ if (mTracer) {
+ newEntry->traceTracker = mTracer->traceInboundEvent(*newEntry);
+ }
if (args.id != android::os::IInputConstants::INVALID_INPUT_EVENT_ID &&
IdGenerator::getSource(args.id) == IdGenerator::Source::INPUT_READER &&
@@ -4713,6 +4776,9 @@
incomingKey.getScanCode(), metaState,
incomingKey.getRepeatCount(),
incomingKey.getDownTime());
+ if (mTracer) {
+ injectedEntry->traceTracker = mTracer->traceInboundEvent(*injectedEntry);
+ }
injectedEntries.push(std::move(injectedEntry));
break;
}
@@ -4770,6 +4836,9 @@
samplePointerCoords +
pointerCount));
transformMotionEntryForInjectionLocked(*injectedEntry, motionEvent.getTransform());
+ if (mTracer) {
+ injectedEntry->traceTracker = mTracer->traceInboundEvent(*injectedEntry);
+ }
injectedEntries.push(std::move(injectedEntry));
for (size_t i = motionEvent.getHistorySize(); i > 0; i--) {
sampleEventTimes += 1;
@@ -5869,6 +5938,8 @@
ns2ms(mConfig.keyRepeatTimeout));
dump += mLatencyTracker.dump(INDENT2);
dump += mLatencyAggregator.dump(INDENT2);
+ dump += INDENT "InputTracer: ";
+ dump += mTracer == nullptr ? "Disabled" : "Enabled";
}
void InputDispatcher::dumpMonitors(std::string& dump, const std::vector<Monitor>& monitors) const {
diff --git a/services/inputflinger/dispatcher/InputDispatcher.h b/services/inputflinger/dispatcher/InputDispatcher.h
index 3567288..5b45531 100644
--- a/services/inputflinger/dispatcher/InputDispatcher.h
+++ b/services/inputflinger/dispatcher/InputDispatcher.h
@@ -32,6 +32,8 @@
#include "Monitor.h"
#include "TouchState.h"
#include "TouchedWindow.h"
+#include "trace/InputTracerInterface.h"
+#include "trace/InputTracingBackendInterface.h"
#include <attestation/HmacKeyManager.h>
#include <gui/InputApplication.h>
@@ -82,6 +84,9 @@
static constexpr bool kDefaultInTouchMode = true;
explicit InputDispatcher(InputDispatcherPolicyInterface& policy);
+ // Constructor used for testing.
+ explicit InputDispatcher(InputDispatcherPolicyInterface&,
+ std::unique_ptr<trace::InputTracingBackendInterface>);
~InputDispatcher() override;
void dump(std::string& dump) const override;
@@ -172,6 +177,9 @@
std::condition_variable mDispatcherIsAlive;
mutable std::condition_variable mDispatcherEnteredIdle;
+ // Input event tracer. The tracer will only exist on builds where input tracing is allowed.
+ std::unique_ptr<trace::InputTracerInterface> mTracer GUARDED_BY(mLock);
+
sp<Looper> mLooper;
std::shared_ptr<const EventEntry> mPendingEvent GUARDED_BY(mLock);
diff --git a/services/inputflinger/dispatcher/trace/EventTrackerInterface.h b/services/inputflinger/dispatcher/trace/EventTrackerInterface.h
new file mode 100644
index 0000000..929820e
--- /dev/null
+++ b/services/inputflinger/dispatcher/trace/EventTrackerInterface.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2024 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.
+ */
+
+#pragma once
+
+namespace android::inputdispatcher::trace {
+
+/**
+ * A tracker used to track the lifecycle of a traced input event.
+ * The tracker should be stored inside the traced event. When the event goes out of scope after
+ * the dispatcher has finished processing it, the tracker will call back into the tracer to
+ * initiate cleanup.
+ */
+class EventTrackerInterface {
+public:
+ virtual ~EventTrackerInterface() = default;
+};
+
+} // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/dispatcher/trace/InputTracerInterface.h b/services/inputflinger/dispatcher/trace/InputTracerInterface.h
new file mode 100644
index 0000000..c6cd7de
--- /dev/null
+++ b/services/inputflinger/dispatcher/trace/InputTracerInterface.h
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2024 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.
+ */
+
+#pragma once
+
+#include "../Entry.h"
+#include "../InputTarget.h"
+#include "EventTrackerInterface.h"
+
+namespace android::inputdispatcher::trace {
+
+/**
+ * InputTracerInterface is the tracing interface for InputDispatcher.
+ *
+ * The tracer is responsible for tracing information about input events and where they are
+ * dispatched. The trace is logged to the backend using the InputTracingBackendInterface.
+ *
+ * A normal traced event should have the following lifecycle:
+ * - The EventTracker is obtained from traceInboundEvent(), after which point the event
+ * should not change.
+ * - While the event is being processed, dispatchToTargetHint() is called for each target that
+ * the event will be eventually sent to.
+ * - Once all targets have been determined, eventProcessingComplete() is called, at which point
+ * the tracer will have enough information to commit the event to the trace.
+ * - For each event that is dispatched to the client, traceEventDispatch() is called, and the
+ * tracer will record that the event was sent to the client.
+ */
+class InputTracerInterface {
+public:
+ InputTracerInterface() = default;
+ virtual ~InputTracerInterface() = default;
+ InputTracerInterface(const InputTracerInterface&) = delete;
+ InputTracerInterface& operator=(const InputTracerInterface&) = delete;
+
+ /**
+ * Trace an input event that is being processed by InputDispatcher. The event must not be
+ * modified after it is traced to keep the traced event consistent with the event that is
+ * eventually dispatched. An EventTracker is returned for each traced event that should be used
+ * 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,
+ * events targeting certain UIDs may be logged as sensitive events.
+ * This may be called 0 or more times for each tracked event before event processing is
+ * completed.
+ */
+ virtual void dispatchToTargetHint(const EventTrackerInterface&, const InputTarget&) = 0;
+
+ /**
+ * Notify the tracer that the event processing is complete. This may be called at most once
+ * for each traced event. If a tracked event is dropped before it can be processed, it is
+ * possible that this is never called before the EventTracker is destroyed.
+ *
+ * This is used to commit the event to the trace in a timely manner, rather than always
+ * waiting for the event to go out of scope (and thus for the EventTracker to be destroyed)
+ * before committing. The point at which the event is destroyed can depend on several factors
+ * outside of our control, such as how long apps take to respond, so we don't want to depend on
+ * that.
+ */
+ virtual void eventProcessingComplete(const EventTrackerInterface&) = 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
+ * must be provided. For events that were not previously traced, the cookie must be null.
+ */
+ virtual void traceEventDispatch(const DispatchEntry&, const EventTrackerInterface*) = 0;
+};
+
+} // namespace android::inputdispatcher::trace
diff --git a/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
new file mode 100644
index 0000000..b430a5b
--- /dev/null
+++ b/services/inputflinger/dispatcher/trace/InputTracingBackendInterface.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2024 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.
+ */
+
+#pragma once
+
+#include "../Entry.h"
+
+namespace android::inputdispatcher::trace {
+
+/**
+ * An interface for the tracing backend, used for setting a custom backend for testing.
+ */
+class InputTracingBackendInterface {
+public:
+ virtual ~InputTracingBackendInterface() = default;
+
+ /** Trace a KeyEvent. */
+ virtual void traceKeyEvent(const KeyEntry&) const = 0;
+
+ /** Trace a MotionEvent. */
+ virtual void traceMotionEvent(const MotionEntry&) const = 0;
+
+ /** Trace an event being sent to a window. */
+ struct WindowDispatchArgs {
+ std::variant<MotionEntry, KeyEntry> eventEntry;
+ nsecs_t deliveryTime;
+ int32_t resolvedFlags;
+ gui::Uid targetUid;
+ int64_t vsyncId;
+ int32_t windowId;
+ ui::Transform transform;
+ ui::Transform rawTransform;
+ std::array<uint8_t, 32> hmac;
+ };
+ virtual void traceWindowDispatch(const WindowDispatchArgs&) const = 0;
+};
+
+} // namespace android::inputdispatcher::trace