Android Performance

Android Systrace Basics - Input Interpretation

Word count: 2.4kReading time: 14 min
2019/11/04
loading

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.

Table of Contents

Series Article Index

  1. Introduction to Systrace
  2. Systrace Basics - Prerequisites for Systrace
  3. Systrace Basics - Why 60 fps?
  4. Android Systrace Basics - SystemServer Explained
  5. Systrace Basics - SurfaceFlinger Explained
  6. Systrace Basics - Input Explained
  7. Systrace Basics - Vsync Explained
  8. Systrace Basics - Vsync-App: Detailed Explanation of Choreographer-Based Rendering Mechanism
  9. Systrace Basics - MainThread and RenderThread Explained
  10. Systrace Basics - Binder and Lock Contention Explained
  11. Systrace Basics - Triple Buffer Explained
  12. Systrace Basics - CPU Info Explained
  13. Systrace Smoothness in Action 1: Understanding Jank Principles
  14. Systrace Smoothness in Action 2: Case Analysis - MIUI Launcher Scroll Jank Analysis
  15. Systrace Smoothness in Action 3: FAQs During Jank Analysis
  16. Systrace Responsiveness in Action 1: Understanding Responsiveness Principles
  17. Systrace Responsiveness in Action 2: Responsiveness Analysis - Using App Startup as an Example
  18. Systrace Responsiveness in Action 3: Extended Knowledge on Responsiveness
  19. Systrace Thread CPU State Analysis Tips - Runnable
  20. Systrace Thread CPU State Analysis Tips - Running
  21. Systrace Thread CPU State Analysis Tips - Sleep and Uninterruptible Sleep

Main Content

In the article Detailed Explanation of Android Rendering Mechanism Based on Choreographer, I mentioned that the execution of an Android App’s main thread is essentially driven by Messages. These Messages can be looping animations, timed tasks, or wake-ups from other threads. However, the most common is the Input Message. Input here refers to the category under InputReader, which includes not only touch events (Down, Up, Move) but also Key events (Home Key, Back Key). In this article, we focus on touch events.

Because the Android system has added Trace points along the Input chain, and these are quite comprehensive, some manufacturers might add their own. However, we’ll focus on standard Trace points here to ensure the information remains consistent across different devices.

Input holds a vital position in Android. Most app scrolling and transitions are driven by Input events. I will eventually write a dedicated article on Android’s Input-based operation mechanism. Here, we look at Input from the perspective of Systrace. Before diving into the flow, keep this general handling process in mind:

  1. The touch screen scans every few milliseconds; if a touch event occurs, it reports it to the corresponding driver.
  2. InputReader reads the touch event and hands it to InputDispatcher for dispatching.
  3. InputDispatcher sends the touch event to the App that registered for Input events.
  4. Once the App receives the event, it performs event distribution. If the App’s UI changes during this process, it requests Vsync, leading to a frame redraw.

Also, when viewing Systrace, remember that time flows from left to right. A vertical line drawn on the Systrace means events to the left always happen before those to the right—this is a cornerstone of our analysis. I hope that after reading these Systrace-based analyses, you’ll have a 3D, graphical flowchart in your mind to quickly locate where your code execution stands.

Input in Systrace

Below is an overview diagram using launcher scrolling as an example (launcher scrolling includes one Input_Down event + several Input_Move events + one Input_Up event, all represented in Systrace, serving as a key entry point for analysis). The main modules involved are SystemServer and the App. Blue identifies event flow information, while red represents auxiliary data.

InputReader and InputDispatcher are two Native threads running within SystemServer, responsible for reading and dispatching Input events. Our Systrace analysis begins here. Below is a brief explanation of the marked numbers in the diagram:

  1. InputReader: Reads Input events from EventHub and passes them to InputDispatcher.
  2. InputDispatcher: Processes events received from InputReader (packaging and dispatching them to the corresponding app).
  3. OutboundQueue: Contains events about to be dispatched to the corresponding AppConnection.
  4. WaitQueue: Records events dispatched to an AppConnection that the App has not yet acknowledged as successfully processed.
  5. PendingInputEventQueue: Records Input events that the App needs to process (now within the application process).
  6. deliverInputEvent: Marks the App UI Thread being woken up by an Input event.
  7. InputResponse: Marks the Input event region, covering the processing stages of Input_Down, Input_Move (multiple), and Input_Up.
  8. App Response to Input Event: This shows the scrolling and subsequent release (fling) actions. Launcher updates the screen as the finger moves and triggers Fling after release; the entire flow is visible in Systrace.

