本文将从下面三方面简单分析总结android property:

  1. Property的使用方式
  2. Property文件的加载
  3. Property的存储

1. Property的使用方式

在工作中经常通过下面三种方式使用property:
1.1 code里面使用SystemProperties.java和properties.cpp
SystemProperties.java为Jave层提供了下面的方法:

 public static String get(String key){...}public static String get(String key, String def){...}public static int getInt(String key, int def){...}public static long getLong(String key, long def){...}public static boolean getBoolean(String key, boolean def){...}public static void set(String key, String val){...}

system/core/libcutils/properties.cpp给Native层提供了下面的API:

int property_set(const char *key, const char *value) {...}
int property_get(const char *key, char *value, const char *default_value) {...}
int8_t property_get_bool(const char *key, int8_t default_value) {...}
static intmax_t property_get_imax(const char *key, intmax_t lower_bound, intmax_t upper_bound,intmax_t default_value) {...}
int64_t property_get_int64(const char *key, int64_t default_value) {...}
int32_t property_get_int32(const char *key, int32_t default_value){...}

1.2 adb 命令
adb的方式为我们调式提供了方便。
[格式]
adb shell getprop [proptery_name]
adb shell setprop propterty_name value
[例子]
adb shell getprop ro.build.type
adb shell setprop persist.log.tag.ImsManager V

1.3 通过文件设置默认property;将property放在文件中,init进程去加载文件。

前两种方式可以读取所有的property;但是在写方面,对于ro.* property这种write-once的property是不能覆盖的。

2. Property文件的加载

Property的初始化,以及相关propety文件的加载都在Init进程中完成。所以这部分内容从init进程的main函数开始。按照main函数中的code顺序,主要内容如下:

  • Property area初始化
  • 加载default property文件
  • 创建property service
  • 加载解析rc文件
  • 执行rc文件中的action; system property, persistent property和override property文件会在这个过程中相继被加载。

main函数会调用proprerty_service.cpp中的property_init()函数来初始化property area。

void property_init() {if (__system_property_area_init()) {LOG(ERROR) << "Failed to initialize property area";exit(1);}
}

property_init函数调用了System_properties.cpp中的__system_property_area_init()函数。后者又先后调用了initialize_properties()和map_system_property_area函数。

initialize_properties()函数:
initialize_properties会尝试加载"/property_contexts", 如果失败会尝试加载/system 和/vendor下对应的文件, 看来从Android O开始/property_contexts文件已经不再使用, 取而代之的是/plat_property_contexts。 "/property_contexts"等文件为property prefix分配了security context,用来控制set权限。
property_contexts内容如下:

...
sys.usb.config          u:object_r:system_radio_prop:s0
ril.                    u:object_r:radio_prop:s0
ro.ril.                 u:object_r:radio_prop:s0
gsm.                    u:object_r:radio_prop:s0
...
static bool initialize_properties() {// If we do find /property_contexts, then this is being// run as part of the OTA updater on older release that had// /property_contexts - b/34370523if (initialize_properties_from_file("/property_contexts")) {//加载"/property_contexts"return true;}// Use property_contexts from /system & /vendor, fall back to those from /if (access("/system/etc/selinux/plat_property_contexts", R_OK) != -1) {//如果可以访问/system/etc/selinux/plat_property_contexts,就去加载解析文件。if (!initialize_properties_from_file("/system/etc/selinux/plat_property_contexts")) {return false;//这里如果解析失败就直接返回了,不再加载其他路径下的文件。}// Don't check for failure here, so we always have a sane list of properties.// E.g. In case of recovery, the vendor partition will not have mounted and we// still need the system / platform properties to function.//这里去加载/vendor/etc/selinux/nonplat_property_contexts, 但是不care结果。initialize_properties_from_file("/vendor/etc/selinux/nonplat_property_contexts");} else {//如果/system没有配置property_contexts文件,那么加载//plat_property_contextsif (!initialize_properties_from_file("/plat_property_contexts")) {return false;}initialize_properties_from_file("/nonplat_property_contexts");}return true;
}/*initialize_properties_from_file负责解析property_contexts文件中的内容,
*将property前缀存在prefixes指向的链表中,将context存在contexts指向的链表中。
*/
static bool initialize_properties_from_file(const char* filename) {FILE* file = fopen(filename, "re");if (!file) {return false;}char* buffer = nullptr;size_t line_len;char* prop_prefix = nullptr;char* context = nullptr;while (getline(&buffer, &line_len, file) > 0) {int items = read_spec_entries(buffer, 2, &prop_prefix, &context);if (items <= 0) {continue;}if (items == 1) {free(prop_prefix);continue;}/** init uses ctl.* properties as an IPC mechanism and does not write them* to a property file, therefore we do not need to create property files* to store them.*/if (!strncmp(prop_prefix, "ctl.", 4)) {free(prop_prefix);free(context);continue;}auto old_context =list_find(contexts, [context](context_node* l) { return !strcmp(l->context(), context); });if (old_context) {list_add_after_len(&prefixes, prop_prefix, old_context);} else {list_add(&contexts, context, nullptr);list_add_after_len(&prefixes, prop_prefix, contexts);}free(prop_prefix);free(context);}free(buffer);fclose(file);return true;
}

