android属性系统

转载自:http://blog.chinaunix.net/uid-20459533-id-3168973.html

一、Property系统简介

属性系统是android的一个重要特性。它作为一个服务运行,管理系统配置和状态。所有这些配置和状态都是属性。每个属性是一个键值对(key/value pair),其类型都是字符串。

从功能上看,属性与windows系统的注册表非常相似。许多android应用程序和库直接或者间接地依赖此特性,以决定它们的运行时行为。例如,adbd进程查询property服务已确认当前是否运行在模拟器环境中。另一个例子是java.io.File.pathSpearator,其返回存储于property服务中的值。

 

二、Property系统使用

使用property系统,首先需要包含头文件<cutils/properties.h>,同时需要在Android.mk文件中加入库libcutils

具体的使用实例如下:

property_set("hw.jpeg.path", "/data/test.jpg");
    char propBuf[PROPERTY_VALUE_MAX];  
    property_get("hw.jpeg.path ", propBuf, ""); 
    LOGI("property_get: %s.", propBuf);

Property系统的使用很简单,这里需要说明的,只有一个问题,那就是属性名称。

 

三、Property系统的属性名称

Property系统中使用的属性名称有一定的命名规则。

如果属性名称以“ro.”开头,那么这个属性被视为只读属性。一旦设置,属性值不能改变。

如果属性名称以“persist.”开头,当设置这个属性时,其值也将写入/data/property

如果属性名称以“net.”开头,当设置这个属性时,“net.change”属性将会自动设置,以加入到最后修改的属性名。(这是很巧妙的。 netresolve模块的使用这个属性来追踪在net.*属性上的任何变化。)

属性“ ctrl.start ”和“ ctrl.stop ”是用来启动和停止服务。每一项服务必须在/init.rc中定义.系统启动时,与init守护进程将解析init.rc和启动property服务。一旦收到设置“ ctrl.start ”属性的请求,property服务将使用该属性值作为服务名找到该服务,启动该服务。这项服务的启动结果将会放入“ init.svc.<服务名>“属性中 。客户端应用程序可以轮询那个属性值,以确定结果。

另外,还有其他的前缀;在介绍这些前缀之前,我们先来看点别的东西。

在文件system/core/init/property_service.h中,有如下两个函数定义:

extern void handle_property_set_fd(int fd);

extern int property_set(const char *name, const char *value);

它们的实现都位于system/core/init/property_service.c

另外在system/core/init/init.c中,有如下code

int main(int argc, char **argv)

{

    ……

for(;;) {

        ……

        if (ufds[0].revents == POLLIN)

            handle_device_fd(device_fd);

        if (ufds[1].revents == POLLIN)

            handle_property_set_fd(property_set_fd);

        if (ufds[3].revents == POLLIN)

            handle_keychord(keychord_fd);

}

……

}

在开机启动后的init操作中,会执行一个loop循环,当检测到有新的设置时,进入设置流程,鉴权失败会提示相关的异常,如:

sys_prop: permission denied uid:1013  name:hw.jpeg.path

这里得介绍一下函数handle_property_set_fd(),其实现为:

void handle_property_set_fd(int fd)

{

    prop_msg msg;

    ……

    switch(msg.cmd) {

    case PROP_MSG_SETPROP:

        msg.name[PROP_NAME_MAX-1] = 0;

        msg.value[PROP_VALUE_MAX-1] = 0;

 

        if(memcmp(msg.name,"ctl.",4) == 0) {

            if (check_control_perms(msg.value, cr.uid, cr.gid)) {

                handle_control_message((char*) msg.name + 4, (char*) msg.value);

            } else {

                ERROR("sys_prop: Unable to %s service ctl [%s] uid: %d pid:%d/n",

                        msg.name + 4, msg.value, cr.uid, cr.pid);

            }

        } else {

            if (check_perms(msg.name, cr.uid, cr.gid)) {

                property_set((char*) msg.name, (char*) msg.value);

            } else {

                ERROR("sys_prop: permission denied uid:%d  name:%s/n",

                      cr.uid, msg.name);

            }

        }

        break;

 

    default:

        break;

    }

}

其中check_perms()的实现如下:

static int check_perms(const char *name, unsigned int uid, int gid)

{

    int i;

    if (uid == 0)

        return 1;

 

    if(!strncmp(name, "ro.", 3))

        name +=3;

 

    for (i = 0; property_perms[i].prefix; i++) {

        int tmp;

        if (strncmp(property_perms[i].prefix, name,

                    strlen(property_perms[i].prefix)) == 0) {

            if ((uid && property_perms[i].uid == uid) ||

                (gid && property_perms[i].gid == gid)) {

                return 1;

            }

        }

    }

 

    return 0;

}

通过这两个函数,我们可以知道,我们所设置的属性名称hw.jpeg.path会与结构体property_perms中的前缀做比较,如果满足,则又会比较uid

结构体property_perms的定义如下:

struct {

    const char *prefix;

    unsigned int uid;

