Documentation from Google on Android init at https://android.googlesource.com/platform/system/core/+/oreo-release/init/README.md says the following
An Action can have multiple property triggers but may only have one event trigger.
For example:
on boot && property:a=b
defines an action that is only executed when the ‘boot’ event trigger happens and the property a equals b.
on property:a=b && property:c=d
defines an action that is executed at three times:
- During initial boot if property a=b and property c=d.
- Any time that property a transitions to value b, while property c already equals d.
- Any time that property c transitions to value d, while property a already equals b.
In the first example the on boot
trigger seems to be a necessary condition for the action to be executed at boot. However in the second example no on boot
trigger is used yet the documentation says the action will still be executed on boot anyway.
So it seems only one of two statements is true:
property
triggers never happen at boot automatically in which case the second example description is wrong about case #1property
triggers are always executed at boot when they are true and not only when they transition, in that case the first example need not include the boot
trigger.If the second statement is true then is it specifically the boot
phase and not say late-init
?
I decided to just go ahead and run some tests. The evidence shows that the documentation is correct about what happens but lacking in detail as to why.
The first example on boot && property:a=b
will execute during boot when init fires boot
event. Note that not all properties are initialized at the same time and as such this may affect their availability during the various boot events. Also note that this example won't run when the property changes. The main confusion is that properties in init are both events and states, where as the init phases are only events.
The second example runs during boot, but it is not related to the boot
event! It runs when init enables property triggers (see below for details). Also as described it runs when either property changes to the declared value and the other property condition is true.
Test results:
on boot
exec /system/bin/echo "Trigger test A"
Run when init script triggers boot
event
on late-init
exec /system/bin/echo "Trigger test B"
Run when init triggers late-init
event
on property:persist.testing=1
exec /system/bin/echo "Trigger test C"
Run when property is loaded from filesystem persisted properties (at time of test this was after post-fs-data
completed), this also runs when property transitions to the specified value any time later
on boot && property:persist.testing=1
exec /system/bin/echo "Trigger test D"
Never run, persist properties are not available when boot
is triggered
on post-fs-data && property:persist.testing=1
exec /system/bin/echo "Trigger test E"
Never run, persist property is not available when post-fs-data
is triggered, it is available immediately afterwards but that's too late!
on property:ro.build.type=eng
exec /system/bin/echo "Trigger test F"
Run when property triggers are enabled, after late-init
has finished being triggered (see note below on why)
on boot && property:ro.build.type=eng
exec /system/bin/echo "Trigger test G"
Run during boot process when init triggers boot
event, the property was available at that time
on post-fs-data && property:ro.build.type=eng
exec /system/bin/echo "Trigger test H"
Run during boot process twice when init triggers post-fs-data
event, once when data is mounted prior to decryption and once when data is mounted after decryption
on late-init && property:ro.build.type=eng
exec /system/bin/echo "Trigger test I"
Run when init triggers late-init
Historical evidence:
01-25 23:26:25.605 0 0 W : Trigger test B
01-25 23:26:25.605 0 0 W : Trigger test I
01-25 23:26:26.892 0 0 W : Trigger test H
01-25 23:26:28.475 0 0 I init : processing action (early-boot) from (/vendor/etc/init/hw/init.target.rc:79)
01-25 23:26:28.537 0 0 I init : processing action (boot) from (/init.rc:660)
01-25 23:26:28.745 0 0 I : Trigger test A
01-25 23:26:28.778 0 0 I : Trigger test G
01-25 23:26:28.939 0 0 I : Trigger test F
01-25 23:26:31.070 0 0 I : Trigger test H
01-25 23:26:34.871 0 0 I init : processing action (post-fs-data) from (/system/etc/init/bootstat.rc:9)
01-25 15:26:35.284 0 0 I : Trigger test C
01-25 15:26:42.468 0 0 I init : processing action (sys.boot_completed=1) from (/init.rc:819)
UPDATE 2022:
I updated some of the prior explanations and test results.
It was pointed out that property-only triggers happen after late-init
. Unclear why Google chose to do that but looking at init.cpp you can see the following code in init.cpp:
// Don't mount filesystems or start core system services in charger mode.
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}
// Run all property triggers based on current state of the properties.
am.QueueBuiltinAction(queue_property_triggers_action, "queue_property_triggers");
This means all late-init
and earlier triggers run before property-only triggers. So a trigger such as on late-init && property:xyz=*
runs before the trigger on property:xyz=*
!