Init & Android services

Read init.rc and understand how native services start.

8 min readChapter 19 of 50Intermediate
In this chapter

Baseline: AOSP android-14.0.0_r1. Android init is PID 1 and performs early userspace setup and process supervision. Its service keyword means an executable process; a Java framework service is an object hosted inside a process. Those meanings connect during boot but must not be conflated.

Before you start#

Read Boot and partitions, Programs, Memory & System Calls, Find Your Way Around AOSP, and The Android Security Model for UID, groups, capabilities, and SELinux terms. No changes to init configuration are part of this chapter.

Learning goals#

You can explain first-stage init → SELinux setup → second-stage init; read a small init .rc declaration; distinguish process start from readiness; find an Android 14 source definition and call site; and identify why properties and service declarations do not grant authority by themselves.

The phases and the two kinds of service#

One init, three execution stages
  1. First-stage init

    Establish early mounts and the environment required for later setup.

  2. SELinux setup

    Load policy from the available platform/vendor environment.

  3. Second-stage init

    Initialize property/service handling, load boot scripts, and evaluate actions.

Source map. Simplified Android 14 source model. Re-execution changes stage; this is not three independent service managers. Mount/recovery details depend on configuration.

When the kernel launches /init (kernel init-binary notes), that process conventionally has PID 1. Android uses staged startup because early userspace needs to establish the environment in which the main system filesystems and security policy can be used. In the pinned Android 14 flow, think of the stages as:

text
early ramdisk / first-stage init
    → establish early devices and mounts needed to reach the main system
    → execute init for SELinux setup (selinux_setup)
    → execute init for second-stage startup (second_stage)
    → parse broader rc configuration; run actions; start services

These are modes/stages of Android's init implementation, not three unrelated service managers. The binary may re-exec itself with stage arguments as the root/mount arrangement changes. First stage consults device configuration and fstab information; SELinux setup loads policy from the available system/vendor environment (the selinux.cpp header comment describes /system/etc/selinux and /vendor/etc/selinux policy); second stage sets up the normal property/service environment and evaluates actions. Exact mount paths and the order of specific device work vary with system-as-root, recovery-as-ramdisk, vendor configuration, and release. This stage sequence is documented in the pinned Android 14 init README and implemented across main.cpp (stage dispatch), first_stage_init.cpp, selinux.cpp, and init.cpp (SecondStageMain); do not infer a retail device's behavior solely from this summary.

The word service has two meanings:

  • In an init .rc file, service name /path/to/program declares a process that init can start, stop, restart, and supervise (init README, Services).
  • A Java framework service (for example, Activity Manager) is an object implementing operations, usually hosted within a larger process such as system_server and exposed through Binder.

Init can start a host process. It does not implement all objects or methods hosted by that process. One process may host many Java Binder services, and a service may not be usable at the instant its process begins.

Read a small init declaration

Started does not mean ready
Started does not mean ready1. init action to Service process: start foo requests process startup. 2. init action to Consumer: action can continue before foo is ready. 3. Service process to Consumer: later: readiness protocol / registration.init actionService processConsumerstart foo requestsprocess startupaction can continue before foo is readylater: readinessprotocol / registration
  1. init actionService process

    start foo requests process startup

  2. init actionConsumer

    action can continue before foo is ready

  3. Service processConsumer

    later: readiness protocol / registration

Time runs downward. Dashed arrows show returns.

Read the text version
  1. init action → Service process: start foo requests process startup
  2. init action → Consumer: action can continue before foo is ready
  3. Service process → Consumer: later: readiness protocol / registration
Conceptual model. Illustrative asynchronous ordering, not a captured init trace. The specific readiness signal may be registration, a property, or a service-specific protocol.
text
# Teaching illustration; not a device-ready rc file.
service demo-daemon /system/bin/demo-daemon
    class core
    user system
    group system
    disabled

on property:sys.boot_completed=1
    start demo-daemon

Here service begins a declaration, followed by an init service name and executable path. The option keywords are documented in the init README. class groups init services for class operations; user/group set credentials; disabled prevents the ordinary class-start behavior from starting it automatically. The on property: action is scheduled when the property condition is met, and start requests process startup. This example is only a grammar illustration: real services need valid binaries, SELinux domains, capabilities, dependencies, triggers, and product-specific policy. A property name does not authorize arbitrary writes, and placing a file on a partition does not make it privileged.

An init action is a sequence of init commands attached to a trigger. Commands in an action run in listed order; that does not promise all independent actions or process initialization finish globally in that order. start foo means “ask init to start foo,” not “foo has registered its API and is ready.” The init README says start is “not synchronous” (README). Readiness may be represented by a property, a Binder registration, or a service-specific protocol. A race exists if consumers assume startup equals readiness.

Identity and properties

An init service can be assigned a Linux UID (user ID), GID (group ID), supplementary groups, capabilities, environment, and SELinux domain (see the user, group, capabilities, setenv, and seclabel options in the init README). UID/GID affect ownership and discretionary access checks; capabilities grant selected privileged operations; SELinux domain is a mandatory access-control identity. These are different controls. Not every process runs as root, and UID 0 does not automatically bypass SELinux. Property access is also controlled by property contexts and SELinux. ro.* is conventionally read-only after its initial setting (property naming guidance), but the prefix is not a security policy by itself. Properties are configuration/state interfaces, not secrets or a general database.

Practice#

Continue into Properties and the control plane for the mechanism behind the short property example: shared-memory reads, peer-identified writes, property labels/types, ro.* write-once checks, persist.* storage, ctl.* control routing, and a readiness race. These are separate behaviors, not merely naming conventions.

Prerequisites: workstation with a pinned AOSP 14 checkout for source practice; browser access to Gitiles is an acceptable alternative. Runtime part additionally requires ADB Platform-Tools and a booted disposable Cuttlefish/emulator or an authorized device. Commands labeled HOST run on your computer; those prefixed adb shell invoke a command on the device. All commands are read-only.

Source-only trace

Open the Android 14 init README. Find the entries documenting service options, actions/triggers, and first-stage/second-stage behavior. In a checkout, from the AOSP workspace root:

sh
# HOST
rg -n 'FirstStageMain|SetupSelinux|SelinuxSetup|SecondStageMain|second_stage' system/core/init

Names are search terms, not guaranteed one-to-one call chain labels. Open matches and record each function's definition, caller, and re-exec/argument behavior. Then find the init rc declaration that starts zygote (search service zygote in the pinned tree) and record executable, class, and options. Expected: a small source map and an explicit statement of what the source does not establish for a vendor device. Failure: no match usually means wrong checkout/tag/path; verify pwd and tag rather than making a boot change.

Runtime observation (optional)

sh
# HOST; one target only
adb devices -l
adb shell getprop init.svc.zygote
adb shell getprop sys.boot_completed
adb shell ps -A -o USER,PID,PPID,NAME

Expected varies: init.svc.zygote may show running; sys.boot_completed often becomes 1 (set by the framework, not init); process-list columns differ by Android build. Empty values or denied ps options are not proof that init failed. unauthorized means stop and authorize only your own workstation. No target means stop and start the disposable device. The host shell does not process a device-side pipe here because each command has no pipeline. Never set properties or edit /system/etc/init for this exercise.

Create a two-link trace: init rc → zygote process; zygote/system-server → framework Binder service. Mark observed facts vs source facts. A process-list snapshot cannot prove service readiness, exact SELinux policy, or the startup trigger that fired.

Knowledge check#

  1. What is the difference between init's service and a Java framework service?
  2. Where does SELinux setup fit in the Android 14 init stage model?
  3. Why does start demo-daemon not prove it is ready?
  4. Does setting a property or running a process as UID 0 automatically grant access?

Answers#

  1. Init's declaration starts/supervises a process; a Java service is an object/method implementation hosted in a process and often reached over Binder.
  2. After first-stage setup and before second-stage normal init, via init's SELinux setup mode in the documented flow.
  3. It requests startup; the process may still be initializing or may fail before registering its service.
  4. No. Property permissions, SELinux, capabilities, API authorization, and other policy remain relevant.

Checkpoint#

Submit a phase diagram with stage arguments, a source map containing one definition and caller for each stage, and the zygote init declaration. Pass if you include the SELinux setup transition, separate process start from readiness, and distinguish source claims from device observations. Targeted remediation: if the stage handoff is unclear, return to the pinned init README's “First stage init,” “SELinux setup,” and “Second stage init” sections; then inspect the matching main.cpp/init.cpp call sites and record the exact argument passed at each exec/re-exec. If readiness is unclear, reread the start/service-state grammar and write one sentence separating requested process start from successful Binder registration. Redo the phase diagram before moving on.

Sources#

A little further beneath the surface.

Check your understanding with the checkpoint. Marking a chapter as read saves a bookmark, not a proficiency assessment.