Inside the Brain of Android: Unveiling the Power of ActivityManagerService.
We have reached the sixth stage of our journey into the deep, hidden and intricate world of Android. This time, we talk about the ActivityManagerService which I consider the real brain of the Android Operating System.
The ActivityManagerService (AMS) is a key system process in Android, responsible for managing components such as activities, services, content providers, and broadcast receivers. As a central part of the Android framework, AMS orchestrates core functions related to application lifecycles, process management, and memory optimization. It is launched by Android's init process as part of the SystemServer module and plays a critical role in maintaining system stability and functionality.
AMS manages the lifecycle of activities, ensuring smooth transitions through states like onCreate, onStart, onResume, onPause, onStop, and onDestroy. Using the ActivityStack, it tracks the activities, determines which one should be in the foreground, and manages tasks (collections of related activities) efficiently to provide a seamless user experience. This management directly affects how the system interacts with users, including launching activities or terminating unused apps.
Beyond activity management, AMS oversees system memory and processes, terminating background applications when necessary to free resources for foreground tasks. It is deeply integrated with Android's inter-process communication (IPC) system, enabling seamless data exchanges between apps and managing BroadcastReceivers and Services.
Lastly, AMS enforces Android's permission model, ensuring apps having the proper authorizations for specific actions. This safeguards system security, while maintaining compliance with Android's framework policies.
In essence, AMS acts as the brain of the Android operating system, coordinating activities, processes, tasks, and user interactions to deliver a robust and fluid experience.
This first article on AMS is divided into two parts. The first part explores the architecture of AMS, starting with an overview of its class diagram. The second part takes a closer look at the detailed sequence of AMS initialization within the system boot-up process.
AMS structure.
AMS is implemented primarily in Java and is an integral part of the system_server process.
The AMS is a highly complex class, but we’ll examine a few key sections to gain a better understanding of how it works.
We can find the AMS code under these two folders of AOSP project:
Many of AMS core functions are exposed through a binder interface so that they can be called by other processes on the system.
Below I have drawn a simplified diagram of the AndroidManagerService class hierarchy.
Here's a look at the classes it extends and the interfaces it implements (all the source code I will show is taken from the Android Open Source Project, version 12.1.0_r11):
- Watchdog.Monitor:
The Watchdog.Monitor interface in Android is a critical part of the Watchdog mechanism, which monitors the health and responsiveness of system services. The ActivityManagerService implements the Watchdog.Monitor interface to report its health status. During its initialization, AMS registers itself with the Watchdog by calling:
Watchdog.getInstance().addMonitor(this);
This ensures that the Watchdog periodically invokes the monitor() method on AMS. The monitor() method in AMS typically checks the health of its internal components, such as activity stacks, process states, and other critical subsystems. If any issue is detected (e.g., deadlocks or unresponsive threads), the method can log warnings or attempt recovery. Here a simplified implementation of monitor() method inside AMS:
@Override
public void monitor() {
synchronized (this) {
// Perform health checks on AMS components.
// For example, check if activity stacks or process states are healthy.
}
}
By implementing Watchdog.Monitor, AMS plays a vital role in maintaining system stability:
- BatteryStatsImpl.BatteryCallback:
The BatteryStatsImpl.BatteryCallback interface in Android is part of the BatteryStats framework, which tracks detailed battery usage statistics. The ActivityManagerService (AMS) implements this callback to monitor battery-related events and to respond to changes in battery statistics that might affect system processes, activity management, and power policies. Here is the BatteryCallback interface from BatteryStartsImpl.java:
public interface BatteryCallback {
public void batteryNeedsCpuUpdate();
public void batteryPowerChanged(boolean onBattery);
public void batterySendBroadcast(Intent intent);
public void batteryStatsReset();
}
The batteryNeedsCpuUpdate method is invoked when the battery statistics indicate that the system's CPU usage needs to be updated. The purpose is to ensure that AMS and related system components adjust their operations basing on updated CPU usage metrics. In the implementation, AMS updates the internal state or notifies components that rely on CPU usage data. This can involve recalculating process priorities or adjusting scheduling policies. Here is the implementation of the method in AMS:
@Override
public void batteryNeedsCpuUpdate() {
updateCpuStatsNow();
}
void updateCpuStatsNow() {
mAppProfiler.updateCpuStatsNow();
}
Later on analyzing the AMS constructor in detail, we will see the purpose of the AppProfiler component.
The batteryPowerChanged method is called when there is a change in the power source, such as switching between battery power and being plugged into a power source. The purpose is to adjust system behavior based on the power state. For example reducing resource-intensive operations when on battery power and resume normal operations when plugged in. Here is the implementation of the method in AMS:
@Override
public void batteryPowerChanged(boolean onBattery) {
// When plugging in, update the CPU stats first before changing the plug state.
updateCpuStatsNow();
synchronized (mProcLock) {
mOnBattery = DEBUG_POWER ? true : onBattery;
mOomAdjProfiler.batteryPowerChanged(onBattery);
}
}
The OomAdjProfiler is a component in AMS that plays a critical role in managing the system's Out-Of-Memory (OOM) adjustment mechanism. Its primary purpose is to monitor and profile how processes are assigned OOM adjustment values and to help in optimizing memory usage across the system (it is on the basis of the OOM values that AMS decides which application to terminate to free resources). Its batteryPowerChanged method recalculates app priorities to conserve power.
The batterySendBroadcast method is responsible to notify applications and system components about significant battery-related changes. Here is the implementation of the method in AMS:
@Override
public void batterySendBroadcast(Intent intent) {
synchronized (this) {
broadcastIntentLocked(null, null, null, intent, null, null, 0, null, null, null, null,
OP_NONE, null, false, false, -1, SYSTEM_UID, Binder.getCallingUid(),
Binder.getCallingPid(), UserHandle.USER_ALL);
}
}
The broadcastIntentLocked method of AMS ensures that the intent is sent system-wide to all components that have registered for such broadcasts. Common battery-related broadcasts include:Intent.ACTION_BATTERY_LOWIntent.ACTION_BATTERY_OKAYIntent.ACTION_POWER_CONNECTEDIntent.ACTION_POWER_DISCONNECTED.
The batterySStatsReset method is called when battery statistics need to be reset, typically during significant changes in the battery state (e.g. a full charge cycle). Here is the implementation of the method in AMS:
@Override
public void batteryStatsReset() {
mOomAdjProfiler.reset();
}
In the implementation of the batteryStatsReset() method in AMS, the OomAdjProfiler component is reset.
- ActivityManagerGlobalLock:
The ActivityManagerGlobalLock is a synchronization mechanism that AMS uses to guard shared resources. It ensures that only one thread can access critical sections of code at a time, which is crucial in a multi-threaded environment like Android.
This lock is not limited to just AMS; it is shared with other system services and components, such as WindowManagerService, to coordinate operations that span multiple subsystems.
The ActivityManagerGlobalLock is created during the initialization of AMS and shared across related services. Here’s how it is typically defined and initialized:
// The global lock for AMS, it's de-facto the ActivityManagerService object as of now.
final ActivityManagerGlobalLock mGlobalLock = ActivityManagerService.this;
AMS uses mGlobalLock in critical sections to synchronize access to its internal state. For example when AMS handling activity lifecycle events it synchronizes operations on mGlobalLock to prevent inconsistent states:
synchronized (mGlobalLock) {
// Access or modify activity-related data structures
........
}
AMS often collaborates with WindowManagerService, and both services synchronize on the same lock to avoid deadlocks during cross-service operations.
synchronized (mGlobalLock) {
windowManager.someOperation();
// Other AMS operations
}
- IActivityManager.Stub:
The ActivityManagerService extends IActivityManager.Stub, which is a key part of the inter-process communication mechanism in the Android framework. This extension allows ActivityManagerService to act as the server-side implementation of the IActivityManager interface, enabling clients (like applications or other system components) to interact with AMS through Binder. We discussed the Binder mechanism already in previous articles of this series (1.)(2.).
Let's do a quick review of the IBinder mechanism in the context of AMS. These are the components involved:
List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses();
@Override
public List<ActivityManager.RunningAppProcessInfo> getRunningAppProcesses() {
enforceNotIsolatedCaller("getRunningAppProcesses");
........
synchronized (mGlobalLock ) {
........
return mProcessList.getRunningAppProcessesLOSP(allUsers, userId, allUids,
callingUid, clientTargetSdk);
}
}
The IActivityManager.Stub class provides a mechanism for dispatching incoming IPC calls to the correct method in AMS. The onTransact method is overridden to delegate calls to the appropriate implementation.
@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
// Dispatch the IPC call to the appropriate method in AMS
return super.onTransact(code, data, reply, flags);
}
The onTransact method in IActivityManager.Stub handles incoming IPC requests. It maps the method calls to specific operations in AMS. Example:
Here is the sequence diagram of this flow:
The getRunningAppProcesses() method showcases how the Proxy-Stub mechanism works seamlessly to bridge client and server processes. The Proxy serializes the request and sends it to the server, where the Stub deserializes it and forwards it to the actual ActivityManagerService. The server processes the request, collects the data, and sends the response back through the same Proxy-Stub pipeline. This design ensures a robust and efficient communication mechanism while maintaining process isolation in Android.
AMS STARTUP.
Let's now begin an in-depth analysis of the AMS startup process.
Recommended by LinkedIn
The start-up of AMS is in the system server process.
We covered the system-service startup process in two previous articles.(1.)(2.).
AMS is started from the StartBootstrapServices method of SystemServe class.
Within the StartBootstrapServices method a new instance of the Lifecycle class is created. This class is responsible for encapsulating the AMS instance and providing controlled access to its initialization and start-up processes.
Here is the snippet from SystemServer.java.
private ActivityManagerService mActivityManagerService;
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
........
mActivityManagerService =
ActivityManagerService.Lifecycle.startService(systemContext, atmService);
........
........
mActivityManagerService.setSystemProcess();
........
}
Here’s what happens in this line:
The Lifecycle class is a nested static class within ActivityManagerService.java and it extends the SystemService. It acts as a wrapper around the lifecycle management of AMS. The purpose of this design is to decouple the instantiation and management logic of AMS from its core implementation, ensuring clarity and separation of concerns.
Here is the code snippet of Lifecycle from ActivityManagerService.java.
public static final class Lifecycle extends SystemService {
private final ActivityManagerService mService;
private static ActivityTaskManagerService sAtm;
public Lifecycle(Context context) {
super(context);
mService = new ActivityManagerService(context, sAtm);
}
public static ActivityManagerService startService(
SystemServiceManager ssm, ActivityTaskManagerService atm) {
sAtm = atm;
return ssm.startService(ActivityManagerService.Lifecycle.class).getService();
}
@Override
public void onStart() { mService.start(); }
........
public ActivityManagerService getService() { return mService; }
}
As we can see in the Lifecycle constructor, it is created an instance of ActivityManagerService and stored it for later management.
In the onStart method it calls the mService.start() to start the AMS service.
AMS constructor.
Now let's have a closer look at the ActivityManagerService constructor.
The constructor of the ActivityManagerService in Android is a critical piece of its implementation, as it sets up various components and subsystems necessary for the proper functioning of the Android operating system. So the ActivityManagerService constructor basically creates the main threads or processes and it also creates the objects to handle the four major components of Android, which are: the activities, services, content providers and broadcast receivers. Here’s a detailed breakdown of the key parts of this constructor and their roles.
Here is the code snippet of AMS constructor (from ActivityManagerService.java).
public ActivityManagerService(Context systemContext,
ActivityTaskManagerService atm) {
mInjector = new Injector(systemContext);
mContext = systemContext;
........
mHandlerThread = new ServiceThread(TAG,
THREAD_PRIORITY_FOREGROUND, false /*allowIo*/);
mHandlerThread.start();
// Main Handler
mHandler = new MainHandler(mHandlerThread.getLooper());
// UI Handler
mUiHandler = mInjector.getUiHandler(this);
........
mAppProfiler=new AppProfiler(this, BackgroundThread.getHandler().getLooper(),
new LowMemDetector(this));
........
mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
"foreground", foreConstants, false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
"background", backConstants, true);
mServices = new ActiveServices(this);
mCpHelper = new ContentProviderHelper(this, true);
........
mBatteryStatsService = new BatteryStatsService(systemContext, systemDir,
BackgroundThread.get().getHandler());
........
mUserController = new UserController(this);
mPendingIntentController = new PendingIntentController(
mHandlerThread.getLooper(), mUserController, mConstants);
........
mActivityTaskManager = atm;
mActivityTaskManager.initialize(mIntentFirewall, mPendingIntentController,
DisplayThread.get().getLooper());
mAtmInternal = LocalServices.getService(ActivityTaskManagerInternal.class);
Watchdog.getInstance().addMonitor(this);
Watchdog.getInstance().addThread(mHandler);
........
}
Let's see key components of the constructor:
final class MainHandler extends Handler {
public MainHandler(Looper looper) {
super(looper, null, true);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case GC_BACKGROUND_PROCESSES_MSG: {
........
} break;
case SERVICE_TIMEOUT_MSG: {
........
} break;
case SERVICE_FOREGROUND_TIMEOUT_MSG: {
........
} break;
case SERVICE_FOREGROUND_CRASH_MSG: {
........
} break;
case PROC_START_TIMEOUT_MSG: {
........
} break;
case CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG: {
........
} break;
case KILL_APPLICATION_MSG: {
........
} break;
........
........
}
}
}
final class UiHandler extends Handler {
public UiHandler() {
super(com.android.server.UiThread.get().getLooper(), null, true);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case SHOW_ERROR_UI_MSG: {
........
} break;
case SHOW_NOT_RESPONDING_UI_MSG: {
........
} break;
case SHOW_STRICT_MODE_VIOLATION_UI_MSG: {
........
} break;
case WAIT_FOR_DEBUGGER_UI_MSG: {
synchronized (mProcLock) {
........
} break;
........
........
}
}
}
Let's briefly summarize what we said about the ActivityManagerService constructor:
The ActivityManagerService constructor is central for initializing the AMS and its associated systems. It prepares the AMS to manage Android’s core functionalities, such as process management, activity lifecycle, and broadcast handling. Understanding this setup is essential to understand how AMS functions as the brain of the Android OS.
Now that we have completed this detailed analysis of the AMS constructor, let's return to the startBootstrapServices function in the SystemServer class. Below, I show just the line of code from startBootstrapServices where we left off in our analysis.
mActivityManagerService =
ActivityManagerService.Lifecycle.startService(systemContext, atmService);
As we can see, after creating the ActivityManagerService instance using the static Lifecycle class, the static startService method of Lifecycle is called. This, inside, invokes the start() method on the newly created ActivityManagerService instance.
Now let's have a look at the start method of AMS.
AMS start method.
This method initializes and links various subsystems and components necessary for AMS to operate effectively. Below is the start() method with an explanation of each step.
private void start() {
removeAllProcessGroups();
mBatteryStatsService.publish();
mAppOpsService.publish();
Slog.d("AppOps", "AppOpsService published");
LocalServices.addService(ActivityManagerInternal.class, mInternal);
LocalManagerRegistry.addManager(ActivityManagerLocal.class,
(ActivityManagerLocal) mInternal);
mActivityTaskManager.onActivityManagerInternalAdded();
mPendingIntentController.onActivityManagerInternalAdded();
mAppProfiler.onActivityManagerInternalAdded();
}
This detailed analysis of the start() method of the ActivityManagerService does not conclude our examination of the AMS startup process. One important element is still missing.
We must return to the startBootstrapServices method in the SystemServer class, as there is one more line of code to understand in order to make this analysis of the AMS startup process complete. Specifically, it is the last statement I previously included in the simplified snippet of the startBootstrapServices function. I will write it again below.
mActivityManagerService.setSystemProcess();
It is the call to the setSystemProcess method on the created AMS instance.
AMS setSystemProcess method.
The setSystemProcess() method in the ActivityManagerService is a critical function that sets up the system-level services, prepares system processes, and initializes various components of the Android system. Let’s break it down:
public void setSystemProcess() {
try {
ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL |
DUMP_FLAG_PROTO);
ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
ServiceManager.addService("meminfo", new MemBinder(this), false,
DUMP_FLAG_PRIORITY_HIGH);
ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
ServiceManager.addService("dbinfo", new DbBinder(this));
mAppProfiler.setCpuInfoService();
ServiceManager.addService("permission", new PermissionController(this));
ServiceManager.addService("processinfo", new ProcessInfoService(this));
ServiceManager.addService("cacheinfo", new CacheBinder(this));
ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
"android", STOCK_PM_FLAGS | MATCH_SYSTEM_ONLY);
mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());
synchronized (this) {
ProcessRecord app = mProcessList.newProcessRecordLocked(info,
info.processName, false, 0, new HostingRecord("system"));
app.setPersistent(true);
app.setPid(MY_PID);
app.mState.setMaxAdj(ProcessList.SYSTEM_ADJ);
app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
addPidLocked(app);
updateLruProcessLocked(app, false, null);
updateOomAdjLocked(OomAdjuster.OOM_ADJ_REASON_NONE);
}
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(
"Unable to find android system package", e);
}
}
Here is a simplified sequence diagram illustrating the AMS startup process.
That's all about the AMS startup. After our in-depth analysis of the Android Open Source Project code related to the modules involved in AMS startup, we can now provide a brief summary.
"The SystemServer initializes the ActivityManagerService through its internal Lifecycle class, ensuring a clean and modular setup process. The Lifecycle class encapsulates the instantiation, initialization, and management of AMS, delegating its core startup logic to the start() method. This design not only enhances the clarity and maintainability of the code but also aligns with Android's modular approach to system service management."
In the next episode we will dive deeper into AMS data structures like ProcessRecord, ActivityRecord and Tasks.
I remind you my newsletter "Sw Design & Clean Architecture" : https://lnkd.in/eUzYBuEX where you can find my previous articles and where you can register, if you have not already done, so you will be notified when I publish new articles.
Thanks for reading my article, and I hope you have found the topic useful,
Feel free to leave any feedback.
Your feedback is very appreciated.
Thanks again.
Stefano
References: