AOSP Foundations
6 min read

The init Process in Detail

A deeper look at the Second Stage init, the property service, and the complex action trigger sequence.

You just powered on an Android device. The bootloader handed control to the kernel. First stage init ran and successfully mounted the core partitions from the ramdisk. Your device has a bare filesystem sitting in memory. Nothing is actually running. There are no services, no display, and no radio. How do you turn a static folder structure into a massive, multi-process operating system?

Why the Ramdisk Isn't Enough: The Second Stage init

The problem is orchestration. A mounted filesystem does nothing on its own. The kernel needs a master process to start all the background services in the exact right order.

That master process is the Second Stage init. It acts as the general contractor for the entire system startup. First stage laid the foundation by mounting the disk. Second stage brings in the plumbers and electricians by starting the event loop.

When the kernel transitions execution to user space, the init binary re-executes itself in a new phase. It sets up an epoll loop to listen for kernel signals and system events. This loop waits for specific triggers and responds by launching specific daemons.

Tip: First stage and second stage init are exactly the same executable binary. They simply behave differently based on the boot phase arguments they receive.

The following flowchart illustrates the transition of execution from the kernel through the two phases of init. This visualization clarifies the exact point where static mounting hands off to active event management.

The kernel starts the first stage to handle mounts, which then pivots to the second stage. Our second stage establishes the continuous event loop that drives the rest of the boot sequence.

To manage this massive orchestration, init needs a globally accessible state machine to track what is ready. This requirement brings us to the Property System.

The Property System: Android's Global State Machine

Different daemons must communicate state before Binder IPC is fully functional. A graphics service cannot start until the display hardware verifies its readiness. The system needs a way to signal these dependencies instantly.

The Android Property System solves this problem. It acts as a giant whiteboard in a factory. Any worker can read the board, but only the supervisor can update it securely.

During the second stage, the init process reads configuration files and loads them into a shared memory dictionary. Properties serve as active execution triggers. When a daemon writes a new value to a property, the init event loop detects the change and fires any associated actions.

The following sequence diagram demonstrates how a simple command triggers a service launch through the property system. This visualization reveals the indirect mechanism behind service activation.

The adb shell writes the start command into the property system. Our init process detects this change and directly executes the surfaceflinger daemon.

You can observe this behavior directly from the command line. Running a command like adb shell setprop ctl.start surfaceflinger tells the system to manually trigger the graphics daemon.

Warning: Properties are not just simple text files that the system reads on demand. They exist as an active, memory-mapped dictionary that the init event loop constantly watches.

These variables provide the triggers, but developers define the actual work in specialized configuration files called .rc scripts.

Actions and Services: The Android Init Language

The init process needs explicit instructions on exactly which processes to launch and when. It cannot guess the correct permissions or restart rules for hundreds of vendor specific daemons.

Developers define these rules using the Android Init Language. This configuration format lives in .rc scripts spread across the system partitions. The syntax breaks down into two fundamental building blocks: Actions and Services.

Actions execute a sequence of commands once when a specific trigger occurs. Services define executable programs that the system launches and maintains as background daemons.

on post-fs-data
    mkdir /data/vendor/wifi 0770 wifi wifi
    mkdir /data/misc/camera 0770 camera camera

This action tells the system to create private directories immediately after the data partition mounts.

service surfaceflinger /system/bin/surfaceflinger
    class core
    user system
    group graphics drmrpc
    onrestart restart zygote

Our service rule instructs the system to launch the surfaceflinger binary as the system user.

Common Mistake: Beginners frequently confuse Actions and Services. Actions run a set of commands exactly once, whereas Services are monitored and kept alive indefinitely by the init process.

Vendors write hundreds of these scripts, but to prevent dependency chaos, init enforces a strict, hardcoded order of execution.

The Trigger Sequence: Enforcing Boot Order

If there are hundreds of script files, the system must avoid race conditions during boot. A daemon requiring network access will crash if it starts before the network interface exists. The system needs guaranteed checkpoints.

The init process enforces an immutable sequence of triggers. This hardcoded C++ sequence guarantees base systems are fully ready before complex daemons start.

The sequence progresses through five distinct phases. Our early-init phase handles basic memory management and cgroups setup. The init phase covers general system setup. Our fs phase mounts the remaining file systems. The post-fs-data phase executes after the system mounts and decrypts the data partition, allowing native daemons to create their private folders. Finally, the boot phase signals that the base native environment is completely ready.

The following flowchart outlines the built-in sequence of boot phases. This visualization highlights the dependencies required to reach a stable state.

Each phase must complete its required tasks before the system allows the next phase to begin. Developers must bind their custom daemons to the correct phase to avoid missing dependencies.

Once the boot trigger fires, the native environment fully stabilizes. The system is finally ready to awaken the massive Java world.

The init process transforms a static filesystem into a living operating system. Our second stage establishes an event loop to orchestrate this massive startup effort. The property system acts as a global state machine to trigger actions defined in configuration scripts. A strict sequence of hardcoded phases ensures every component starts in the correct order.

The boot trigger has fired. Native daemons are running successfully. There are still no applications visible to the user. How does Android transition from native C++ daemons to the rich Java framework? Enter the Zygote.