map_system_property_area函数:
在看map_system_property_area(…)函数之前,我们先看下__system_property_area_init()函数。

int __system_property_area_init() {free_and_unmap_contexts();//property_filename所指的路径为/dev/__properties__; #define PROP_FILENAME "/dev/__properties__"mkdir(property_filename, S_IRWXU | S_IXGRP | S_IXOTH);if (!initialize_properties()) {return -1;}bool open_failed = false;bool fsetxattr_failed = false;/*从property_contexts中解析的context全在contexts指向的链表中,下面这段代码就是遍历链表,*open函数将每个context_node节点对应的文件都打开,并映射到一块share memory中。*并在这块memory上构造一个prop_area指针存在context_node节点中。*/list_foreach(contexts, [&fsetxattr_failed, &open_failed](context_node* l) {if (!l->open(true, &fsetxattr_failed)) {open_failed = true;}});if (open_failed || !map_system_property_area(true, &fsetxattr_failed)) {free_and_unmap_contexts();return -1;}initialized = true;return fsetxattr_failed ? -2 : 0;
}

下面看看map_system_property_area函数:
打开/dev/properties/properties_serial文件,并映射到共享内存,将地址保存在__system_property_area__中。

static bool map_system_property_area(bool access_rw, bool* fsetxattr_failed) {char filename[PROP_FILENAME_MAX];//经过下面的格式化之后filename指向的文件为/dev/__properties__/properties_serialint len =__libc_format_buffer(filename, sizeof(filename), "%s/properties_serial", property_filename);if (len < 0 || len > PROP_FILENAME_MAX) {__system_property_area__ = nullptr;return false;}if (access_rw) {/*map_prop_area_rw函数会打开filename,映射一块共享内存,然后将地址返回。*地址将保存在 __system_property_area__中, 这个是一个全局变量,property*相关的操作还会用到这个变量。*/__system_property_area__ =map_prop_area_rw(filename, "u:object_r:properties_serial:s0", fsetxattr_failed);} else {__system_property_area__ = map_prop_area(filename);}return __system_property_area__;
}

到这里property area初始化就完成了。

在讲property文件的加载之前,有必要先讲下rc文件相关的知识点。rc文件被加载解析之后,所有的action会被放到ActionManager的actions_ (vector类型)容器里; 而action对应的commend对放到对应action的commands_ (vector类型)容器里。ActionManager负责根据trigger来执行action。
command对象在被创建的时候,会根据关键字在KeywordMap类型的指针function_map_ 中查找对应的function; function_map_ 的赋值是在main函数中,使用的是BuiltinFunctionMap对象,BuiltinFunctionMap是继承自KeywordMap。最终是在BuiltinFunctionMap内Map类型的变量builtin_functions中查找。
下面是buildin_functions中的部分内容:

        {"load_persist_props",      {0,     0,    do_load_persist_props}},{"load_system_props",       {0,     0,    do_load_system_props}},{"loglevel",                {1,     1,    do_loglevel}},

