在device.mk文件中新增了一个persist类的property,名字为persist.control,device.mk中将其默认值设定位0。如下所示:
PRODUCT_PROPERTY_OVERRIDES += persist.control=0
进程P根据该property的值来选择执行不同的逻辑,开发者可以通过setprop命令或者property_set接口改变persist.control的值:
setprop persist.control 1
getprop persist.control
1
#include <cutils/properties.h>
char get_prop_crtl[PROPERTY_VALUE_MAX];
property_get("persist.control", get_prop_crtl, "0");
从上述的背景看,是相当简单的需求,修改persist.control的值后,在命令行启动进程P,也确实可以获取到修改后的persist.control的值并执行对应的结果。于是将进程P放到init.rc作为daemon service启动,通过名字为S的service启动进程P,service S的启动条件是on init:
service S /system/bin/P
class core
critical
disabled
user shell
group shell
seclabel u:r:shell:s0
...
on init
start S
但是重启系统后发现,进程P并没有执行修改后的persist.control的值对应的逻辑,而是执行了persist.control默认值(即device.mk中设定的0)对应的逻辑,但是通过getprop命令查看,persist.control的值确实被修改为1了。
之前也有使用persist类property的经验,但是一直没有了解它的原理,趁这个机会翻了网上的资料和代码,发现persist类property的设置和获取大概是这样:
相关的代码可以查阅system/core/init和system/core/toolbox
从上述流程可以看出,猜测是进程P被启动前,init还没执行第4步,因为进程P被执行的时机是on init,为了验证这一猜想,将进程P的执行时间点,即service S的执行时间点从on init修改为on property:persist.control=1,即等init执行了上述第4步操作后,才去启动进程P:
service S /system/bin/P
class core
critical
disabled
user shell
group shell
seclabel u:r:shell:s0
...
on property:persist.control=1
start S
修改后,进程P确实运行了persist.control=1对应的逻辑,验证了猜想
除此之外,还有另一种修改方法,在system/core/init/property_service.cpp代码中可以看到,当通过/data/property/persistent_properties完成persist类property的值的更新后,会设置ro.persistent_properties.ready为true:
switch (init_message.msg_case()) {
case InitMessage::kLoadPersistentProperties: {
load_override_properties();
// Read persistent properties after all default values have been loaded.
auto persistent_properties = LoadPersistentProperties();
for (const auto& persistent_property_record : persistent_properties.properties()) {
InitPropertySet(persistent_property_record.name(),
persistent_property_record.value());
}
InitPropertySet("ro.persistent_properties.ready", "true");
persistent_properties_loaded = true;
break;
}
default:
LOG(ERROR) << "Unknown message type from init: " << init_message.msg_case();
}
因此,也可以将启动service S的条件设置为on property:ro.persistent_properties.ready=true,即:
service S /system/bin/P
class core
critical
disabled
user shell
group shell
seclabel u:r:shell:s0
...
on property:ro.persistent_properties.ready=true
start S
这里可能会有疑惑,为什么ro类的property可以重新设置,其实是因为ro.persistent_properties.ready这个property并没有在代码中被设定默认值,即代码中没有定义ro.persistent_properties.ready这个property,但是却有根据ro.persistent_properties.ready的值去触发操作的逻辑,这在Android系统中是允许的,即用setprop或者property_set接口去设置一个代码中未定义的property的值,注意ro类的property只能设置一次,如果想要重新设置,则需要重启机器,因为这类未在代码定义的property的值,是不会固化保存的(persist类除外,因为这类property是保存在data分区的)。
对于已经在代码中定义好初始值的ro类property,则无法修改对应的值,因为它们的初始值都被保存在system和vendor等只读分区,每次启动都会从对应的prop文件中读取默认值到内存。
从上面可以看出,所谓的ro类的property,严格来说并不是真的只读,而是在每次系统启动后,只有一次设置值的机会,对于已经在代码中定义好的property,都会由系统加载默认值到内存,所以这里的ro,贴切来说,应该是内存中的property只允许赋值一次。
遇到这类系统原生机制导致的问题,还是要回到源码本身和机制的原理进行分析