Zygote
system_serverintermediate

Activity and task management

Two services divide the work of running apps: ActivityTaskManager owns activities and the back stack, and ActivityManager owns processes and everything else. The split explains most of what a launch actually costs.

12 minAPI 1–37 · 17interactivequizverified 2026-10-27

startActivity() looks like a local call. It is not. It is a request to a process you do not control, which has to decide whether you are allowed to make it, which app should handle it, whether that app is already running, and where the resulting screen goes in the back stack.

TL;DR#

  • owns activities, tasks and the back stack. owns processes, services, broadcasts, providers and OOM importance.
  • The two were one class until Android 10 (API 29Android 10), when ActivityTaskManagerService was split out of ActivityManagerService.1
  • Apps still call most public APIs through ActivityManager, but the internal Binder name for the activity side is activity_task, not activity.
  • A launch is asynchronous from the caller's point of view: startActivity returns when the system accepts the request, not when the target is visible.
  • The app side of the conversation is ActivityThread and its ApplicationThread Binder object. The system calls into the app to make it create and resume an activity.

Mental model#

Imagine a theatre.

The stage manager decides what is on stage, in what order, and what comes back when a scene ends. That is ActivityTaskManager: the running order, the set changes, the queue of scenes behind the current one.

The company manager hires the actors, decides who is in the building, sends people home when the budget runs out, and keeps a list of who is essential to tonight's show. That is ActivityManager: processes, importance, and the decision to kill.

They work in the same office because they need the same information. The stage manager cannot decide whether the next scene is possible without knowing whether the actors are already in the building.

Where the analogy breaks#

A theatre has one stage. An Android device has several — split screen, picture-in-picture, multiple displays — and a task can be visible but paused, or running but not visible, or cached and resumable. The state machine is much larger than "on stage" and "off stage", which is why dumpsys activity activities prints so much.

The big picture#

Loading diagram…
Text description of this diagram

A sequence diagram for an activity launch. The calling app calls startActivity, which reaches Instrumentation in the same process, which makes a Binder call to ActivityTaskManager in system_server. ActivityTaskManager checks the caller and resolves the intent, possibly by asking PackageManager. If no suitable process exists, ActivityManager asks Zygote to fork a new process, which specialises into the target app and runs ActivityThread.main. The system then sends bindApplication and launchActivity transactions to the new process. The target runs onCreate and setContentView. Meanwhile the caller's startActivity has already returned — it returned when the request was accepted, not when the activity appeared.

Read the diagram from both ends. The caller's thread does almost nothing: one Binder transaction and it is done. The target's thread does all the expensive work: class loading, application creation, layout inflation, measurement, drawing. That asymmetry is the design. A launcher must never wait for another app's layout pass.

Deep dive#

The split, and why it happened#

Before Android 10, ActivityManagerService was one very large class that managed both processes and activities. Those are different problems with different state:

ActivityTaskManagerActivityManager
OwnsActivities, tasks, the back stack, launch modesProcesses, services, broadcasts, providers
Binder nameactivity_taskactivity
Key stateTask, ActivityRecord, TaskFragmentProcessRecord, ServiceRecord, ConnectionRecord
ImportanceWhich activity is visible and focusedWhich process is worth killing
Public reachActivityTaskManager (added API 29)ActivityManager (API 1)

