This is the sixth article in the Systrace series, primarily providing a brief introduction to Input in Systrace. It covers the Input workflow, how Input information is represented in Systrace, and how to combine Input info to analyze related performance issues.
The purpose of this series is to view the overall operation of the Android system from a different perspective using Systrace, while also providing an alternative angle for learning the Framework. Perhaps you’ve read many articles about the Framework but can never remember the code, or you’re unclear about the execution flow. Maybe from Systrace’s graphical perspective, you can gain a deeper understanding.
Version note — reviewed 2026-10-13: The 2019 screenshots and original dumpsys data are retained, with AOSP
android-9.0.0_r1as the historical reference and Android 16/Perfetto updates. Queue/trace names are not stable interfaces across every system. Both public attachment traces were inspected as text; no device experiment was rerun.
Table of Contents
- Series Article Index
- Main Content
- Input in Systrace
- InputDown Event Workflow in SystemServer
- InputDown Event Workflow in App
- Key Knowledge Points and Processes
- Input Refresh and Vsync
- Input Debug Information
- References
- Attachments
- Other Addresses
- About Me && Blog
Series Article Index
- Introduction to Systrace
- Systrace Basics - Prerequisites for Systrace
- Systrace Basics - Why 60 fps?
- Android Systrace Basics - SystemServer Explained
- Systrace Basics - SurfaceFlinger Explained
- Systrace Basics - Input Explained
- Systrace Basics - Vsync Explained
- Systrace Basics - Vsync-App: Detailed Explanation of Choreographer-Based Rendering Mechanism
- Systrace Basics - MainThread and RenderThread Explained
- Systrace Basics - Binder and Lock Contention Explained
- Systrace Basics - Triple Buffer Explained
- Systrace Basics - CPU Info Explained
- Systrace Smoothness in Action 1: Understanding Jank Principles
- Systrace Smoothness in Action 2: Case Analysis - MIUI Launcher Scroll Jank Analysis
- Systrace Smoothness in Action 3: FAQs During Jank Analysis
- Systrace Responsiveness in Action 1: Understanding Responsiveness Principles
- Systrace Responsiveness in Action 2: Responsiveness Analysis - Using App Startup as an Example
- Systrace Responsiveness in Action 3: Extended Knowledge on Responsiveness
- Systrace Thread CPU State Analysis Tips - Runnable
- Systrace Thread CPU State Analysis Tips - Running
- Systrace Thread CPU State Analysis Tips - Sleep and Uninterruptible Sleep
Main Content
Read this with Perfetto 5: Choreographer and Perfetto 7: MainThread/RenderThread. Locate system entry, target selection, InputChannel publication, app receipt/handling, and completion acknowledgment, then correlate any required frame with presentation. Acknowledging input and displaying its result are different boundaries.
The Choreographer article distinguishes Looper message dispatch from native FD callbacks. Readable input channels can enter Java through the native Looper, so input is not always an ordinary Handler Message. This article focuses on touch MotionEvent; KeyEvent has different focus, interception, and fallback paths. Home/Back are not universally ordinary keys delivered to the current app.
A typical touch path is:
- The controller samples according to device policy and the driver reports kernel events. Sampling, reporting, and Reader scheduling frequencies need not match one-to-one.
- EventHub reads events; InputReader’s device mappers process coordinates/actions and notify the dispatch chain.
- InputDispatcher selects connections using event type, display, touch regions, gesture state, and policy. It does not broadcast to all apps registered for input.
- The app handles input immediately or in batches as appropriate. UI changes may schedule/coalesce frame work; one input does not guarantee one new frame.
Time runs left to right, but compare synchronized event/slice boundaries. Cross-thread order alone does not prove causality: check channels, sequences, stacks, and scheduling. I hope this graphical view helps you locate code execution in the larger flow.
Input in Systrace
This historical single-pointer Launcher scroll normally starts with DOWN, continues through MOVE, and ends with UP or CANCEL. Multi-pointer gestures also use POINTER_DOWN/POINTER_UP; Down-Move-Up is not every possible stream.

InputReader and InputDispatcher are native system_server threads in this capture. Interpret the other annotations separately:
- Reader processes EventHub data; touch uses Motion notifications and keys use Key notifications.
- Dispatcher selects targets and maintains dispatch state. Names and trace points are version/vendor dependent.
oq:counts outbound entries for a Connection, which may represent a window or monitor. An app can have multiple windows/connections.wq:tracks successfully published entries awaiting finished-protocol processing, not only events for which business code must return true.aq:pendingcounts queued ViewRootImpl input not yet taken for processing, not all in-flight input.deliverInputEventtracks app delivery/processing, not the kernel wakeup instant. Inspect sched_wakeup/sched_switch separately.- The old viewer’s
Input Response/Rendering Responseare interaction-analysis groupings, not direct universal hardware touch-to-display measurements. - Release can continue into fling when gesture/velocity conditions allow; UP does not always cause fling.
DOWN comes first below. MOVE batching, history, and resampling materially affect timing and are not negligible differences.
InputDown Event Workflow in SystemServer
The image shows Reader work, Dispatcher scheduling, and connection-counter changes. Colors/counts guide investigation; select matching events and read timestamps for exact delays.

