• Android Property属性的实现细节

    init的三个全局列表
    服务器君一共花费 33.035 ms 进行了 3 次数据库查询,努力地为您提供了这个页面。
    广告很萌的

    属性(property)是一对键/值(key/value)组合,键和值都是字符串类型。Androd中非常多的应用程序和库直接或者间接的依赖于属性系统,并由此决定其运行期的行为。它的处理流程同android的其他模块一样,也分为服务端和客户端,property设置必须在服务端,读取直接在客户端。

    工作原理图如下:

    我们就来看系统的守护进程init,服务断就在这个进程里,分析它的main()@system/core/init/init.c函数。

    1. 创建文件目录,打开中断设备,读取/init.rc,/init.**.rc。

    这些都跟我们要分析的关系不大,但是还是要说明一下读取.rc文件的过程:

    init有三个全局列表service_list,action_list,queue_list

    init.rc文件格式(截图):

    当读取文件扫描到以on开头的一行数据时,将紧跟on后面的数据和下面几行数据组装成struct action@system/core/init/init.h变量,并加到action_list列表里。以service开头的就组装成strcut service@system/core/init/init.h变量,并加到service_list列表里。

    • service_list记录了系统所有服务的列表;
    • action_list记录了系统动作列表,但是相应的命令是不会运行的,除非将元素移动到queue_list列表;
    • queue_list记录了将要执行的动作列表。

    2. queue_builtin_action和action_for_each_trigger的调用

    其中跟property有关的调用如下:

    queue_builtin_action(property_init_action,"property_init");
    queue_builtin_action(property_service_init_action,"property_service_init");
    

    把第一个(函数)和第二个参数(名字)组装成一个action变量加到action_list和queue_list列表。

    action_for_each_trigger("init", action_add_queue_tail);在action_list列表里查找以init为名的action变量,找到后将它加到queue_list列表里。

    3. 进入死循环for(;;)

    4. execute_one_command和restart_processes的调用

    执行所有在queue_list列表里的action,其中就有调用property_init_action和property_service_init_action它们分别调用property_init(system/core/init/Porpertys_services.c)和start_property_servicet(system/core/init/Porpertys_services.c)。运行service_list里的服务。

    (1) 申请共享内存ashmem_create_region("system_properties", PA_SIZE),共享内存的参数保存在pa_workspace,共享内存分两部分,__system_property_area__指向内存起始地址,pa_info_array指向内存起始地址加PA_INFO_START的地址。__system_property_area__用来记录属性列表的统计信息,pa_info_array就是记录属性列表的地方,属性的个数最多为PA_COUNT_MAX。其中涉及到的宏定义如下:

    #define PA_COUNT_MAX  24  
    #define PA_INFO_START 1024  
    #define PA_SIZE       32768
    

    共享内存的结构图:

    (2) 从下面宏定义的文件及PERSISTENT_PROPERTY_DIR指定的目录下以persist.开头的文件中读取属性列表并储存在共享内存中。

    #define PROP_PATH_RAMDISK_DEFAULT  "/default.prop"  
    #define PROP_PATH_SYSTEM_BUILD     "/system/build.prop"  
    #define PROP_PATH_SYSTEM_DEFAULT   "/system/default.prop"  
    #define PROP_PATH_LOCAL_OVERRIDE   "/data/local.prop"   
    #define PERSISTENT_PROPERTY_DIR    "/data/property"  
    

    (3) 创建本地Socket,fd = socket(PF_UNIX, SOCK_STREAM, 0),路径是ANDROID_SOCKET_DIR/PROP_SERVICE_NAME。绑定路径监听Socket(非阻塞)。

    #define PROP_SERVICE_NAME  "property_service"  
    #define ANDROID_SOCKET_DIR "/dev/socket"  
    

    (4) 启动service_list列表里的服务,在启动服务之前,将共享内存的句柄(当然是dup之后的)和大小保存在服务进程的环境变量里。

    sprintf(tmp, "%d,%d", dup(fd), sz);  
    add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);//ENV[n] = tmp;     
    execve(svc->args[0], (char**)svc->args, (char**) ENV)  
    

    5. 使用select函数,等待客户端Socket的连接。

    有连接到来,调用handle_property_set_fd函数处理,首先accept获取连接,getsockopt(s, SOL_SOCKET, SO_PEERCRED,&cr, &cr_size)获取到客户端权限;再接受数据,判断命令类型,现只接受一种命令PROP_MSG_SETPROP(修改property),在修改之前要判断客户端有没有相应的权限。

    命令有两种类型:

    • ''ctrl:start netd(服务名)''/''ctrl:stopnetd(服务名)'';
    • ''service.video.path(property名字)=normal(property值)'';

    第一种是用来启动停止服务的,与我们要分析的关系不大,第二种就是property的设置了调用property_set。

    6. property_set(system/core/init/Property_service.c)

    先根据property名字从pa_info_array指向的内存中查找,若查找到更新内存中的值,否则在内存的后面添加。并查找此property设置有没有相应的action,若存在则加到queue_list列表里,比如前面的init.rc文件有内容:

    on property:persist.service.adb.enable=1   
    start adbd
    

    当调用property_set(“persist.service.adb.enable”,”1”)时,由于action_list存在这个action,将它加入到queue_list,就会触发这个action的动作start adbd(启动adbd服务)。

    这里有几种特殊的情况:

    • 以ro.开头的property不可以修改,只能初始化一次;
    • 以net.开头的property同时设置''net.change=name'';
    • 以persist.开头的property同时创建以PERSISTENT_PROPERTY_DIR/name命名的文件并写入它的值。

    客户端

    JAVA程序可以通过SystemProperties(framework/base/core/java/anroid/os/SystemProperties.java)类来修改和获得property,其实质还是调用到c++ 空间的property_get/property_set(system/core/libcutils/Properties.c)

    1. 获得property共享内存地址和大小

    在客户端进程初始化libc库的时候都会调用_libc_preint@bionic\libc\bionic\libc_init_dynamic.c(动态加载)或_libc_init@bionic\libc\bionic\libc_init_static.c(静态加载),这两个函数都会调用_libc_init_common(bionic/libc/bionic/libc_init_common.c),进一步调用__system_properties_init(bionic/libc/bionnic/System_properties.c)。获取环境变量值getenv("ANDROID_PROPERTY_WORKSPACE"),得到内存句柄和大小,使用mmap得到内存地址 __system_property_area__。

    2. propety_get 直接在共享内存查找,这个不需要通过服务端。

    3. propety_set 连接上服务端socket,发送到服务端处理。

更多 推荐条目

Welcome to NowaMagic Academy!

现代魔法 推荐于 2013-02-27 10:23   

本章最新发布
随机专题
  1. [PHP程序设计] CodeIgniter与PHP框架设计 5 个条目
  2. [软件工程与项目管理] 了解一点WebKit 9 个条目
  3. [移动开发] Android根基概念Context 8 个条目
  4. [Python程序设计] Django Web环境配置 2 个条目
  5. [PHP程序设计] 命令式编程范式 6 个条目
  6. [Linux操作系统] CentOS上使用EPEL Repository 2 个条目
  7. [移动开发] Android Studio里的Gradle 3 个条目
  8. [PHP程序设计] 对输入文件类型的检测 1 个条目
  9. [Python程序设计] Django模板系统 11 个条目
  10. [Python程序设计] Django 入门知识浅介 10 个条目
  11. [搜索引擎优化] 与百度权重有关的信息 2 个条目
  12. [移动开发] Android抽屉导航NavigationDrawer 5 个条目
窗口 -- [资讯]