Chinaunix首页 | 论坛 | 博客
  • 博客访问: 180755
  • 博文数量: 63
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 69
  • 用 户 组: 普通用户
  • 注册时间: 2013-03-18 13:11
文章分类
文章存档

2013年(64)

我的朋友

分类: LINUX

2013-05-29 16:23:44

原文地址:Android 系统属性(4) 作者:gunnerlzx

一直想研究一下android的属性系统,刚好最近一个项目告一段落,可以开始研究一下相关代码。

按照我的理解,Android属性分为两个部分

1、一个部分是系统属性,一般与虚拟机相关的一些属性,

代码位置

dalvik/libcore/luni-kernel/src/main/java/java/lang/System.java

dalvik/libcore/luni/src/main/java/java/util/Properties.java

dalvik/vm/Properties.c

虚拟机有一些默认属性,例如os.arch, java.boot.class.path等,只加载一次。

来看一些这种属性的加载过程,以Settings.java中的VNC属性为例

  1. "font-family:Arial, Verdana, sans-serif;">"white-space: normal;">"font-family:monospace;">"white-space: pre;">        private DialogInterface.OnClickListener mVncDisableListener =  new DialogInterface.OnClickListener()  
  2.         {  
  3.             public void onClick(DialogInterface dialog, int whichButton)  
  4.             {  
  5.                 System.setProperty("vncserver.enable""0");  
  6.                 System.setProperty("vncserver.password""");  
  7.   
  8.   
  9.             }  
  10.         };  
  11.   
看System.java的代码
  1. public static String setProperty(String prop, String value) {  
  2.     if (prop.length() == 0) {  
  3.         throw new IllegalArgumentException();  
  4.     }  
  5.     SecurityManager secMgr = System.getSecurityManager();  
  6.     if (secMgr != null) {  
  7.         secMgr.checkPermission(new PropertyPermission(prop, "write"));  
  8.     }  
  9.     return (String)internalGetProperties().setProperty(prop, value);  
  10. }  

首先会对该线程执行写权限的检查,然后才设置属性

在internalGetProperties方法里面,会加载虚拟机默认属性。

  1. static Properties internalGetProperties() {  
  2.     if (System.systemProperties == null) {  
  3.         SystemProperties props = new SystemProperties();  
  4.         props.preInit();  
  5.         props.postInit();  
  6.         System.systemProperties = props;  
  7.     }  
  8.   
  9.     return systemProperties;  
  10. }  

这里的SystemProperties只是内部类,跟android.os.SystemProperties不是同一个类。
  1. class SystemProperties extends Properties {  
  2.     // Dummy, just to make the compiler happy.  
  3.   
  4.     native void preInit();  
  5.   
  6.     native void postInit();  
  7. }  

它继承了Properties,两个JNI接口在dalvik/vm/native/java_lang_SystemProperties.c中 注册,preInit调用本地到本地dvmCreateDefaultProperties函数,该函数就负责加载刚才说的虚拟机默认属性。

  1. static void Dalvik_java_lang_SystemProperties_preInit(const u4* args,  
  2.     JValue* pResult)  
  3. {  
  4.     dvmCreateDefaultProperties((Object*) args[0]);  
  5.     RETURN_VOID();  
  6. }  
也就是说System.setProperty调用到Properties.setProperty,
  1. public Object setProperty(String name, String value) {  
  2.     return put(name, value);  
  3. }  
Properties是继承Hashtable的
  1. public class Properties extends Hashtable  
这样,就完成设置属性的动作,获取的动作类似,最后从哈希表中根据key拿到value,整个过程比较简单。

可以看到这套属性系统只适合一些不会变化,或者很少变的属性,如果你希望你的属性改变之后能触发某些实践,例如init.rc脚本中的动作,那就要用到另外一套属性系统了。



2、剩下一部分是常规属性。

它的实现原理跟刚才的hash表不一样,是讲属性保存在一块共享内存之中,该共享内存的大小由环境变量ANDROID_PROPERTY_WORKSPACE决定

代码位置:

frameworks/base/core/java/android/os/SystemProperties.java

frameworks/base/core/jni/android_os_SystemProperties.cpp

system/core/init/property_service.c

bionic/libc/bionic/system_properties.c

