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#
- First-stage init
Establish early mounts and the environment required for later setup.
- SELinux setup
Load policy from the available platform/vendor environment.
- Second-stage init
Initialize property/service handling, load boot scripts, and evaluate actions.
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:
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
.rcfile,service name /path/to/programdeclares 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_serverand 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
- init actionService process
start foo requests process startup
- init actionConsumer
action can continue before foo is ready
- Service processConsumer
later: readiness protocol / registration
Time runs downward. Dashed arrows show returns.
Read the text version
- init action → Service process: start foo requests process startup
- init action → Consumer: action can continue before foo is ready
- Service process → Consumer: later: readiness protocol / registration
# 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:
# 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)
# 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#
- What is the difference between init's
serviceand a Java framework service? - Where does SELinux setup fit in the Android 14 init stage model?
- Why does
start demo-daemonnot prove it is ready? - Does setting a property or running a process as UID 0 automatically grant access?
Answers#
- Init's declaration starts/supervises a process; a Java service is an object/method implementation hosted in a process and often reached over Binder.
- After first-stage setup and before second-stage normal init, via init's SELinux setup mode in the documented flow.
- It requests startup; the process may still be initializing or may fail before registering its service.
- 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#
- Pinned AOSP 14 init README and source directory.
- Pinned
init/main.cpp— dispatchesselinux_setupandsecond_stagearguments;first_stage_init.cppexecsselinux_setup. - Linux kernel: “No working init found” — how the kernel looks for the init binary; does not itself specify PID values.
- Pinned
ActivityManagerService.java— setssys.boot_completed(framework, not init). - Pinned Android 14
SystemServer.java. - System properties and adding properties.
- Android security overview.