Search code examples
android-source

Do Android init property triggers always happen on boot and if so when exactly?


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:

  1. During initial boot if property a=b and property c=d.
  2. Any time that property a transitions to value b, while property c already equals d.
  3. 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:

  1. property triggers never happen at boot automatically in which case the second example description is wrong about case #1
  2. property 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?


Solution

  • 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=*!