Technical update — 2026-10-13: The historical screenshots and code are retained. Code excerpts were checked against AOSP
android-10.0.0_r1; modern differences useandroid-16.0.0_r1as an example. These tags do not identify the screenshot devices: the original traces, ROMs and build details were not supplied. The descriptions of animation ownership, StartingWindow and thread interactions are corrected below.
This is the fourth article in the Systrace series, primarily providing a brief introduction to SystemServer. It covers several important threads within SystemServer. Since Input and Binder are particularly critical, they are discussed separately and won’t be covered in detail here.
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
- Main Content
- Window Animations
- ActivityManagerService
- WindowManagerService
- Input
- Binder
- HandlerThreads
- ServiceThread
- Modern Android Mapping
- 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
Separate processes, services and threads first. system_server hosts services such as AMS, ATMS and WMS. One service can run work on Binder threads and several Handler threads; services do not map one-to-one to threads. Here, android.ui is a system-service thread, not an app’s main thread. SurfaceFlinger runs in a separate process.
Read this SystemServer article together with Perfetto 10: Binder scheduling and lock contention. In old Systrace, SystemServer analysis focused on long slices around window animation, Input, AMS/WMS, Binder calls, and lock contention. In Perfetto, the same problems should be read across SystemServer threads, Binder thread pools, caller app thread states, CPU scheduling, and lock-wait evidence.
If the symptom is an app waiting for a system call, locate the corresponding SystemServer request and check whether the server thread is Running (executing), Runnable (waiting for CPU), or sleeping/blocked. Lock waiting needs additional evidence. If it is a Binder call, also check whether the Binder thread pool is queueing, whether the caller is blocked by synchronous Binder, and whether CPU scheduling, wakeups and run queues contribute to the delay. This separates “SystemServer is slow” into window animation work, AMS/WMS lock holding, remote Binder execution, and CPU scheduling delay.
Window Animations
Window animations are a useful entry point into system_server, but window management, animation calculation, app drawing and final composition are separate responsibilities. Historical implementations commonly involve android.anim and android.anim.lf (lowercase l and f). Remote implementations such as Launcher can participate in some transitions; not all animations execute on these two threads.
For an icon launch, Launcher initiates the launch and may participate in its animation; system window management decides whether to show a StartingWindow. In AOSP Android 10, AppWindowToken makes the relevant decisions and schedules starting-surface creation. Launcher does not create the app’s StartingWindow itself. App frames, starting-surface removal and transition playback have synchronization conditions; they do not form one universal “Launcher animation ends, then the app window animation begins” sequence.
The historical case is retained below. The first two images show Launcher3 (PID 2089) and Contacts (PID 26196), both with a 253.986 ms selection for comparing Launcher animation and app startup work. That selection is not automatically the full launch duration or input-to-first-frame latency.
Launcher Animation:
Correspondingly, the App is starting:
The Contacts image shows activityStart, activityResume and Choreographer#doFrame. The next image shows activity on android.anim and android.anim.lf. Its embedded “startup complete/switch” labels are retained as the original case interpretation; these crops alone do not establish physical presentation of the first frame or a universal transition handoff.

ActivityManagerService
AMS and WMS are common investigation points, but which service is busiest depends on the workload. Activity/process-management instrumentation often uses TRACE_TAG_ACTIVITY_MANAGER, with atrace category am. An ActivityManager track does not imply one dedicated AMS thread. Android 10 already has a separate ActivityTaskManagerService for activities and task containers; do not attribute all modern Activity management to AMS.
The historical Start proc: sample.tencent.matrix slice reports 14.962 ms Wall Duration and 0.678 ms CPU Duration. These describe that slice, not the app’s complete cold start, and do not mean 14.962 ms of CPU execution:
Various scenarios for processes and the four components have corresponding Trace points, such as activityStart, activityResume, and activityStop. Some reside in the application process and others in SystemServer, so analyzing Activity logic requires toggling between both processes to understand state changes and SystemServer‘s role.

WindowManagerService
WMS instrumentation commonly uses TRACE_TAG_WINDOW_MANAGER, with atrace category wm. A Binder window request can enter a system_server Binder thread, while layout, animation and follow-up work may run on Handler threads. This image shows relayoutWindow on Binder:925_9; WMS should not be equated with the Binder thread pool:

Various Window scenarios have Trace points like relayoutWindow, performLayout, and prepareToDisplay.

Input
The input pipeline spans device reading, dispatch, window routing and app consumption. InputReader and InputDispatcher are two important native threads in system_server, but not the whole pipeline. This is detailed in Systrace Basics - Input Interpretation and won’t be repeated here.

Binder
Since SystemServer provides numerous base services, inter-process communication (IPC) is heavy, and most of it uses Binder. Synchronous Binder waits, thread-pool queueing and shared locks can increase latency, but background-app count alone is not root-cause evidence. A oneway call does not wait for a synchronous reply, yet still incurs queueing and execution work. Follow the specific call, peer and lock owner rather than diagnosing jank from the number of Binder threads. This is detailed in Binder and Lock Contention Explained.