    unsigned int gid;

} property_perms[] = {

    { "net.rmnet0.",      AID_RADIO,    0 },

    { "net.gprs.",        AID_RADIO,    0 },

    { "net.ppp",          AID_RADIO,    0 },

    { "ril.",             AID_RADIO,    0 },

    { "gsm.",             AID_RADIO,    0 },

    { "persist.radio",    AID_RADIO,    0 },

    { "net.dns",          AID_RADIO,    0 },

    { "net.",             AID_SYSTEM,   0 },

    { "dev.",             AID_SYSTEM,   0 },

    { "runtime.",         AID_SYSTEM,   0 },

    { "hw.",              AID_SYSTEM,   0 },

    { "sys.",             AID_SYSTEM,   0 },

    { "service.",         AID_SYSTEM,   0 },

    { "wlan.",            AID_SYSTEM,   0 },

    { "dhcp.",            AID_SYSTEM,   0 },

    { "dhcp.",            AID_DHCP,     0 },

    { "vpn.",             AID_SYSTEM,   0 },


属性系统是android的一个重要特性。它作为一个服务运行,管理系统配置和状态。所有这些配置和状态都是属性。每个属性是一个键值对(key/value pair),其类型都是字符串。

         从功能上看,属性与windows系统的注册表非常相似。许多android应用程序和库直接或者间接地依赖此特性,以决定它们的运行时行为。例如,adbd进程查询property服务已确认当前是否运行在模拟器环境中。另一个例子是java.io.File.pathSpearator,其返回存储于property服务中的值。

         属性系统是如何工作的

         属性系统的上层架构如下图所示。

         图中有3个进程、一组永久属性文件和一块共享内存区域。共享内存区域是所有属性记录的存储所在。只有property服务进程才可以写入共享内存区域,它负责从永久文件中加载属性记录并将它们保存在共享内存中。

         consumer进程将共享内存加载到其自身的虚拟地址空间并直接访问这些属性。setter进程同样将共享内存加载到其自身的虚拟地址空间,但其不能直接写该内存。当setter试图增加或者更新一个属性时,它将该属性通过unix domain socket发送至property服务。property服务代表setter进程将该属性写入共享内存和永久文件中。

         property服务运行于init进程中。init进程首先创建一个共享内存区域,并保存一个指向该区域的描述符fd。init进程将该区域通过使用了MAP_SHARED标志的mmap映射至它自身的虚拟地址空间,这样,任何对于该区域的更新对于所有进程都是可见的。fd和区域大小被存储在一个名为ANDROID_PROPERTY_WORKSPACE的变量中。任何其他进程,比如consumer和setter将使用这个变量来获得fd和尺寸,这样它们就能mmap这个区域到它们自身的虚拟地址空间中。该共享内存区域如下图所示。

在这之后,init进程将从下列文件加载属性:

/default.prop
/system/build.prop
/system/default.prop
/data/local.prop

下一步是启动property服务。在这一步中,一个unix domain socket服务被创建。此socket的路径是/dev/socket/property_service,该路径对于其他客户端进程是熟知的。最后,init进程调用poll来等待该socket上的连接事件。

         在consumer一边,当它初始化libc(bionic/libc/bionic/libc_common.c __libc_init_common 函数),它将从环境变量中返回fd和尺寸,并映射共享内存到其自身的地址空间(bionic/libc/bionic/system_properties.c __system_properties_init 函数)。在这之后,libcutils可以想读取普通内存那样为consumer读取属性。

         目前,属性是不能够被删除的。也就是说,一旦添加了一个属性,它将不能够被删除,其键也不能够被改变。

         如何读取/设置属性

         Android上有三种主要途径来get/set属性。

1、  native code

当编写本地应用程序时,可以使用property_get和property_set 这两个API来读取/设置属性。要使用它们,我们需要include cutils/properties.h,并链接libcutils库。

2、  java code

在Java包(java.lang.System)中提供有System.getProperty和System.setProperty方法。但值得注意的是,尽管这两个API在语义上等同native函数,但其将数据存储于完全不同的位置。实际上,dalvik VM使用一个哈希表来存储这些属性。所以,用这两个API存储的属性是独立的,不能存取native属性,反之亦然。

然而Android有一个内部隐藏类(@hide,对SDK不可见)android.os.SystemProperties来操纵native属性。其通过jni来存取native属性库。

3、  shell脚本

Android提供getprop和setprop命令行工具来获取和更新属性。其依赖libcutils实现。

 

以上翻译自http://rxwen.blogspot.com/2010/01/android-property-system.html,有修正。


         补充:通过查看property_service.c,我们可以明确以下事实:

1、  属性名不是随意取的。在property_perms数组中定义了当前系统上可用的所有属性的前缀,以及相对应的存取权限UID。对属性的设置要满足权限要求,同时命名也要在这些定义的范围内。

2、  PA_COUNT_MAX指定了系统(共享内存区域中)最多能存储多少个属性。

3、  PROP_NAME_MAX指定了一个属性的key最大允许长度;PROP_VALUE_MAX则指定了value的最大允许长度。

此外,http://blog.csdn.net/tekkamanitachi/archive/2009/06/18/4280982.aspx 这篇文章翻译了Android的官方文档,从另一个角度叙述了属性系统,需要者请参看。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值