diff options
| author | 2023-12-15 07:30:22 +0000 | |
|---|---|---|
| committer | 2024-01-17 22:49:31 +0000 | |
| commit | abcdf5cf07d8321c0bc6224fc623fc19bbdc2b74 (patch) | |
| tree | f1f1d87da1cf5b489277da69bea16f9cca33b956 | |
| parent | a57e6352c0dd4e3fee25229eed77cc68fc4c684d (diff) | |
InputDispatcher: Add InputTracerInterface for tracing input events
The interface documents how the tracer will interact with
InputDispatcher.
Bug: 210460522
Test: Build
Change-Id: Id97f9b0851f1248956b95e8b18b2302ecff1bff5
7 files changed, 260 insertions, 1 deletions
diff --git a/services/inputflinger/dispatcher/Entry.cpp b/services/inputflinger/dispatcher/Entry.cpp index cc0d49c15e..2153d8a3f5 100644 --- a/services/inputflinger/dispatcher/Entry.cpp +++ b/services/inputflinger/dispatcher/Entry.cpp @@ -164,6 +164,11 @@ std::string KeyEntry::getDescription() const { 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 e2e13c3fde..a915805182 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 @@ struct KeyEntry : EventEntry { 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 @@ struct KeyEntry : EventEntry { 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 @@ struct MotionEntry : EventEntry { 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 e998d917c0..0ddea7195a 100644 --- a/services/inputflinger/dispatcher/InputDispatcher.cpp +++ b/services/inputflinger/dispatcher/InputDispatcher.cpp @@ -78,6 +78,14 @@ static const bool REMOVE_APP_SWITCH_DROPS = true; 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 @@ Result<void> validateWindowInfosUpdate(const gui::WindowInfosUpdate& update) { // --- 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 @@ InputDispatcher::InputDispatcher(InputDispatcherPolicyInterface& policy) SurfaceComposerClient::getDefault()->addWindowInfosListener(mWindowInfoListener); #endif mKeyRepeatState.lastKeyEntry = nullptr; + + if (traceBackend) { + // TODO: Create input tracer instance. + } } InputDispatcher::~InputDispatcher() { @@ -1108,6 +1124,10 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t& nextWakeupTime) { dropReason = DropReason::BLOCKED; } done = dispatchKeyLocked(currentTime, keyEntry, &dropReason, nextWakeupTime); + if (done && mTracer) { + ensureEventTraced(*keyEntry); + mTracer->eventProcessingComplete(*keyEntry->traceTracker); + } break; } @@ -1138,6 +1158,10 @@ void InputDispatcher::dispatchOnceInnerLocked(nsecs_t& nextWakeupTime) { } } done = dispatchMotionLocked(currentTime, motionEntry, &dropReason, nextWakeupTime); + if (done && mTracer) { + ensureEventTraced(*motionEntry); + mTracer->eventProcessingComplete(*motionEntry->traceTracker); + } break; } @@ -1249,6 +1273,9 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE // 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 @@ bool InputDispatcher::enqueueInboundEventLocked(std::unique_ptr<EventEntry> newE 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 @@ std::shared_ptr<KeyEntry> InputDispatcher::synthesizeKeyRepeatLocked(nsecs_t cur 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 @@ bool InputDispatcher::dispatchKeyLocked(nsecs_t currentTime, std::shared_ptr<con // 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 @@ bool InputDispatcher::dispatchMotionLocked(nsecs_t currentTime, // 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 @@ status_t InputDispatcher::publishMotionEvent(Connection& connection, 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 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, 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 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, 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 @@ void InputDispatcher::notifyKey(const NotifyKeyArgs& args) { 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 @@ void InputDispatcher::notifyMotion(const NotifyMotionArgs& args) { 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 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev incomingKey.getScanCode(), metaState, incomingKey.getRepeatCount(), incomingKey.getDownTime()); + if (mTracer) { + injectedEntry->traceTracker = mTracer->traceInboundEvent(*injectedEntry); + } injectedEntries.push(std::move(injectedEntry)); break; } @@ -4770,6 +4836,9 @@ InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* ev 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 @@ void InputDispatcher::dumpDispatchStateLocked(std::string& dump) const { 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 3567288f1d..5b455313e8 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 @@ public: 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 @@ private: 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 0000000000..929820e0a6 --- /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 0000000000..c6cd7de4b6 --- /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 0000000000..b430a5bb28 --- /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 |