Property加载流程

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.字段获取所需要的值.

猜你喜欢

转载自blog.csdn.net/chi_wy/article/details/80285133