Property概述
Android的系统属性Property整体上看,是键值对保存, 即Key -- Value方式。在系统运行过程中,Property是以字典树的方式存储内存中。
Property机制的运作机理可以汇总成一下几点:
1) 系统一启动就会从若干属性脚本文件中加载属性内容;
2) 系统中的所有属性(key/value)会存入同一块共享内存中;
3) 系统中的各个进程会将这块共享内存映射到自己的内存空间,这样就可以直接读取属性内容了;
4) 系统中只有一个实体可以设置、修改属性值,它就是属性服务(Property Service);
5) 不同进程只可以通过socket方式,向属性服务发出修改属性值的请求,而不能直接修改属性值;
6) 共享内存中的键值内容会以一种字典树的形式进行组织。
Build.prop生成过程
项目中的一些字段如
"ro.product.brand=$PRODUCT_BRAND" 品牌名
等等ro开头字段是在build/make/tools/buildinfo.sh中定义同时由build/core/Makefile解析完成后生成build.prop。
BUILD_FINGERPRINT也是在Makefile 拼接而成
Makefile也把$(TARGET_DEVICE_DIR)/system.prop的内容追加到build.prop中。
还会收集ADDITIONAL_BUILD_PROPERTIES中的属性,追加到build.prop中,
ADDITIONAL_BUILD_PROPERTIES又会收集PRODUCT_PROPERTY_OVERRIDES中定义的属性。
BUILDINFO_SH := build/tools/buildinfo.sh
bash $(BUILDINFO_SH) >> $@
...... $(hide) $(foreach file,$(system_prop_file), \
if [ -f "$(file)" ]; then \
echo "#" >> $@; \
echo Target buildinfo from: "$(file)"; \
echo "# from $(file)" >> $@; \
echo "#" >> $@; \
cat $(file) >> $@; \
fi;)
$(if $(FINAL_BUILD_PROPERTIES), \
$(hide) echo >> $@; \
echo "#" >> $@; \
echo "# ADDITIONAL_BUILD_PROPERTIES" >> $@; \
echo "#" >> $@; )
$(hide) $(foreach line,$(FINAL_BUILD_PROPERTIES), \
echo "$(line)" >> $@;)
.............
property初始化
build.prop生成之后就需要通过加载来实现读取其中的参数,property的初始化过程如下:
system/core/init/init.cpp中
int main(int argc, char** argv) {
......
初始化
property_init();
.......
property_load_boot_defaults();
export_oem_lock_status();
启动属性服务
start_property_service();
....
}
system/core/init/property_service.cpp
void property_init() {
if (__system_property_area_init()) {
LOG(ERROR) << "Failed to initialize property area";
exit(1);
}
}
__system_property_area_init() 存在于bionic/libc/bionic/system_properties.cpp初始化属性共享内存。
bionic/libc/include/sys/_system_properties.h
:#define PROP_SERVICE_NAME "property_service"
void start_property_service() {
property_set("ro.property_service.version", "2");
property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
false, 0666, 0, 0, nullptr, sehandle);
if (property_set_fd == -1) {
PLOG(ERROR) << "start_property_service socket creation failed";
exit(1);
}
listen(property_set_fd, 8);
register_epoll_handler(property_set_fd, handle_property_set_fd);
}
通过load properties这样的函数,将一些文件里面的property加载到内存,然后创建了一个socket,
其名为 "/dev/socket/property_service”
然后调用listen监听这个socket,并将创建的socket描述符赋值给全局变量property_set_fd。
加载由build/core/Makefile中生成的prop配置文件
void load_system_props() {
load_properties_from_file("/system/build.prop", NULL);
load_properties_from_file("/odm/build.prop", NULL);
load_properties_from_file("/vendor/build.prop", NULL);
load_properties_from_file("/factory/factory.prop", "ro.*");
load_recovery_id_prop();
}
在init.rc中启动加载properties,对应property_service.cpp中load_system_props()
system/core/rootdir/init.rc
on late-init
trigger post-fs
on post-fs
# Load properties from
# /system/build.prop,
# /odm/build.prop,
# /vendor/build.prop and
# /factory/factory.prop
load_system_props
# start essential services
property_service.cpp中load_properties_from_file加载prop文件字段
// Filter is used to decide which properties to load: NULL loads all keys,
// "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
static bool load_properties_from_file(const char* filename, const char* filter) {
Timer t;
std::string data;
std::string err;
if (!ReadFile(filename, &data, &err)) {
PLOG(WARNING) << "Couldn't load property file: " << err;
return false;
}
data.push_back('\n');
load_properties(&data[0], filter);
LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
return true;
}
system/init/util.cpp中的读取prop文件
bool ReadFile(const std::string& path, std::string* content, std::string* err) {{
.......
if (!android::base::ReadFdToString(fd, content)) {
*err = "Unable to read '" + path + "': " + strerror(errno);
return false;
}
.......
}
load_properties会逐行分析传来的buffer并且过滤点其中的#注释,解析出行内的key、value部分,并调用property_set(),将key、value设置进系统的属性共享内存去。
在load_properties中
if (!strncmp(key, "import ", 7) && flen == 0) {... fn = key + 7; while (isspace(*fn)) fn++; key = strchr(fn, ' '); if (key) { *key++ = 0; while (isspace(*key)) key++; } load_properties_from_file(fn, key); ...}
有这么一句可以看出在build.prop中或者其他加载文件中添加 import xxx.prop可以将信息导入,实现ro的动态替换改写。
property_set()是在bionic/libc/bionic/system_properties.cpp实现的具体操作
uint32_t property_set(const std::string& name, const std::string& value) {
if (name == "selinux.restorecon_recursive") {
return PropertySetAsync(name, value, RestoreconRecursiveAsync);
}
return PropertySetImpl(name, value);
}
PropertySetImpl中检测到是ro.开头的则会返回只读,然后更新字段。
static uint32_t PropertySetImpl(const std::string& name, const std::string& value) {
........
prop_info* pi = (prop_info*) __system_property_find(name.c_str());
if (pi != nullptr) {
// ro.* properties are actually "write-once".
if (android::base::StartsWith(name, "ro.")) {
LOG(ERROR) << "property_set(\"" << name << "\", \"" << value << "\") failed: "
<< "property already set";
return PROP_ERROR_READ_ONLY_PROPERTY;
}
__system_property_update(pi, value.c_str(), valuelen);
}
...........
}
Property默认存储笔数
bionic/libc/include/sys/system_properties.h
#define PROP_NAME_MAX 32
#define PROP_VALUE_MAX 92
bionic/libc/bionic/system_properties.cpp static constexpr size_t PA_SIZE = 128 * 1024;
static prop_area* map_prop_area_rw(const char* filename, const char* context, bool* fsetxattr_failed) {
........ pa_size = PA_SIZE;
pa_data_size = pa_size - sizeof(prop_area);
void* const memory_area = mmap(nullptr, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
........ }
可以算出来prop至少的个数是128*1024/(32+92) = 1036
Property JNI映射
SystemProperties调用注册流程图
当默认的配置属性被加载后,则可以通过SystemProperties类来查询关键字属性值
frameworks/base/core/jni/android_os_SystemProperties.cpp
static const JNINativeMethod method_table[] = {
{ "native_get", "(Ljava/lang/String;)Ljava/lang/String;",
(void*) SystemProperties_getS },
{ "native_get", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
(void*) SystemProperties_getSS },
{ "native_get_int", "(Ljava/lang/String;I)I",
(void*) SystemProperties_get_int },
.....
.....
}
int register_android_os_SystemProperties(JNIEnv *env)
{
return RegisterMethodsOrDie(env, "android/os/SystemProperties", method_table,
NELEM(method_table));
}
此jni的函数与frameworks/base/core/java/android/os/SystemProperties.java中的native方法匹配。
而register_android_os_SystemProperties该函数注册于
frameworks/base/core/jni/AndroidRuntime.cpp虚拟机中。
将对于native_get的指向引导到了SystemProperties_getS函数而其内部跳转最终跳转到SystemProperties_getSS。
static jstring SystemProperties_getS(JNIEnv *env, jobject clazz,jstring keyJ){
return SystemProperties_getSS(env, clazz, keyJ, NULL);
}
在frameworks/base/core/java/android/os/SystemProperties.java中
private static native String native_get(String key);
private static native String native_get(String key, String def);
public static String get(String key) {
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get(key);
}
/**
* Get the value for the given key.
* @return if the key isn't found, return def if it isn't null, or an empty string otherwise
*/
public static String get(String key, String def) {
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get(key, def);
}
最后应用层或者frameworks读取ro.字段获取所需要的值.