A detailed code walkthrough of the Android boot process, covering BootROM, Bootloader, Kernel, Init, Zygote, SystemServer, and SystemUI, with references to key AOSP source files.
The Android boot process is a complex, multi-stage journey that begins with the hardware-level operations and ends with the familiar Android home screen. Each step in this process involves several critical components, from the BootROM and bootloader to the Android runtime and SystemServer. While much of the early boot process is specific to the hardware, the open-source nature of Android allows us to explore the latter stages through its codebase.
In this article, we will look at the Android boot process through a code walkthrough. We will begin by understanding the conceptual roles of the BootROM and bootloader, then dive into the core Android components like the Linux kernel, the init process, and Zygote. By following the code flow from initialization to the launching of the home screen, we aim to provide a clear picture of how Android transforms from a powered-off state to a fully operational system.
Whether you are a developer working on Android internals or simply curious about how Android boots up, this guide will walk you through the process step by step, with references to key source files and functions in the Android Open Source Project (AOSP).
BootROM:
- This is the very first step in the boot process, and it’s embedded into the hardware. The BootROM code is stored in the chip (SoC) by the manufacturer and is non-modifiable. Its primary job is to initialize basic hardware like RAM and storage and to find and load the next stage bootloader from a predefined location (like an SD card or internal flash).
- Since BootROM is hardware-specific and deeply integrated with the SoC design, no source code is publicly available.
- BootROM usually performs the hardware checks, and its behavior differs based on the SoC. If there is a valid bootloader image, it passes control to it.
Bootloader:
- The Bootloader is the first piece of software that runs after BootROM. It’s responsible for setting up the environment for the Android kernel to run, including initializing hardware components and loading the kernel image from storage into memory.
- Depending on the manufacturer, bootloaders like U-Boot or proprietary ones (like Qualcomm’s aboot) are used.
- The bootloader typically handles features like boot partition verification and may include the ability to boot into recovery mode or fastboot mode for development and debugging.
Code Example: (for illustration)
Kernel:
- Once the bootloader finishes its job, it hands over control to the Linux kernel (which is highly customized for Android). The kernel initializes the system’s hardware, mounts the root file system, and starts the init process.
- The kernel can be customized by OEMs for their specific hardware, but generally, it contains Android-specific drivers (such as those found in /drivers/- * or /arch/arm64/).
- Kernel Command Line: When the kernel starts, it receives parameters from the bootloader (like root= which specifies the root partition).
Code Example: (hypothetical)
Init Process and main.cpp
File Location:[ main.cpp](https://cs.android.com/android/platform/superproject/main/+/main- :system/core/init/main.cpp)
The init process is the cornerstone of Android’s user-space initialization. This process begins in main.cpp, which is located in the system/core/init directory.
Key functions in main.cpp:
- main(): The entry point for the init process
- First, it sets a high process priority (setpriority()).
- It checks if the program name is ueventd. If true, it starts the ueventd process by calling ueventd- _main().
- Depending on the command-line argument (argv), it determines which function to call:
If subcontext is passed, it calls SubcontextMain().If selinux- _setup is passed, it calls SetupSelinux().If second- _stage is passed, it calls SecondStageMain().
- Otherwise, by default, the first stage of the initialization is handled by calling FirstStageMain().
FirstStageMain() (from first- _stage- _init.cpp)
File Location:[ first- _stage- _init.cpp](https://cs.android.com/android/platform/superproject/main/+/main- :system/core/init/first_stage_init.cpp)
This function handles the first stage of the Android boot process, where the basic system components are initialized.
Key steps in FirstStageMain():
- Initializes the basic system environment, such as setting up the kernel arguments and mounting essential filesystems.
- It calls PropertyInit(), which is responsible for initializing the Android property service (discussed later).
- Handles the early setup of SELinux by calling SetupSelinux().
- Once completed, it will transition to the second stage of the boot process.
SetupSelinux() (from selinux.cpp)
File Location:[ selinux.cpp](https://cs.android.com/android/platform/superproject/main/+/main- :system/core/init/selinux.cpp)
This function is responsible for setting up SELinux in Android. SELinux is a mandatory access control system that enforces security policies.
Key steps in SetupSelinux():
- It loads the SELinux policy and configures the system based on security contexts.
- Verifies and enforces SELinux modes like permissive or enforcing mode.
- This setup ensures that the necessary security contexts are applied before the Android system moves further in the boot process.
PropertyInit() (from property- _service.cpp)
File Location:[ property- _service.cpp](https://cs.android.com/android/platform/superproject/main/+/main- :system/core/init/property_service.cpp)
The property service in Android is responsible for system-wide properties, similar to environment variables, used to configure various aspects of the system.
Key steps in PropertyInit():
- It sets up the property service and loads initial system properties.
- Properties are essential for various components in Android, including system services and apps.
- This function initializes the service by reading property values from /system, /vendor, and other system partitions.
Second Stage Initialization (SecondStageMain())
After the first stage completes, the second stage is triggered by SecondStageMain(). This process includes:
- Starting additional system services.
- Running Android’s init scripts (from .rc files).
- Starting the zygote process, which spawns system processes.
system/core/rootdir/init.rc -
File Location:[ init.rc](https://cs.android.com/android/platform/superproject/main/+/main- :system/core/rootdir/init.rc)
This file is one of the main Android init scripts, and it defines the initial set of commands that the init process runs during system boot. It sets up the core environment, mounts filesystems, and starts essential services.
- Example entries in init.rc:
- on boot: Commands executed during boot, such as setting up SELinux and initializing properties.
- on zygote-start: This defines how to start the Zygote process, which is responsible for launching apps and system services.
After init.rc completes its setup, it proceeds to start the Zygote process by executing the zygote service defined in init.zygote64.rc.
init.zygote64.rc
File Location:[ init- _zygote.rc](https://cs.android.com/android/platform/superproject/main/+/main- :system/core/rootdir/init.zygote64.rc)
This script focuses on starting the 64-bit Zygote process, which is responsible for forking new processes, including system services and apps.
This file is an init script written in the Android init language. It defines various commands that need to be executed by the init process during system startup.
Key steps:
- The .rc file is parsed and executed by the init system during the second stage of the boot.
- init.zygote64.rc specifically defines how the Zygote process (the process that spawns Android apps and services) should be started. It also handles starting the system server and other essential processes.
For example:
- The service zygote64 entry in init.zygote64.rc starts the Zygote process.
- Other entries in the file can define services, mount points, and configuration directives that the init process follows during the second stage. Explanation:
- The zygote service starts the Zygote process by calling /system/bin/app- _process64 with the necessary arguments.
- The — start-system-server argument instructs Zygote to start the SystemServer process after initialization.
At this stage, the Zygote process is initialized and starts listening for fork requests from the system to launch other processes. This leads to the app- _process and AndroidRuntime layers.
Code Flow Summary:
- main() in main.cpp: This is the entry point of the Android init process. It calls FirstStageMain() if no special command-line arguments are provided.
- FirstStageMain() initializes the base system environment, mounts necessary filesystems, and calls PropertyInit() to initialize the property service and SetupSelinux() to handle SELinux.
- SetupSelinux() loads and configures SELinux policies.
- PropertyInit() sets up Android’s property service, which allows different system components to interact through shared properties.
- After the first stage, SecondStageMain() is called to start system services and parse the init scripts, including init.zygote64.rc, which starts the Zygote process.
frameworks/base/cmds/app- _process/app- _main.cpp
File Location:[ app- _main.cpp](https://cs.android.com/android/platform/superproject/main/+/main- :frameworks/base/cmds/app_process/app_main.cpp)
This file is responsible for setting up and starting the Zygote process. It acts as the entry point for both the Zygote and SystemServer.
Key steps:
- The main() function in this file checks the arguments to determine whether it should start Zygote or SystemServer.
Zygote initialization:
If the zygote flag is set (based on arguments like — zygote), it invokes the ZygoteInit class, which handles the next phase of Zygote initialization.
This leads to the AndroidRuntime and ZygoteInit classes being initialized.
frameworks/base/core/jni/AndroidRuntime.cpp
File Location:[ AndroidRuntime.cpp](https://cs.android.com/android/platform/superproject/main/+/main- :frameworks/base/core/jni/AndroidRuntime.cpp)
This file is responsible for initializing the Android Runtime (ART) and starting the ZygoteInit process.
Key function:
Explanation:
- The start() function initializes the JVM (ART) and invokes the main() method of the ZygoteInit class in Java.
- This sets up the environment to start running Android’s Java-based system services.
This leads us to the Java-based ZygoteInit class.
ZygoteInit.java
File Location:[ ZygoteInit.java](https://cs.android.com/android/platform/superproject/main/+/main- :frameworks/base/core/java/com/android/internal/os/ZygoteInit.java)
This class is responsible for further initializing the Zygote process and starting the SystemServer.
Key steps:
Explanation:
- It sets up the Zygote process, enables DDMS (Dalvik Debug Monitor), and if — start-system-server was passed as an argument, it calls startSystemServer() to start the SystemServer process.
- It then enters the runSelectLoop(), where it listens for process forking requests.
This leads to the ZygoteServer and SystemServer classes.
ZygoteServer.java
File Location:[ ZygoteServer.java](https://cs.android.com/android/platform/superproject/main/+/main- :frameworks/base/core/java/com/android/internal/os/ZygoteServer.java)
This class handles the server-side of Zygote, which waits for requests to fork new processes.
Key method:
Explanation:
- The runSelectLoop() function runs a continuous loop to accept new connections (process requests) and forks them.
- This keeps the Zygote process running and responsive to requests to start new processes.
Zygote.java
File Location:[ Zygote.java](https://cs.android.com/android/platform/superproject/main/+/main- :frameworks/base/core/java/com/android/internal/os/Zygote.java)
The Zygote class provides static methods for forking new processes. It’s responsible for handling the actual process creation when requests are received.
Key function:
Explanation:
- This method invokes a native function nativeForkSystemServer() (implemented in C++), which forks the SystemServer process.
The SystemServer process is then initialized.
com- _android- _internal- _os- _Zygote.cpp
File Location:[ com- _android- _internal- _os- _Zygote.cpp](https://cs.android.com/android/platform/superproject/main/+/main- :frameworks/base/core/jni/com_android_internal_os_Zygote.cpp)
This file contains the native code that implements the forkSystemServer() method.
Key function:
com- _android- _internal- _os- _Zygote- _nativeForkSystemServer():
Explanation:
- It uses the fork() system call to create a new process for SystemServer.
- The child process becomes the SystemServer that will later start various Android services.
SystemServer.java
File Location:[ SystemServer.java](https://cs.android.com/android/platform/superproject/main/+/main- :frameworks/base/services/java/com/android/server/SystemServer.java)
The SystemServer is a crucial process that starts up core system services in Android. It handles the initialization of system services like the activity manager, window manager, and others.
Key functions:
main():
Explanation:
- startBootstrapServices(): Starts essential services like the activity manager, power manager, and package manager.
- startCoreServices(): Starts core system services, such as the battery service and alarm manager.
- startOtherServices(): Starts all other system services that aren’t part of the core.
- startSystemUi(): Finally, this method starts the SystemUI process, which manages the Android status bar, navigation, and system interface.
After SystemServer.java starts SystemUI via the startSystemUi() method, the launcher app (which is typically Launcher3 in AOSP) is started as part of the Android Activity Manager and Window Manager system. Here’s a breakdown of the sequence of function calls and events that lead to the launcher starting.
startSystemUi()
The method startSystemUi() is invoked within SystemServer.java:
Explanation: This method sends an intent to start SystemUI, which is responsible for things like the status bar, navigation bar, and other system-level UI components. SystemUI runs in its own process.
The launcher app is not started directly by SystemUI. It is started by the Android ActivityManagerService after the boot completes, which we will discuss later in this article.
SystemUI
1. startSystemUi()
The startSystemUi() method begins by creating an Intent object with the component set to the SystemUI service component, which is retrieved from PackageManagerInternal. It then calls context.startServiceAsUser(intent, UserHandle.SYSTEM), which launches the SystemUI service.
- The key component is retrieved by calling pm.getSystemUiServiceComponent(), which provides the component information for SystemUI.
2. Intent starts SystemUI
The startServiceAsUser() method starts the SystemUI process by sending the intent. This intent is directed to the SystemUI component, starting the SystemUIService.java in the SystemUI process.
- Context.startServiceAsUser() sends the intent to launch the SystemUIService.java class, which is a bound service that manages the initialization of SystemUI components.
3. SystemUIService.java
SystemUIService.java is one of the first classes that are executed in the SystemUI process. This service is responsible for coordinating and managing system-level UI components. The service also interacts with SystemUIApplication.java to start the required services for SystemUI.
- SystemUIService serves as a central service for starting and managing UI components like the status bar, lock screen, and other visual elements of the Android system.
4. SystemUIApplication.java
Once the SystemUIService is initiated, SystemUIApplication.java is executed. This class is responsible for the overall setup of the SystemUI application. In particular, it calls the startServicesIfNeeded() method, which bootstraps all the core SystemUI components.
- SystemUIApplication is the entry point for the SystemUI application and is responsible for starting the system services related to the UI.
5. startServicesIfNeeded() in SystemUIApplication.java
The startServicesIfNeeded() method within SystemUIApplication.java checks whether the essential services required for SystemUI are running. If not, it starts those services. These services include:
- StatusBar: Manages the status bar (battery, network indicators, notifications).
- NavigationBar: Manages the system navigation (back, home, multitask buttons).
- LockScreen: Manages the lock screen functionality.
This method ensures that all the system-level UI components are set up and running.
6. SystemUiFactory.java
In the process of starting various services in SystemUI, SystemUiFactory.java is often called to instantiate and configure specific services. For instance, if the SystemUIApplication needs to start a specific service or component dynamically, it can use SystemUiFactory to create it.
- SystemUiFactory is responsible for creating key services that are part of the system UI. It ensures that all necessary services are instantiated and running correctly.
7. WindowManagerService.onSystemUiStarted()
Back in the SystemServer.java, after starting the SystemUI service, the method windowManager.onSystemUiStarted() is called. This lets the WindowManagerService know that SystemUI has started, so it can continue with any tasks that depend on SystemUI being available.
- WindowManagerService manages the window system in Android, which includes tasks like rendering the system bars, displaying application windows, and handling transitions between activities.
- This call ensures that SystemUI is fully functional before continuing with other window-related operations.
ActivityManagerService — systemReady()
File Location:[ ActivityManagerService.java](https://cs.android.com/android/platform/superproject/main/+/main- :frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java)
The systemReady() method in ActivityManagerService is responsible for finalizing the system’s readiness, including launching the home (launcher) app.
Key function:
systemReady() in ActivityManagerService.java:
Starting Persistent Apps
startPersistentApps(PackageManager.MATCH- _DIRECT- _BOOT- _AWARE);
This part starts only encryption-aware persistent apps, not directly related to the launcher, but ensures essential system services are up.
- Enabling the Home Activity for System User The home activity (i.e., the launcher) is enabled for the system user if the property SYSTEM- _USER- _HOME- _NEEDED is set to true. This ensures that the system can always boot with a home activity:
- Starting the Home Activity (Launcher) for System User If the current user is the system user (i.e., UserHandle.USER- _SYSTEM), AMS calls startHomeOnAllDisplays(), which triggers the launcher (home app) to start. This method is key to starting the home screen after the system has booted:
- Resuming Top Activities:
mAtmInternal.resumeTopActivities(false /- * scheduleIdle - */);
This ensures the top-most activities, including the home/launcher, are resumed and brought to the foreground.
The Android boot process is a complex sequence involving low-level hardware initialization and high-level system setup. The journey begins with proprietary components like BootROM and Bootloader, transitions to the Linux kernel, and culminates in Android’s user-space initialization with init, Zygote, SystemServer, and the launcher.
By tracing through the Android source code, you can see how each component plays a role in bringing the system to life. This article covers the major steps from the kernel to launching the Android UI, providing code insights where applicable.