HandlerThread
The constructors and two usage excerpts below were checked against AOSP android-10.0.0_r1. They are internal Framework implementations, not public app APIs. Search-result screenshots show the source at the time, rather than a complete call-site inventory for every release.
BackgroundThread
com/android/internal/os/BackgroundThread.java
1 | private BackgroundThread() { |
BackgroundThread in Systrace:
BackgroundThread is a shared singleton HandlerThread within each process that uses it. It suits deferrable background work, not tasks with no performance requirements at all. A long task delays later work on the same queue. Check the PID as well as android.bg: this name alone does not identify system_server.

ServiceThread
ServiceThread inherits from HandlerThread. Several worker threads mentioned below inherit from ServiceThread, performing different functions with varying priorities: UiThread, IoThread, DisplayThread, AnimationThread, FgThread, and SurfaceAnimationThread.
Each started HandlerThread has its own Looper/MessageQueue and normally dispatches its queued work serially. Separate queues do not isolate shared locks, synchronous Binder calls, CPU contention or cross-thread waits. In this version, ServiceThread’s allowIo=false initializes a StrictMode thread policy; it is not a kernel prohibition on all I/O. Conversely, allowIo=true does not make work asynchronous or parallel.
The constructor priority is an Android/Linux nice priority, not java.lang.Thread’s 1–10 scale. In the Android 10 Process constants, smaller values generally have higher priority. Thread groups/cpusets, scheduler policy and later boosts also affect execution; the constructor does not freeze the runtime priority forever.
| Thread name | Constructor priority | Interpretation |
|---|---|---|
| android.bg | BACKGROUND = 10 | Deferrable work on a shared serial queue |
| android.ui | FOREGROUND = -2 | System-service UI thread, not an app main thread |
| android.io | DEFAULT = 0 | I/O-related work, not a parallel pool |
| android.display | DISPLAY + 1 = -3 | One step below -4 animation priority; not SurfaceFlinger |
| android.anim | DISPLAY = -4 | Historical WindowAnimator and related work |
| android.fg | DEFAULT = 0 | Its name does not imply the FOREGROUND nice value |
| android.anim.lf | DISPLAY = -4 | Some Surface animation work, still subject to waits and contention |
UiThread
com/android/server/UiThread.java
1 | private UiThread() { |
UiThread in Systrace: the UiHandler slice below has 39.383 ms Wall Duration and contains monitor-contention slices. Its 0.194 ms Self Time is not the total CPU time; the full span does not mean continuous execution.
UiThread is shared UI work infrastructure for system services, not system_server’s main thread or an app’s main thread. The search image includes AMS, PreBootBroadcaster and ATMS users. Their tasks still share a serial Looper; search UiThread.get() in the relevant version for exact responsibilities.

IoThread
com/android/server/IoThread.java
1 | private IoThread() { |
IoThread hosts I/O-related service work, but remains a serial HandlerThread rather than an unbounded parallel I/O pool. The image shows historical call sites; search IoThread.get() in the relevant revision.

DisplayThread
com/android/server/DisplayThread.java
1 | private DisplayThread() { |
DisplayThread in Systrace:

AnimationThread
com/android/server/AnimationThread.java
1 | private AnimationThread() { |
AnimationThread in Systrace:
Android 10 WindowAnimator initializes Choreographer on AnimationThread and schedules frame callbacks there; related paths still access the WMS global lock. SurfaceAnimationThread already exists in Android 9 / P and handles some Surface animation work separately. This does not guarantee lock-free or jank-free behavior for all window animations.

FgThread
com/android/server/FgThread.java
1 | private FgThread() { |
FgThread in Systrace:
FgThread serializes work that should not be subject to background scheduling restrictions. Its name does not imply THREAD_PRIORITY_FOREGROUND: the constructor uses DEFAULT. The following Android 10 UserController excerpt serializes credential-key eviction with related unlocking work to avoid data corruption. The mLock check finishes before lockUserKey is called:
1 | FgThread.getHandler().post(() -> { |
SurfaceAnimationThread
com/android/server/wm/SurfaceAnimationThread.java
1 | private SurfaceAnimationThread() { |
SurfaceAnimationThread in Systrace (named android.anim.lf):
The corresponding historical SurfaceAnimationThread call sites:

This thread handles some Surface animation work with less dependence on android.anim/WMS paths. “LockFreeAnimation” does not mean no locks exist: Android 10 SurfaceAnimationRunner still has mLock and mCancelLock, and posts completion callbacks back to AnimationThread. Historical further reading: Android P——LockFreeAnimation.
1 | SurfaceAnimationRunner( AnimationFrameCallbackProvider callbackProvider, |
The SurfaceAnimationRunner constructor above obtains that thread’s Choreographer on android.anim.lf through runWithScissors. getSfInstance() selects the SF Vsync source; it does not move Java animation code into the SurfaceFlinger process. SurfaceControl transactions still have to be submitted and processed by the display pipeline.
Modern Android Mapping
In AOSP android-16.0.0_r1, these thread classes still exist with comparable constructor names/priorities. Their presence does not mean the whole window-animation architecture is unchanged. ServiceThread and FgThread source now resides under core/java/com/android/server/; locate the other classes by their respective paths.
- Activity and window management: follow the specific AMS, ATMS and WMS entry points and executing threads. Confirm requests, waits and replies using PIDs/TIDs and Binder associations, rather than inferring a thread from a service name.
- StartingWindow: Android 16 StartingSurfaceController requests starting windows through TaskOrganizerController. This does not support the old “Launcher creates it” explanation.
- Transition animations: when Shell transitions are enabled, TransitionController cooperates with WM Shell Transitions, which may delegate to remote handlers. Inspect the actual Shell/Launcher/SystemUI processes and threads rather than only android.anim.lf in system_server.
- Graphical state and timing: use Perfetto for scheduling, Binder and recorded frame data. Use Winscope when window/layer state, transactions or transition participants are needed. Configure the relevant sources and permissions separately; the historical screenshots cannot acquire this missing information retroactively.
Distinguish Wall Duration, CPU Duration and Self Time. Wall duration spans the slice; CPU time requires scheduling evidence; self time generally subtracts nested slices from the span. They are not interchangeable. After locating a waiter, identify the lock owner or Binder peer. A long slice, sleeping thread or collection of background apps alone does not establish the root cause.
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.