InputDown Event Workflow in App
An ordinary DOWN generally does not follow MOVE’s batching delay and can be delivered once its channel is readable. This is not determined by whether the system “has VSync.” A busy main thread, Runnable scheduling delay, or another input-stage wait can still postpone handling.

App’s Pending Queue

“Back to zero means finished” is too strong. Android 9 ViewRootImpl.doProcessInputEvents() decrements the count after dequeuing and before calling deliverInputEvent(). At zero, an event can still be processing, awaiting an asynchronous stage, or not yet acknowledged. A thread blocked before reading the native channel need not first build up this Java queue.
Main Thread Processing Input Events
The stack shows native poll → InputEventReceiver → ViewRootImpl stages → DecorView/Activity/View dispatch. Other event types can traverse different stages; instrumented method duration is not unqualified normal-runtime cost.

Key Knowledge Points and Processes
The normal path is Reader notification → Dispatcher inbound/pending → target selection and per-connection outbound → successful publication into wait → app handling → finished sequence/handled response over the input channel → Dispatcher completion policy and removal or continuation.
Events and acknowledgments primarily use the InputChannel socket protocol, not a per-touch Binder callback into Java InputManagerService. handled=false can be a valid result, and finished does not mean the UI has redrawn. Broken-connection cleanup can also reduce queues, so read connection state alongside counts.
InputReader
Using AOSP android-9.0.0_r1 as the reference, EventHub watches input-device FDs and device changes; mappers process RawEvents. Its 256-entry buffer stores RawEvents, not 256 gestures, a sampling rate, or a universal constant. Touch follows NotifyMotionArgs/MotionEntry; Key follows NotifyKeyArgs/KeyEntry.
The historical diagram includes key-specific policy such as interceptKeyBeforeQueueing, not a mandatory path for every touch:

Here is Android 9’s complete loopOnce() method. Notice that blocking reads and listener flush both happen outside the Reader lock. It remains system source, not a standalone app example:
1 | void InputReader::loopOnce() { |
InputDispatcher
Reader notifications may enqueue and wake Dispatcher; filtering and other wakeup sources also exist. The selected Runnable interval below records TID 25669, shown as InputReader, as its waker. That is evidence for this occurrence, not every Dispatcher wakeup.

Android 9 Dispatcher handles Key and Motion separately. Keys commonly involve focus and key policy; touch also involves hit testing, gesture targets, split/cancel behavior, and other conditions. The diagram below explicitly uses dispatchKeyLocked, findFocusedWindowTargetsLocked, and publishKeyEvent; it is not the exact touch path.

The following pseudocode describes the queue boundaries:
1 | dispatchOnce: |
That version sets deliveryTime while attempting publication in startDispatchCycleLocked(). It is not the hardware sample time, nor should the first dispatchOnce entry time be treated as every later delivery time despite intervening waits. Backpressure such as WOULD_BLOCK and connection errors require separate handling. Policy commands can run from a command queue at appropriate lock boundaries.
InboundQueue
notifyKey and notifyMotion may enqueue after their respective checks. Removing an entry into mPendingEvent reduces iq, while that event may still wait for a target or continue dispatch. iq=0 does not mean all system input work is finished.

OutboundQueue
Each Connection has entries awaiting publication. One event can have multiple eligible targets or be filtered; an app can own multiple windows, and monitors have separate purposes. The selected counter below belongs to StatusBar, with value 1, not the entire Launcher input stream.

WaitQueue
After successful publication, Dispatcher moves the entry from outbound to wait. App finishInputEvent(event, handled) sends a native InputConsumer acknowledgment; Dispatcher processes it by connection/sequence and retires the matching entry, with fallback policy where applicable. See InputTransport and InputEventReceiver.
Sustained wait growth is a clue, with possible causes including scheduling, app work, MOVE batching, ACK transport/processing, and connection problems. Queue length is neither duration nor an ANR criterion. This next crop belongs to a separate sample.tencent.mat... list trace: the selection spans 32.276 ms, and one counter is 8. It is not the Launcher trace, nor proof that all eight entries have yet to enter the app.

Overall Logic
This Gityuan diagram includes KeyboardInputMapper/notifyKey. It is a historical reading aid, not the exact Android 16 path for every touch.

Input Refresh and Vsync
Does a higher touch sampling rate always help? First, separate touch sampling, reporting/dispatch cadence, display refresh rate, and actual app frame rate. 120Hz is about 8.33ms; 160Hz is 6.25ms. The original 8ms for 120Hz was approximate, and these are not universal current-device specifications.

I labeled this device 160Hz in the capture at the time, but Reader execution spacing is also affected by report batching and scheduling. Temporary WaitQueue accumulation does not mean the app never consumes input. Batched MOVE may be consumed in Choreographer’s INPUT phase against frame time; DOWN/UP cannot be generalized to the same rule.
Multiple samples are not simply reduced to the latest with all others discarded. The MotionEvent batching contract allows ACTION_MOVE to contain historical samples, read before current coordinates. Android 9 InputConsumer.consumeBatch/consumeSamples() selects the appropriate sample-time range, builds MotionEvent history, and maintains sequence chains for acknowledgment. Enabled resampling can also estimate positions relative to frame time.
A 60Hz display therefore does not prove 120Hz touch is sufficient, nor does 90Hz require exactly 180Hz touch. Higher sampling can reduce some waits or provide finer data, with processing/power costs. Alternating groups of two/three samples do not themselves prove visible jitter. Compare event times, history/resampling, presentation, and end-to-end latency rather than requiring an exact 2:1 ratio.
Input Debug Information
1 | adb shell dumpsys input |
This command produces a lot of output. I picked out the Device, InputReader, and InputDispatcher sections below from the device snapshot I took at the time. Fields/formats depend on the system, and this is not a continuous record of an interaction. Interpret unknowns, empty fields, and sentinel values according to their meaning.
Device Info
Paths, descriptors, IDs, and mapping files belong to this snapshot. event6 is not every phone’s touch node, and these illustrative IDs alone do not identify a manufacturer.
1 | 3: main_touch |
Input Reader Status
Raw (660, 1338) and cooked (659.389, 1337.401) reflect coordinate mapping/calibration, not identical coordinate spaces. PhysicalWidth here is explicitly in px, not millimeters. unknown range is not a zero range. The external-stylus 9223372036854775807 values are old-state sentinels such as no active deadline, not real input times.
1 | Device 3: main_touch |
InputDispatcher Status
Distinguish focused application from focused window, per-display touch targets, window entries, monitors, and connections. MonitoringChannels is not the list of ordinary channels for every window, and this is an excerpt. DispatchFrozen describes input dispatch state, not the later cached-app freezer.
1 | Input Dispatcher State: |
This snapshot has three waiting MOVE entries. age measures from event time to dump time; wait measures from that entry’s deliveryTime to dump time. Neither is total visual response latency. Its 5000.000ms is a reported dispatch timeout, not every device’s or every ANR type’s universal limit. The official input-dispatch ANR guide describes a default of 5 seconds and cases such as no focused window. Correlate timeout logs, targets, and blocking causes; three outstanding events do not themselves establish an ANR.
Continuing on Newer Versions
AOSP Android 16 InputDispatcher has a Perfetto input-event tracing backend, but that tag gates it on userdebug/eng builds and the relevant feature flag. Do not promise that ordinary user builds can obtain android.input.inputevent by toggling one setting.
With the required ATrace data, Perfetto’s android.input module can break down send, receive, handling, and ACK round trips. Structured android.input.inputevent is a separate source with its own fields; neither is guaranteed present. Frame associations may be missing or speculative. ACK round trip is not complete physical touch-to-display latency, and unavailable data is not zero latency.
References
Portions of this article reference Gityuan’s blog. These articles provide deep dives into code details for those interested after finishing this piece.
- http://gityuan.com/2016/12/11/input-reader/
- http://gityuan.com/2016/12/10/input-manager/
- http://gityuan.com/2016/12/17/input-dispatcher/
- https://zhuanlan.zhihu.com/p/29386642
Attachments
The directory contains trace_input_blog_launcher_scroll.html.zip and trace_input_blog_list_jank.html.zip, respectively the Launcher and list-jank cases. Both readable archives contain HTML with an old viewer, process dumps, and ftrace data, targeting different apps. Current-browser compatibility of the embedded viewer was not tested here. A tool supporting legacy Systrace data can also be used to import and inspect the events:
Download Systrace attachments for this article
Other Addresses
For comments or likes, please visit the Zhihu or Juejin pages for this article:
Juejin - Systrace Basics - Input Interpretation
About Me && Blog
Below is my personal intro and related links. I look forward to exchanging ideas with fellow professionals. “When three walk together, one can always be my teacher!”
- Blogger Intro: Includes personal WeChat and WeChat group links.
- Blog Content Navigation: A guide for my blog content.
- Curated Excellent Blog Articles - Android Performance Optimization Must-Knows: Welcome to recommend projects/articles.
- Android Performance Optimization Knowledge Planet: The paid community stopped operating in 2026 and no longer accepts new members; this link preserves its history.
One walks faster alone, but a group walks further together.