写属性的过程:

SystemProperties.java

  1. public static void set(String key, String val) {  
  2.     if (key.length() > PROP_NAME_MAX) {  
  3.         throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);  
  4.     }  
  5.     if (val != null && val.length() > PROP_VALUE_MAX) {  
  6.         throw new IllegalArgumentException("val.length > " +  
  7.             PROP_VALUE_MAX);  
  8.     }  
  9.     native_set(key, val);  
  10. }  
value值只支持String类型,而get重载了各种类型的value

这些方法调用jni

  1. private static native String native_get(String key);  
  2. private static native String native_get(String key, String def);  
  3. private static native int native_get_int(String key, int def);  
  4. private static native long native_get_long(String key, long def);  
  5. private static native boolean native_get_boolean(String key, boolean def);  
  6. private static native void native_set(String key, String def);  

这些jni在frameworks/base/core/jniandroid_os_SystemProperties.cpp注册
  1. static JNINativeMethod method_table[] = {  
  2.     { "native_get""(Ljava/lang/String;)Ljava/lang/String;",  
  3.       (void*) SystemProperties_getS },  
  4.     { "native_get""(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",  
  5.       (void*) SystemProperties_getSS },  
  6.     { "native_get_int""(Ljava/lang/String;I)I",  
  7.       (void*) SystemProperties_get_int },  
  8.     { "native_get_long""(Ljava/lang/String;J)J",  
  9.       (void*) SystemProperties_get_long },  
  10.     { "native_get_boolean""(Ljava/lang/String;Z)Z",  
  11.       (void*) SystemProperties_get_boolean },  
  12.     { "native_set""(Ljava/lang/String;Ljava/lang/String;)V",  
  13.       (void*) SystemProperties_set },  
  14. };  

其中SystemProperties_set方法调用到property_service.c中的
  1. int property_set(const char *name, const char *value)  

在property_set中的流程是这样的

首相,通过

  1. pi = (prop_info*) __system_property_find(name);  

找到对应的键值对,prop_info在bionic/libc/include/sys/_system_properties.h有定义
  1. struct prop_area {  
  2.     unsigned volatile count;  
  3.     unsigned volatile serial;  
  4.     unsigned magic;  
  5.     unsigned version;  
  6.     unsigned reserved[4];  
  7.     unsigned toc[1];  
  8. };  
  9.   
  10. #define SERIAL_VALUE_LEN(serial) ((serial) >> 24)  
  11. #define SERIAL_DIRTY(serial) ((serial) & 1)  
  12.   
  13. struct prop_info {  
  14.     char name[PROP_NAME_MAX];  
  15.     unsigned volatile serial;  
  16.     char value[PROP_VALUE_MAX];  
  17. };  

来看看__system_property_find的实现,该函数位于system_properties.c中
  1. const prop_info *__system_property_find(const char *name)  
  2. {  
  3.     prop_area *pa = __system_property_area__;  
  4.     unsigned count = pa->count;      
  5.     unsigned *toc = pa->toc;  
  6.     unsigned len = strlen(name);     
  7.     prop_info *pi;  
  8.   
  9.     while(count--) {  
  10.         unsigned entry = *toc++;         
  11.         if(TOC_NAME_LEN(entry) != len) continue;  
  12.           
  13.         pi = TOC_TO_INFO(pa, entry);     
  14.         if(memcmp(name, pi->name, len)) continue;  
  15.   
  16.         return pi;  
  17.     }    
  18.   
  19.     return 0;  
  20. }  


这个函数就是找出键值对,看看TOC_NAME_LEN和TOC_TO_INFO的定义,
  1. #define TOC_NAME_LEN(toc)       ((toc) >> 24)  
  2. #define TOC_TO_INFO(area, toc)  ((prop_info*) (((char*) area) + ((toc) & 0xFFFFFF)))  

因此toc的高8位保存的是属性名长度,低24位保存属性键值对的地址,


再看__system_property_area__了,这是个全局变量,在system_properties.c的__system_properties_init函数中初始化

该函数读取ANDROID_PROPERTY_WORKSPACE环境变量,格式为:fd,size

然后利用mmap将"fd"处的内容,映射"size"大小,赋给__system_property_area__。


