Android Property System

Android Property System

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

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

属性系统是如何工作的

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

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

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

属性服务运行于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

       下一步是启动属性服务。在这一步中,一个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实现。

getprop <属性名>
setprop <属性名><<属性值>

 

特别属性
如果属性名称以“ro.”开头,那么这个属性被视为只读属性。一旦设置,属性值不能改变。
如果属性名称以“persist.”开头,当设置这个属性时,其值也将写入/data/property。
如果属性名称以“net.”开头,当设置这个属性时,“net.change”属性将会自动设置,以加入到最后修改的属性名。(这是很巧妙的。netresolve模块的使用这个属性来追踪在net.*属性上的任何变化。)
属性“ctrl.start ”和“ctrl.stop ”是用来启动和停止服务。每一项服务必须在/init.rc中定义.系统启动时,与init守护进程将解析init.rc和启动属性服务。一旦收到设置“ctrl.start ”属性的请求,属性服务将使用该属性值作为服务名找到该服务,启动该服务。这项服务的启动结果将会放入“init.svc.<服务名>“属性中 。客户端应用程序可以轮询那个属性值,以确定结果。

 

 补充:通过查看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的官方文档,从另一个角度叙述了属性系统,需要者请参看。

 

4、设置key的value时,需要作鉴权,根据设置程序所在进程的fd获知uid值,比如system server进程可以设置net打头的key,不可以设置gsm打头的key,相关的定义如下:

5、Android 的系统属性包括两部分:文件保存的持久属性和每次开机导入的cache属性。(cache属性也可以理解为持久属性,比如在开机初始化程序时调用setprop <property_name> <property_value>);在程序里设置的属性应该叫temp属性(自己理解),因为是保存在内存中,所以每次开机时会被清空。

 

android系统属性的命名方式:

创建与修改android属性用Systemproperties.set(name,value),获取android属性用Systemproperties.get(name),需要注意的是android属性的名称是有一定的格式要求的,如下: 前缀必须用system\core\init\property_service.c中定义的前缀 ,进行系统属性设置的程序也必须有相应的进程权限,

如何将android程序的权限提升到system权限?方法是这样的:

1、在AndroidManifest.xml中,在manifest加入android:sharedUserId="android.uid.system "。

2、在Android.mk中,將LOCAL_CERTIFICATE:= XXX修改成LOCAL_CERTIFICATE := platform 。

经过以上两步就可以把ap的权限提升到system权限了。 但是用这种方法提升权限有两个弊端,如下:

1、程序的拥有都必须有程序的源码;

2、程序的拥有都还必须有android开发环境,就是说自己能make整个android系统。

---------

我改的是Contacts,加入权限后,发现明显的有两个问题1.启动SynSimService的时候说without permission private to package,没搞明白;2.读取数据库的时候发生异常,刚开始怀疑是 Contacts改到system进程,而ContactsProvider还是com.uid.shared进程,随将ContactsProvider改为com.uid.system,结果情况依旧,提示:

01-0100:15:10.270: ERROR/DatabaseUtils(1319): Writing exception to parcel
01-0100:15:10.270: ERROR/DatabaseUtils(1319):java.lang.UnsupportedOperationException: Only CrossProcessCursor cursors aresupported across process for now
01-0100:15:10.270: ERROR/DatabaseUtils(1319):    atandroid.database.CursorToBulkCursorAdaptor.<init>(CursorToBulkCursorAdaptor.java:97)
01-0100:15:10.270: ERROR/DatabaseUtils(1319):    atandroid.content.ContentProvider$Transport.bulkQuery(ContentProvider.java:155)
01-0100:15:10.270: ERROR/DatabaseUtils(1319):    atandroid.content.ContentProviderNative.onTransact(ContentProviderNative.java:111)
01-0100:15:10.270: ERROR/DatabaseUtils(1319):    at android.os.Binder.execTransact(Binder.java:288)
01-0100:15:10.270: ERROR/DatabaseUtils(1319):    at dalvik.system.NativeStart.run(Native Method)
01-0100:15:10.270: ERROR/DatabaseUtils(1319): Caused by:java.lang.ClassCastException:com.android.providers.contacts.ContactsProvider2$3
01-0100:15:10.270: ERROR/DatabaseUtils(1319):    atandroid.database.CursorToBulkCursorAdaptor.<init>(CursorToBulkCursorAdaptor.java:81)
01-0100:15:10.270: ERROR/DatabaseUtils(1319):    ... 4 more
01-0100:15:10.280: WARN/AsyncQuery(1311): java.lang.UnsupportedOperationException:Only CrossProcessCursor cursors are supported across process for now


为了解这个问题,开个后门吧:

/system/core/init/prepery_service.c

static int check_perms(const char *name, unsigned int uid, unsigned int gid)
{
    int i;
    if (uid == 0)
        return 1;

    if(!strncmp(name, "ro.", 3))
        name +=3;
    /*增加特殊属性,不用检测UID,但是也会给系统留有后门,尽量不要修改此处 begin*/
    if(!strcmp(name, "xxx.yyy")){
    	return 1;
    }
    /*end*/
    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;
}


 

你可能感兴趣的:(android,socket,System,存储,setter)