Next, we’ll detail the workflow for the first Input_Down event. Processing for Move and Up events is similar (with minor differences that don’t affect the big picture).

InputDown Event Workflow in SystemServer

Magnifying the SystemServer section reveals the workflow (blue). Launcher scrolling includes Input_Down + several Input_Move + Input_Up; we are looking at the Input_Down event here.

InputDown Event Workflow in App

Upon receiving an Input event, the App may process it immediately (if no Vsync is present) or wait for a Vsync signal. Here, the Input_Down event directly wakes the main thread for processing. Its Systrace is relatively simple: an Input event queue at the top, with the main thread handling it.

App’s Pending Queue

Main Thread Processing Input Events

The main thread processing Input events is a familiar concept. From the call stack below, we see the Input event passed to ViewRootImpl, eventually reaching DecorView, followed by the familiar Input event distribution mechanism.

Key Knowledge Points and Processes

Based on the Systrace above, the basic Input event flow is as follows:

  1. InputReader reads the Input event.
  2. InputReader places the read Input event into the InboundQueue.
  3. InputDispatcher takes the event from InboundQueue and dispatches it to the OutBoundQueue of various Apps (connections).
  4. Simultaneously, the event is recorded in each App’s WaitQueue.
  5. The App receives the Input event, records it in PendingInputEventQueue, and handles its distribution.
  6. After processing, the App calls back InputManagerService to remove the corresponding Input from the WaitQueue.

Through this process, a single Input event is consumed (this is the normal case; many exceptions and details exist). This section explains several important knowledge points from this critical flow (some processes and diagrams reference Gityuan’s blog, linked in the References section).

InputReader

InputReader is a Native thread running in the SystemServer process. Its core functions are reading events from EventHub, processing them, and sending them to InputDispatcher.

The InputReader Loop flow is:

  1. getEvents: Reads events from EventHub (monitoring /dev/input), places them into mEventBuffer (size 256), and converts input_event to RawEvent.
  2. processEventsLocked: Processes events, converting RawEvent -> NotifyKeyArgs (or other NotifyArgs).
  3. QueuedListener->flush: Sends events to the InputDispatcher thread, converting NotifyKeyArgs -> KeyEntry (or EventEntry).

The core loopOnce processing flow is:

Logic of InputReader::loopOnce:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void InputReader::loopOnce() {
int32_t oldGeneration;
int32_t timeoutMillis;
bool inputDevicesChanged = false;
std::vector<InputDeviceInfo> inputDevices;
{ // acquire lock
// ...
// Get input events and device add/remove events; count is the event number
size_t count = mEventHub->getEvents(timeoutMillis, mEventBuffer, EVENT_BUFFER_SIZE);
{
// ...
if (count) { // Process events
processEventsLocked(mEventBuffer, count);
}
}
// ...
mQueuedListener->flush(); // Send events to InputDispatcher
}

InputDispatcher

After InputReader calls mQueuedListener->flush, Input events are added to InputDispatcher‘s mInboundQueue, waking it up. Systrace shows the InputDispatch thread being woken by InputReader.

Core logic of InputDispatcher:

  1. dispatchOnceInnerLocked(): Takes EventEntry from mInboundQueue. The start time of this method (currentTime) becomes the deliveryTime for subsequent dispatchEntry.
  2. dispatchKeyLocked(): May add doInterceptKeyBeforeDispatchingLockedInterruptible under certain conditions.
  3. enqueueDispatchEntryLocked(): Generates DispatchEntry and adds it to the connection’s outbound queue.
  4. startDispatchCycleLocked(): Takes DispatchEntry from outboundQueue and moves it to the connection’s waitQueue.
  5. InputChannel.sendMessage: Sends the message to the remote process via socket.
  6. runCommandsLockedInterruptible(): Iteratively processes all commands in mCommandQueue (added via postCommandLocked()).

Core processing in dispatchOnceInnerLocked:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void InputDispatcher::dispatchOnceInnerLocked(nsecs_t* nextWakeupTime) {
if (!mPendingEvent) {
if (mInboundQueue.isEmpty()) {
// ...
} else {
mPendingEvent = mInboundQueue.dequeueAtHead();
traceInboundQueueLengthLocked();
}

if (mPendingEvent->policyFlags & POLICY_FLAG_PASS_TO_USER) {
pokeUserActivityLocked(mPendingEvent);
}

resetANRTimeoutsLocked();
}
case EventEntry::TYPE_MOTION: {
done = dispatchMotionLocked(currentTime, typedEntry, &dropReason, nextWakeupTime);
break;
}

if (done) {
if (dropReason != DROP_REASON_NOT_DROPPED) {
dropInboundEventLocked(mPendingEvent, dropReason);
}
mLastDropReason = dropReason;
releasePendingEventLocked();
*nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately
}
}

InboundQueue

When InputDispatcher executes notifyKey, it encapsulates the Input event and places it in InboundQueue. InputDispatcher then retrieves events from here during its processing loop.

OutboundQueue

“Outbound” refers to events leaving for App processing. Each App (Connection) has an OutboundQueue. Events enter InboundQueue and are then dispatched by InputDispatcher to each App’s OutboundQueue.

WaitQueue

Once InputDispatcher dispatches an event, it moves the DispatchEntry from outboundQueue to WaitQueue. When the published event is “finished,” InputManagerService receives a reply and removes the event from WaitQueue. In Systrace, you’ll see the App’s WaitQueue length decrease.

If the main thread is janky, Input events aren’t consumed in time, which manifests in the WaitQueue:

Overall Logic

Diagram from Gityuan’s Blog

Input Refresh and Vsync

Input refresh depends on touch screen sampling. Many screens now use 120Hz or 160Hz, sampling every 8ms or 6.25ms. Let’s see how this looks in Systrace:

In the diagram, InputReader reads data every 6.25ms and hands it to InputDispatcher for dispatching. Is a higher sampling rate always better? Not necessarily. As shown, even if data is read every 6.25ms, the WaitQueue shows the App isn’t consuming it. Why?

The reason is that Apps consume Input events when the Vsync signal arrives. For a 60Hz screen, Vsync intervals are 16.6ms. If two or three Input events occur in that span, only the latest one is usually taken. Therefore:

  1. If screen refresh rate and system FPS are 60, blindly increasing sampling rate has little effect. It might even cause uneven event groups (two vs. three events per Vsync), leading to UI jitter.
  2. At 60Hz/60FPS, a 120Hz sampling rate is sufficient.
  3. At 90Hz/90FPS, 120Hz sampling isn’t enough; a 180Hz sampling rate should be used.

Input Debug Information

Dumpsys Input is primarily for debugging. We can check key information here if issues arise. Command:

1
adb shell dumpsys input

The output is extensive; we’ll focus on Device info, InputReader, and InputDispatcher.

Device Info

Lists currently connected devices; below is a touch-related snippet:

1
2
3
4
5
6
7
8
9
10
11
12
13
3: main_touch
Classes: 0x00000015
Path: /dev/input/event6
Enabled: true
Descriptor: 4055b8a032ccf50ef66dbe2ff99f3b2474e9eab5
Location: main_touch/input0
ControllerNumber: 0
UniqueId:
Identifier: bus=0x0000, vendor=0xbeef, product=0xdead, version=0x28bb
KeyLayoutFile: /system/usr/keylayout/main_touch.kl
KeyCharacterMapFile: /system/usr/keychars/Generic.kcm
ConfigurationFile:
HaveKeyboardLayoutOverlay: false

Input Reader Status

Current Input event display:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
Device 3: main_touch
Generation: 24
IsExternal: false
HasMic: false
Sources: 0x00005103
KeyboardType: 1
Motion Ranges:
X: source=0x00005002, min=0.000, max=1079.000, flat=0.000, fuzz=0.000, resolution=0.000
Y: source=0x00005002, min=0.000, max=2231.000, flat=0.000, fuzz=0.000, resolution=0.000
PRESSURE: source=0x00005002, min=0.000, max=1.000, flat=0.000, fuzz=0.000, resolution=0.000
SIZE: source=0x00005002, min=0.000, max=1.000, flat=0.000, fuzz=0.000, resolution=0.000
Keyboard Input Mapper:
Parameters:
HasAssociatedDisplay: false
OrientationAware: false
HandlesKeyRepeat: false
KeyboardType: 1
Orientation: 0
KeyDowns: 0 keys currently down
MetaState: 0x0
DownTime: 521271703875000
Touch Input Mapper (mode - direct):
Parameters:
GestureMode: multi-touch
DeviceType: touchScreen
AssociatedDisplay: hasAssociatedDisplay=true, isExternal=false, displayId=''
OrientationAware: true
Raw Touch Axes:
X: min=0, max=1080, flat=0, fuzz=0, resolution=0
Y: min=0, max=2232, flat=0, fuzz=0, resolution=0
Viewport: displayId=0, orientation=0, logicalFrame=[0, 0, 1080, 2232], physicalFrame=[0, 0, 1080, 2232], deviceSize=[1080, 2232]
Last Raw Touch: pointerCount=1
[0]: id=0, x=660, y=1338, pressure=44, ...
Last Cooked Touch: pointerCount=1
[0]: id=0, x=659.389, y=1337.401, ...

InputDispatcher Status

Key InputDispatch information includes:

  1. FocusedApplication: App currently in focus.
  2. FocusedWindow: Window currently in focus.
  3. TouchStatesByDisplay
  4. Windows: All Windows.
  5. MonitoringChannels: Channels corresponding to Windows.
  6. Connections: All connections.
  7. AppSwitch: pending status.
  8. Configuration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Input Dispatcher State:
DispatchEnabled: 1
DispatchFrozen: 0
FocusedApplication: name='AppWindowToken{... Launcher}', dispatchingTimeout=5000.000ms
FocusedWindow: name='Window{... Launcher}'
TouchStatesByDisplay:
0: down=true, split=true, deviceId=3, source=0x00005002
Windows:
0: name='Window{... Launcher}', pointerIds=0x80000000, targetFlags=0x105
Windows:
9: name='Window{... Launcher}', displayId=0, paused=false, hasFocus=true, visible=true, ...
MonitoringChannels:
0: 'WindowManager (server)'
Connections:
12: channelName='3c007ad launcher (server)', windowName='Window{... Launcher}', status=NORMAL, ...
OutboundQueue: <empty>
WaitQueue: length=3
MotionEvent(..., action=MOVE, age=17.4ms, wait=16.8ms
MotionEvent(..., action=MOVE, age=11.1ms, wait=10.4ms
MotionEvent(..., action=MOVE, age=5.2ms, wait=4.6ms
AppSwitch: not pending
Configuration:
KeyRepeatDelay: 50.0ms
KeyRepeatTimeout: 500.0ms

References

Portions of this article reference Gityuan’s blog. These articles provide deep dives into code details for those interested after finishing this piece.

  1. http://gityuan.com/2016/12/11/input-reader/
  2. http://gityuan.com/2016/12/10/input-manager/
  3. http://gityuan.com/2016/12/17/input-dispatcher/
  4. https://zhuanlan.zhihu.com/p/29386642

Attachments

Attachments for this article have been uploaded. Extract and open in Chrome:
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!”

  1. Blogger Intro: Includes personal WeChat and WeChat group links.
  2. Blog Content Navigation: A guide for my blog content.
  3. Curated Excellent Blog Articles - Android Performance Optimization Must-Knows: Welcome to recommend projects/articles.
  4. Android Performance Optimization Knowledge Planet: Welcome to join and thank you for your support~

One walks faster alone, but a group walks further together.

Scan WeChat QR Code

CATALOG
  1. 1. Table of Contents
  • Series Article Index
  • Main Content
  • Input in Systrace
    1. 1. InputDown Event Workflow in SystemServer
    2. 2. InputDown Event Workflow in App
      1. 2.1. App’s Pending Queue
      2. 2.2. Main Thread Processing Input Events
  • Key Knowledge Points and Processes
    1. 1. InputReader
    2. 2. InputDispatcher
    3. 3. InboundQueue
    4. 4. OutboundQueue
    5. 5. WaitQueue
    6. 6. Overall Logic
  • Input Refresh and Vsync
  • Input Debug Information
    1. 1. Device Info
    2. 2. Input Reader Status
    3. 3. InputDispatcher Status
  • References
  • Attachments
  • Other Addresses
  • About Me && Blog