如果匹配成功,看看property_set是怎么做的

  1. if(pi != 0) {  
  2.        /* ro.* properties may NEVER be modified once set */  
  3.        if(!strncmp(name, "ro.", 3)) return -1;  
  4.   
  5.        pa = __system_property_area__;  
  6.        update_prop_info(pi, value, valuelen);  
  7.        pa->serial++;  
  8.        __futex_wake(&pa->serial, INT32_MAX);  
  9.    }  

注意pa->serial++,它的修饰符包含一个volatile,这样做是确保每一次针对属性系统的改动都能得到处理。

看看update_prop_info

  1. static void update_prop_info(prop_info *pi, const char *value, unsigned len)  
  2. {  
  3.     pi->serial = pi->serial | 1;  
  4.     memcpy(pi->value, value, len + 1);  
  5.     pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff);  
  6.     __futex_wake(π->serial, INT32_MAX);  
  7. }  

首先讲针对该格式的修改序列号+1,然后保存属性值,最后调用__futex_wake触发一个系统调用,在atomics_x86.c中是这样写的
  1. int __futex_wake(volatile void *ftx, int count)  
  2. {  
  3.     int ret;  
  4.     asm volatile (  
  5.         "int $0x80;"  
  6.         : "=a" (ret)  
  7.         : "0" (FUTEX_SYSCALL),  
  8.           "b" (ftx),  
  9.           "c" (FUTEX_WAKE),  
  10.           "d" (count)  
  11.     );  
  12.     return ret;  
  13. }  

具体是什么意思待研究。

接下来,就是property_set执行如下语句

  1. property_changed(name, value);  
property_changed在system/core/init/init.c中有定义
  1. void property_changed(const char *name, const char *value)  
  2. {  
  3.     if (property_triggers_enabled) {  
  4.         queue_property_triggers(name, value);  
  5.         drain_action_queue();  
  6.     }  
  7. }  

property_triggers_enabled在执行main函数里面设定。
  1. void queue_property_triggers(const char *name, const char *value)  
  2. {         
  3.     struct listnode *node;  
  4.     struct action *act;  
  5.     list_for_each(node, &action_list) {  
  6.         act = node_to_item(node, struct action, alist);  
  7.         if (!strncmp(act->name, "property:", strlen("property:"))) {  
  8.             const char *test = act->name + strlen("property:");  
  9.             int name_length = strlen(name);  
  10.               
  11.             if (!strncmp(name, test, name_length) &&  
  12.                     test[name_length] == '=' &&  
  13.                     !strcmp(test + name_length + 1, value)) {  
  14.                 action_add_queue_tail(act);  
  15.             }  
  16.         }  
  17.     }  
  18. }  

这个函数讲action_list中的所有关心该属性的动作都串到act中,action_list应该是在解析初始化脚本文件的时候生成的。
  1. void drain_action_queue(void)  
  2. {         
  3.     struct listnode *node;  
  4.     struct command *cmd;  
  5.     struct action *act;  
  6.     int ret;  
  7.   
  8.     while ((act = action_remove_queue_head())) {  
  9.         INFO("processing action %p (%s)\n", act, act->name);  
  10.         list_for_each(node, &act->commands) {  
  11.             cmd = node_to_item(node, struct command, clist);  
  12.             ret = cmd->func(cmd->nargs, cmd->args);  
  13.             INFO("command '%s' r=%d\n", cmd->args[0], ret);  
  14.         }  
  15.     }  
  16. }     

这个函数负责触发各个回调函数。



脚本文件的解析由system/core/init/parser.c完成,来看init.c的main函数有如下语句

  1. get_hardware_name();  
  2. snprintf(tmp, sizeof(tmp), "/init.%s.rc", hardware);  
  3. parse_config_file(tmp);  

在parser.c里面
  1.       
  2. int parse_config_file(const char *fn)  
  3. {     
  4.     char *data;  
  5.     data = read_file(fn, 0);  
  6.     if (!data) return -1;  
  7.   
  8.     parse_config(fn, data);  
  9.     DUMP();  
  10.     return 0;  
  11. }  
parse_config_file读入脚本文件,并且进行解析。


原文地址:http://blog.csdn.net/yinlijun2004/article/details/6981954

阅读(1365) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~