AOSP Framework & Internals
6 min read

ActivityManagerService (AMS)

The undisputed brain of Android. Understand how the AMS orchestrates processes, lifecycles, and the dreaded OOM Killer.

Bridging the Kernel's Semantic Gap

The Linux kernel understands memory allocations, threads, and processes. It has absolutely no concept of an Android application. When a device runs low on RAM, the kernel looks for large processes to terminate. Left alone, the standard Out of Memory killer will blindly destroy a background music player simply because it consumes significant memory. The kernel does not know the user is actively listening to that music.

Android solves this semantic gap with the ActivityManagerService. Known as the AMS, this core system component acts as the undisputed brain of the Android framework. It tracks exactly what the user is doing and maintains the state of every running application.

The AMS translates high-level Android concepts like Activities and background syncs into low-level Linux process priorities. As the user navigates between screens, the AMS continuously evaluates the importance of every application. It then updates the kernel with these priorities. The kernel relies on this guidance to know which processes are critical and which can be safely destroyed.

This translation layer allows Android to orchestrate complex lifecycles without requiring heavy modifications to the mainline Linux kernel. The operating system handles memory intelligently based on user intent. This priority system works perfectly until the system genuinely runs out of memory. When that happens, the kernel needs specific instructions on exactly who dies first.

Calculating Process Expendability

We need a mechanism to explicitly tell the Linux kernel which processes are safe to kill. The kernel requires a simple numerical ranking. It cannot parse complex UI states or backstack histories.

The AMS provides this ranking by calculating an Out of Memory adjustment score for every process. Engineers call this the oom_adj score. This single number dictates the expendability of an application. A lower number means the process remains critical to the user. A higher number marks the process as a prime candidate for immediate termination.

The following flowchart illustrates how the AMS assigns these priorities based on the application state. Visualizing this flow reveals how the AMS groups processes into distinct categories to prevent the kernel from making blind guesses. Pay attention to how the scores increase as the application becomes less critical to the immediate user experience.

The AMS writes these scores directly into the kernel via the /proc/[pid]/oom_score_adj file. A foreground application receives a score of zero, making it nearly immune to termination. A background service receives a mid-tier score, while minimized apps get pushed into the highest danger zone. During severe memory pressure, the Android Low Memory Killer simply reads these files and destroys the processes with the highest numbers.

You can view these raw priority scores in real time on any device. Running the following command outputs a list of all running processes alongside their current oom_adj values.

adb shell dumpsys activity oom

Common Mistake: Engineers often assume a background service runs indefinitely. Monitoring this output proves how quickly a background service score drops into the danger zone under memory pressure.

This priority system keeps active applications alive. However, it does not explain how those apps start in the first place. That responsibility requires an entirely different cross-process workflow.

Orchestrating the Launch Sequence

When a user taps an application icon, the target process usually does not exist yet. The system must translate that single tap into a running process with a visible user interface. The Launcher cannot do this alone. It lacks the privileges to create processes or resolve intents.

The AMS acts as the central coordinator for the entire cold start sequence. It bridges the gap between the Launcher, the package manager, and the process spawner. The AMS ensures that every component fires in the exact right order.

The following sequence diagram illustrates the Inter-Process Communication required to launch a cold application. Visualizing this exchange reveals how many process boundaries the system must cross just to display a single screen. Pay attention to how the AMS acts as the central hub, delegating the actual process creation to the Zygote.

The Launcher initiates the startup by sending an Intent to the AMS via Binder IPC. The AMS consults the PackageManagerService to identify the exact component to launch. If the application process does not exist, the AMS opens a local socket connection to the Zygote and commands it to fork a new virtual machine. Once the new process initializes, it immediately calls back into the AMS to provide a Binder token. Finally, the AMS uses this token to trigger the onCreate() and onResume() lifecycle callbacks.

This complex dance explains why cold starts require significant time and CPU cycles. The AMS ensures these steps happen safely across multiple security boundaries. Over time, coordinating both process creation and complex UI transitions pushed the AMS beyond its architectural limits.

Breaking the Monolith

Early versions of Android forced the AMS to manage everything. It handled memory, broadcast receivers, content providers, and complex window states. Over many years of development, the AMS grew unmanageable. It became a monolithic class often exceeding thirty thousand lines of code.

To improve maintainability, platform engineers split the AMS apart. They created a new system service called the ActivityTaskManagerService. Engineers refer to this new component as the ATMS.

The ATMS now exclusively handles the Activity backstack, recent tasks, and window configurations. The AMS retains responsibility for the core process lifecycle, OOM adjustments, and broadcast dispatching. They communicate constantly, but their responsibilities remain strictly separated.

This separation of concerns makes the Android operating system significantly more modular. It allows window management changes to happen without risking the stability of core process memory management. Modern AOSP development requires understanding this split, as many legacy AMS responsibilities have now shifted to the ATMS.

The AMS manages the process, while the ATMS organizes the backstack. But organizing a backstack does not put pixels on a physical display. The system still needs a way to take these logical Activity states and draw actual windows on the screen. That problem introduces the complex territory of the WindowManager.