The split made each service smaller, gave each its own lock (the activity manager's global lock was a notorious contention point), and let the two evolve at different speeds. It also created a persistent confusion: apps that want activity information often still go through ActivityManager, which forwards to the task manager internally.

What a launch actually does#

The interesting part of startActivity is the set of decisions made between the caller and the target. They happen in a class called ActivityStarter, and they are roughly:

  1. Is the caller allowed? Background activity starts have been restricted since API 29Android 10.2 A caller without a visible window, a recent notification or a foreground service may simply be refused.
  2. Resolve the intent. For an explicit intent, the component is already known. For an implicit one, PackageManager matches intent filters and the caller's package visibility.
  3. Find or create the task. Launch modes and flags decide whether this goes into the current task, a new task, or an existing task with a matching affinity.
  4. Find or create the process. If the target app is already running, reuse it. If not, ActivityManager asks Zygote to fork.
  5. Create the activity record and the window. The window is created before the activity, because the window is what the system needs in order to lay out and animate.
  6. Tell the app to launch. A ClientTransaction carries the sequence of lifecycle items to execute.3

Steps 1 to 5 happen in system_server. Only step 6 crosses into the app, and it crosses as an asynchronous transaction — the system does not block waiting for onCreate to finish.

The app side: ActivityThread and ApplicationThread#

Every app process has exactly one ActivityThread. Despite the name it is not a thread you create; it is the object that runs your app's main thread and owns the message loop.

The system does not call your Activity directly. It calls a Binder object called ApplicationThread, which is an inner class of ActivityThread, and that object posts work onto the main thread's Handler. This indirection is the whole point: the Binder thread that receives the transaction is not the main thread, and lifecycle callbacks must run on the main thread.

The modern transport is ClientTransaction. A transaction is a list of ClientTransactionItems — LaunchActivityItem, ResumeActivityItem, PauseActivityItem, StopActivityItem, DestroyActivityItem — executed in order. It replaced a series of one-off Binder methods (scheduleLaunchActivity, scheduleResumeActivity, …) with a single transaction type, which made it possible to batch and to reason about ordering.

Tasks, launch modes and the back stack#

A is a stack of activities that the user experiences as one thing. The back stack is the ordered set of tasks. The rules are set by launchMode in the manifest and by intent flags at runtime, and the combinations are the source of most navigation bugs in real apps.

The four classic launch modes are standard, singleTop, singleTask and singleInstance. They control whether a new instance is created, whether an existing instance is reused, and whether the activity lives in its own task. singleTask in particular is frequently misunderstood: it means "at most one instance in the system, in a task of its own", not "don't create duplicates in this task".

Process importance: the other half#

Everything above is about activities. The other service is about the processes those activities live in, and its central output is a number: the .

ActivityManagerService computes an oom_adj (and, on modern kernels, an oom_score_adj) for every process, based on what it is doing. A process with a visible activity is important; a process with only a cached activity is not. The kernel's low-memory killer and the userspace lmkd daemon use that number to decide who dies first.

The computation is continuous, not on-demand. Every time an activity starts, stops or is destroyed, the importance of every process that might be affected is recalculated. That is why dumpsys activity processes is so long: it is a snapshot of a system that is always recomputing.

The pieces you can actually watch#

dumpsys activity is the window into all of this, and it has subcommands that map onto the split:

  • dumpsys activity activities — tasks, activity records, their states, and which is resumed.
  • dumpsys activity processes — every process, its oom_adj, and why it has that value.
  • dumpsys activity services — running services and their bindings.
  • dumpsys activity broadcasts — registered receivers and pending broadcasts.

The volume is large because the state is large. The useful habit is to read one section at a time and to look for the reason field, which is the system explaining its own decision.

How it evolved#

Architecture changes29 releases
APIVersionWhat changed
11.0
  • Initial AOSP platform: Linux kernel, Dalvik VM, core libraries
  • The first zygote-based app spawning model
41.6 Donut
  • Support for different screen densities via resource qualifiers
52.0 Eclair
  • First multi-core-friendly scheduling and graphics work
82.2 Froyo
  • Trace-based JIT compiler added to Dalvik
  • Support for x86 and ARMv7 optimisations
92.3 Gingerbread
  • Concurrent garbage collector added to Dalvik (GC pause reduction)
113.0 Honeycomb
  • Hardware acceleration for the View system (opt-in per app)
133.2 Honeycomb
  • New screen-size resource qualifiers for compatibility scaling
144.0 Ice Cream Sandwich
  • One system image for phones and tablets
  • Hardware acceleration on by default
164.1 Jelly Bean
  • Project Butter: vsync, triple buffering, Choreographer
  • RenderThread architecture begins
174.2 Jelly Bean
  • Multi-user support on tablets
  • SELinux enforcement begins in the kernel (behind the scenes)
184.3 Jelly Bean
  • SELinux enforcement rolled out more broadly
  • Binder changes for restricted profiles
194.4 KitKat
  • Android Runtime (ART) appears as an experimental runtime
  • Kernel moved under a tighter vendor control
204.4W KitKat (Wear)
  • First time the platform shipped for a new form factor
215.0 Lollipop
  • ART becomes the only runtime: ahead-of-time compilation at install
  • 64-bit ARM and x86 support
  • RenderThread and OpenGL ES 3.1
  • Project Volta and JobScheduler
  • SELinux fully enforcing
225.1 Lollipop
  • Device protection ties verified boot to Google accounts
236.0 Marshmallow
  • Runtime permission model changes how every app asks for access
  • Doze batches background work
  • Fingerprint HAL
  • Adoptable storage
247.0 Nougat
  • Project Treble groundwork
  • A/B seamless updates
  • Hybrid JIT + AOT compilation in ART
  • MediaServer split into sandboxed processes
268.0 Oreo
  • Project Treble: the vendor interface separates vendor and system code
  • Background execution limits
  • Broadcast and service restrictions
  • HIDL for HALs
278.1 Oreo
  • Android Go: a lighter configuration of the same platform
289 Pie
  • App Standby Buckets
  • Mainline groundwork
  • Non-SDK interface restrictions (greylists) begin
  • BiometricPrompt
2910
  • Project Mainline: APEX and updatable system modules
  • Scoped storage
  • Bubbles
  • Project Mainline starts shipping through Play
3011
  • GKI: the Generic Kernel Image splits the kernel into core + vendor modules
  • App hibernation
  • Package visibility filtering
  • Incremental app installs
3112
  • Bluetooth permissions split into the nearby-devices family
  • Exact alarm permissions
  • Foreground service launch restrictions
3212L
  • Window size classes and activity embedding
3313 Tiramisu
  • Granular media permissions replace READ_EXTERNAL_STORAGE
  • The notification permission becomes a runtime permission
  • Predictive back gesture
3414 Upside Down Cake
  • Foreground service types are mandatory
  • Selected photos access via READ_MEDIA_VISUAL_USER_SELECTED
3515 Vanilla Ice Cream
  • 16 KB page size support for native libraries
  • Edge-to-edge is enforced when targeting 35
3616 Baklava
  • The release moves to Q2 (June) with a Q4 feature drop
  • Two SDK releases per year starting with 36
  • Desktop windowing arrives in developer options then stable
3717 Cinnamon Bun
  • Strict per-device app memory limits are enforced by the system
  • Generational garbage collection is added to ART's Concurrent Mark-Compact collector
  • A lock-free MessageQueue for apps targeting API 37
  • Compose-first: new platform and Jetpack APIs target Compose only; View libraries enter maintenance mode
  • Hybrid APK signing (v3.2) combines classical and ML-DSA post-quantum signatures
  • Native library dynamic code loading must be read-only

The trajectory is consistent: fewer implicit behaviours, more explicit state, more of the decision recorded rather than reconstructed. Background activity starts were restricted at API 29Android 10. PendingIntent mutability became mandatory at API 31Android 12. Android 17 (API 37Android 17) stops recreating activities for keyboard, navigation and colour-mode configuration changes by default, which removes a whole class of "why did my activity restart" questions.

Under the hood#

Curiosity corner#

  • ActivityThread is not a Thread. It extends Object and runs on the main thread; the name is a historical artefact from a design where it may have been one.
  • The launcher is just an app. It starts activities through exactly the same path your app uses, with no special API. The only difference is which permissions it holds.
  • A "cold start" is mostly not your code. Process fork, application creation and resource loading usually dominate onCreate; the drawing is a smaller share than most profiling sessions suggest.

Try it#

Watch a launch happen

adb shell dumpsys activity activities | head -60 adb shell dumpsys activity processes | grep -A4 "com.example.app"

Example output
ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)
* Task{7f3a1c2 #1234 type=standard A=10123:com.example.app}
  * ActivityRecord{8a2b3c4 u0 com.example.app/.MainActivity t1234}
    state=RESUMED visible=true

The A= field is the app's UID; t1234 is the task id. In the process dump, look for oom= and adj= — those are the numbers the low-memory killer reads.

Check yourself#

Check yourself

1 / 12

How does system_server's process come into existence at boot?

0 answered · 0 correct

Sources#

Sources

Every version number, API level, class name and date on this page is traced to one of these. Pages are re-verified against them; last_verified above says when.

  1. docsAndroid platform architecture (AOSP)source.android.comaccessed 2026-10-27↩
  2. docsPerubahan perilaku: aplikasi yang menargetkan API level 28+developer.android.comaccessed 2026-10-27↩
  3. docs活動簡介 | App architecturedeveloper.android.comaccessed 2026-10-27↩
  4. docsالمهام وحزمة التطبيقات الخلفية | App architecturedeveloper.android.comaccessed 2026-10-27↩
  5. docsProcesses and threads overview | App qualitydeveloper.android.comaccessed 2026-10-27↩
  6. docsZygotesource.android.comaccessed 2026-10-27↩
  7. docsIntents and intent filters | App architecturedeveloper.android.comaccessed 2026-10-27↩