下面是init.rc中的相关内容:

on post-fs# Load properties from#     /system/build.prop,#     /odm/build.prop,#     /vendor/build.prop and#     /factory/factory.propload_system_props...on load_persist_props_actionload_persist_propsstart logdstart logd-reinit...on property:vold.decrypt=trigger_load_persist_propsload_persist_propsstart logd

当load_persist_props和load_system_props 命令执行的时候do_load_persist_props和do_load_system_props 函数会分别执行。

//下面是system property
static int do_load_system_props(const std::vector<std::string>& args) {load_system_props();return 0;
}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();
}//下面是persist properties
static int do_load_persist_props(const std::vector<std::string>& args) {load_persist_props();return 0;
}
void load_persist_props(void) {load_override_properties();/* Read persistent properties after all default values have been loaded. */load_persistent_properties();property_set("ro.persistent_properties.ready", "true");
}static void load_override_properties() {if (ALLOW_LOCAL_PROP_OVERRIDE) {load_properties_from_file("/data/local.prop", NULL);}
}

在执行rc文件内的action之前,main函数会调用property_load_boot_defaults()函数加载默认property; 调用start_property_service()函数来创建了一个名字为property_service的Unix domain Socket(PROP_SERVICE_NAME:property_service)来处理set prop请求。

void property_load_boot_defaults() {if (!load_properties_from_file("/system/etc/prop.default", NULL)) {// Try recovery pathif (!load_properties_from_file("/prop.default", NULL)) {// Try legacy pathload_properties_from_file("/default.prop", NULL);}}load_properties_from_file("/odm/default.prop", NULL);load_properties_from_file("/vendor/default.prop", NULL);update_sys_usb_config();
}

下面总结了可能会被加载的property文件或property(按照code顺序):

File/property comments
/system/etc/prop.default
/prop.default
/default.prop
/odm/default.prop
/vendor/default.prop
/system/build.prop
/odm/build.prop
/vendor/build.prop
/factory/factory.prop 只加载ro.*
ro.recovery_id
/data/local.prop
/data/property 这个路径下只加载persist开头的property

/system/build.prop, /vendor/build.prop(PRODUCT_PROPERTY_OVERRIDES包含在内),/default.prop等生成规则都在代码build/core/Makefile中有定义,当然如果不懂make 语法和函数,是不可能看的很明白。

3. Property的存储

Property的存储,工作中用不到,所以不想深究,根据System_properties.cpp中的注释知道使用的是混合树结构(hybrid trie/binary tree),查找速度快,也省空间(分割了前缀, 可以共用)。
Property是只能由init进程(单线程)更新, 由property service完成。为了避免对线程读的问题,在节点上使用了atomic_uint_least32_t。

/** Properties are stored in a hybrid trie/binary tree structure.* Each property's name is delimited at '.' characters, and the tokens are put* into a trie structure.  Siblings at each level of the trie are stored in a* binary tree.  For instance, "ro.secure"="1" could be stored as follows:** +-----+   children    +----+   children    +--------+* |     |-------------->| ro |-------------->| secure |* +-----+               +----+               +--------+*                       /    \                /   |*                 left /      \ right   left /    |  prop   +===========+*                     v        v            v     +-------->| ro.secure |*                  +-----+   +-----+     +-----+            +-----------+*                  | net |   | sys |     | com |            |     1     |*                  +-----+   +-----+     +-----+            +===========+*/

Android Property相关推荐

  1. Android Property Animation属性动画:scale缩放动画(4)

     Android Property Animation属性动画:scale缩放动画(4) 和之前我写的附录文章1,2,3相似,本文将接着使用Android Property Animation属性 ...

  2. 深入讲解Android Property机制

    摘要: 本文以Android 4.4为准,深入讲解Android Property机制的运作机理. 深入讲解Android Property机制 侯亮 1      概述 Android系统(本文以A ...

  3. Android Property Animation动画

    3.0以前,android支持两种动画模式,tween animation,frame animation,在android3.0中又引入了一个新的动画系统:property animation,这三 ...

  4. 《Android Property

    每个属性都有一个名称和值,他们都是字符串格式.属性被大量使用在Android系统中,用来记录系统设置 或进程之间的信息交换.属性是在整个系统中全局可见的.每个进程可以get/set属性. 在系统初始化 ...

  5. android property实现路径

    上层接口实现 system/core/libcutils/properties.cpp: property_set/property_get:#include <sys/_system_prop ...

  6. Android平台上使用属性系统(property system)

    在使用Android的属性系统(property system)时遇到了一些问题,结合此次经历,对属性系统的使用做以简单介绍. 一.Property系统简介 属性系统是android的一个重要特性.它 ...

  7. Android版网易云音乐唱片机唱片磁盘旋转及唱片机机械臂动画关键代码实现思路...

     Android版网易云音乐唱片机唱片磁盘旋转及唱片机机械臂动画关键代码实现思路 先看一看我的代码运行结果. 代码运行起来初始化状态: 点击开始按钮,唱片机的机械臂匀速接近唱片磁盘,同时唱片磁盘也 ...

  8. Android应用程序获得root权限

    我在博文<Android程序的安全系统>中提到两种让root权限的办法.最近在网上发现很多朋友转载那篇文章,但是对那篇文章中提到的第一种方法怎样实现,不是很明白.本文将会以一个例子实现来演 ...

  9. go语言能编android程序吗,用 Golang 开发 Android 应用(二)—— 简单 UI-Go语言中文社区...

    计划按以下的内容更新 简单 UI 关于开发一个应用,要有自己的应用名(显示用),和包名(真正唯一的应用名),简单说一台 Android 手机中所有应用的包名是唯一的,如果新安装的应用包名和已安装的应用 ...

  10. Android之内核学习笔记

    0.Android系统启动 <Android系统启动流程 -- bootloader> <The Android boot process from power on> < ...

最新文章

  1. python 函数的调用的时候参数的传递_python定义函数时的参数调用函数时的传参...
  2. idea tomcat乱码_Tomcat新手常见问题
  3. 关于linux分区与挂载的解释
  4. Python 实现 csv文件转换成json文件
  5. 更改密码 sp_password
  6. Spring Boot 2 尝鲜-动态 Banner
  7. Linux网络协议栈:关闭一个还有没发送数据完的TCP连接
  8. 32树莓派_树莓派推出8GB内存版本
  9. 在React / React Native中使用构造函数与getInitialState有什么区别?
  10. hector与gmapping总结
  11. 给定一个区间寻找三角形_计算给定图片中的三角形数量–程序化解决方案
  12. css实现动态箭头_Jerry的反省:程序员不要轻易说出quot;这个功能技术上无法实现quot;...
  13. 【HDU - 4794】Arnold【斐波那契数列循环节】
  14. PLSQL官方下载、安装和使用完全指南
  15. 杜比音效卡刷包android 7.0,杜比音效7.0清爽卡刷包-杜比音效7.0定制版v2.1.0 安卓版-手机腾牛网...
  16. yii框架封装拼多多开放平台sdk
  17. 北京队“接触风波”受罚背后:CBA职业化不断进步
  18. 推荐一款最好用的windows文件管理器
  19. iOS上二维码和一维码识别系列二
  20. scala-第七章-打印9*9乘法口诀表

热门文章

  1. xyz坐标转换ybc_GNSS仰角和方位角的计算及代码,XYZ转BLH坐标的代码及原理
  2. 网课答案查询单页源码+免费题库API接口
  3. lumion最全电脑配置推荐,lumion选择什么样的电脑看这里
  4. 幼儿园故事导入语案例_幼儿语言活动方案优秀案例合集
  5. 微信调用手机浏览器打开下载链接
  6. 在ThinkPHP5.0打造一个专属短信接口
  7. KEGG Orthology 数据库简介
  8. flog和flag_立flag是什么梗 立个flag是什么意思
  9. 计算机知识论,计算机科学与技术中的系统论与辩证法
  10. *rock ,scissors ,paperpku 2339