Android 系统属性-SystemProperties详解***

摘要:
方法如下:1.在AndroidManifest中。xml,将android:sharedUserId=“android.uid.system”添加到清单中。2.在Android Mk中,将LOCAL_CERTIFICATE:=XXX设置为LOCAL_CERTICATE:=platformAndroid的系统属性包括两部分:文件保存的持久属性和每次引导导入的缓存属性。前者主要保存在以下文件中:bionic/libc/include/sys/__system.properties。h1#definePROP_SERVICE_NAME“property_SERVICE”2#definePROP_PATH_RAMDISK_DEFAULT“/DEFAULT.prop”3#definePPROP_PATH_SYSTEM_BILD“/SYSTEM/BUILD.prop”4#definePAPH_PATH_SYSTM_DEFAULT”/SYSTEM/DEFAULT.prost“5#definePROF_PATH_LOCAL_OVERRIDE”/data/LOCAL.prop“后者使用framework/base/core/java/android/os/SystemProperties java接口定义,1privatestationativeStringnative_get;2私有静态字符串获取;3私有静态演化集;4公共静态无效集{5if{6thrownewIllegalArgumentException;7}8if(val!

  创建与修改android属性用Systemproperties.set(name, value),获取android属性用Systemproperties.get(name),需要注意的是android属性的名称是有一定的格式要求的:

  如下:1.前缀必须用systemcoreinitproperty_service.c中定义的前缀。 2. 进行系统属性设置的程序也必须有system或root权限,

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

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

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

  经过以上两步就可以把ap的权限提升到system权限了。

Android 的系统属性包括两部分:文件保存的持久属性和每次开机导入的cache属性。前者主要保存在下面几个文件中:

bionic/libc/include/sys/_system_properties.h

1     #define PROP_SERVICE_NAME "property_service"
2     #define PROP_PATH_RAMDISK_DEFAULT  "/default.prop"
3     #define PROP_PATH_SYSTEM_BUILD     "/system/build.prop"
4     #define PROP_PATH_SYSTEM_DEFAULT   "/system/default.prop"
5     #define PROP_PATH_LOCAL_OVERRIDE   "/data/local.prop"

后者则通过frameworks/base/core/java/android/os/SystemProperties.java的接口定义,

 1     private static native String native_get(String key);
 2     private static native String native_get(String key, String def);
 3     private static native void native_set(String key, String def);
 4     public static void set(String key, String val) {
 5         if (key.length() > PROP_NAME_MAX) {
 6             throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
 7         }
 8         if (val != null && val.length() > PROP_VALUE_MAX) {
 9             throw new IllegalArgumentException("val.length > " +
10                 PROP_VALUE_MAX);
11         }
12         native_set(key, val);
13     }

该接口类在初始化运行环境中注册对应的cpp接口android_os_SystemProperties.cpp,实际操作通过JNI调用的是cpp文件对应的接口:

frameworks/base/core/jni/AndroidRuntime.cpp

1     namespace android {
2     extern int register_android_os_SystemProperties(JNIEnv *env);
3     }

frameworks/base/core/jni/android_os_SystemProperties.cpp

 1     static void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ, jstring valJ)
 2     {
 3         int err;
 4         const char* key;
 5         const char* val;
 6         key = env->GetStringUTFChars(keyJ, NULL);
 7         if (valJ == NULL) {
 8             val = "";       /* NULL pointer not allowed here */
 9         } else {
10             val = env->GetStringUTFChars(valJ, NULL);
11         }
12         err = property_set(key, val);
13         env->ReleaseStringUTFChars(keyJ, key);        
14         if (valJ != NULL) {
15             env->ReleaseStringUTFChars(valJ, val);
16         }
17     }

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

system/core/include/private/android_filesystem_config.h

1     #define AID_ROOT             0  /* traditional unix root user */
2     #define AID_SYSTEM        1000  /* system server */
3     #define AID_RADIO         1001  /* telephony subsystem, RIL */
4     #define AID_DHCP          1014  /* dhcp client */
5     #define AID_SHELL         2000  /* adb and debug shell user */
6     #define AID_CACHE         2001  /* cache access */
7     #define AID_APP          10000 /* first app user */
vim  ./system/core/init/property_service.c
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 },
    { "net.qmi",          AID_RADIO,    0 },
    { "net.lte",          AID_RADIO,    0 },
    { "net.cdma",         AID_RADIO,    0 },
    { "ril.",             AID_RADIO,    0 },
    { "gsm.",             AID_RADIO,    0 },
    { "persist.radio",    AID_RADIO,    0 },
    { "net.dns",          AID_RADIO,    0 },
    { "sys.usb.config",   AID_RADIO,    0 },
    { "net.",             AID_SYSTEM,   0 },
    { "dev.",             AID_SYSTEM,   0 },
    { "runtime.",         AID_SYSTEM,   0 },
    { "hw.",              AID_SYSTEM,   0 },
    { "sys.",             AID_SYSTEM,   0 },
    { "sys.powerctl",     AID_SHELL,    0 },
    { "service.",         AID_SYSTEM,   0 },
    { "wlan.",            AID_SYSTEM,   0 },
    { "bluetooth.",       AID_BLUETOOTH,   0 },
    { "dhcp.",            AID_SYSTEM,   0 },
    { "dhcp.",            AID_DHCP,     0 },
    //MStar Android Patch Begin
    { "dhcp.offer",       AID_NOBODY,     0 },
    //MStar Android Patch End
    { "debug.",           AID_SYSTEM,   0 },
    { "debug.",           AID_SHELL,    0 },
    { "log.",             AID_SHELL,    0 },
    { "service.adb.root", AID_SHELL,    0 },
    { "service.adb.tcp.port", AID_SHELL,    0 },
    { "persist.sys.",     AID_SYSTEM,   0 },
    { "persist.service.", AID_SYSTEM,   0 },
    { "persist.security.", AID_SYSTEM,   0 },
    { "persist.service.bdroid.", AID_BLUETOOTH,   0 },
    { "selinux."         , AID_SYSTEM,   0 },
    // MStar Android Patch Begin
    { "mstar.",           AID_SYSTEM,   0 },
    { "mstar.",           AID_MEDIA,    0 },
    { "http.",            AID_SYSTEM,   0 },
    { "mstar.media.",     AID_MEDIA,   0 },
    { "mstar.dvfs.",      AID_MEDIA,   0 },
    { "persist.bt.", AID_BLUETOOTH,   0 },
    { "mstar.",           AID_GRAPHICS, 0 },
    // MStar Android Patch End
    { NULL, 0, 0 }
}; 

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

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     }
 8     int main(int argc, char **argv)
 9     {
10         parse_config_file("/init.rc");
11         qemu_init();
12         device_fd = device_init();
13         property_init();
14         fd = open(console_name, O_RDWR);
15         property_set_fd = start_property_service();
16         ufds[0].fd = device_fd;
17         ufds[0].events = POLLIN;
18         ufds[1].fd = property_set_fd;
19         ufds[1].events = POLLIN;
20         ufds[2].fd = signal_recv_fd;
21         ufds[2].events = POLLIN;
22         fd_count = 3;
23         for(;;) {
24             if (ufds[0].revents == POLLIN)
25                 handle_device_fd(device_fd);
26 
27             if (ufds[1].revents == POLLIN)
28                 handle_property_set_fd(property_set_fd);
29             if (ufds[3].revents == POLLIN)
30                 handle_keychord(keychord_fd);
31         }
32         return 0;
33     }

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

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

         属性系统是如何工作的

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

  Android 系统属性-SystemProperties详解***第1张

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

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

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

  Android 系统属性-SystemProperties详解***第2张

在这之后,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实现。 

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

Android系统源码中,存在大量的SystemProperties.get或SystemProperties.set,具体使用如下:

1、SystemProperties的使用
SystemProperties的使用很简单,在SystemProperties.java中所以方法都是static,直接通过SystemProperties.get(String key)或SystemProperties.set(String key, String val)就可以了,系统属性都是以键值对的形式存在即name和value
需要注意的是对name和value的length是有限制的,name的最大长度是31,value最大长度是91,具体定义如下:

//frameworks/base/core/java/android/os/SystemProperties.java
public class SystemProperties
{
    public static final int PROP_NAME_MAX = 31;
    public static final int PROP_VALUE_MAX = 91;
    ...
    private static native String native_get(String key);
    private static native void native_set(String key, String def);
    /**
     * Get the value for the given key.
     * @return an empty string if the key isn't found
     * @throws IllegalArgumentException if the key exceeds 32 characters
     */
    public static String get(String key) {
        if (key.length() > PROP_NAME_MAX) {
            throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
        }
        return native_get(key);
    }
    ...
    /**
     * Set the value for the given key.
     * @throws IllegalArgumentException if the key exceeds 32 characters
     * @throws IllegalArgumentException if the value exceeds 92 characters
     */
    public static void set(String key, String val) {
        if (key.length() > PROP_NAME_MAX) {
            throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
        }
        if (val != null && val.length() > PROP_VALUE_MAX) {
            throw new IllegalArgumentException("val.length > " +
                PROP_VALUE_MAX);
        }
        native_set(key, val);
    }
    ...
}

在调用get或set时,都是通过调用native方法去操作的,开始本来想一笔带过的,还是看看native方法中的具体流程吧,如果不感兴趣可以直接看第三条

ps:在调用SystemProperties.set时所在的apk uid必须在system group,否则设置属性会报错,在manifest配置下行:

  android:sharedUserId="android.uid.system"


2、SystemProperties中方法具体实现
SystemProperties.java中所有native方法都是在android_os_SystemProperties.cpp里实现,先来看看java中个方法是怎么和cpp中方法对应起来的

//frameworks/base/core/jni/android_os_SystemProperties.cpp
static const JNINativeMethod method_table[] = {
    { "native_get", "(Ljava/lang/String;)Ljava/lang/String;",
      (void*) SystemProperties_getS },
    { "native_get", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
      (void*) SystemProperties_getSS },
    { "native_get_int", "(Ljava/lang/String;I)I",
      (void*) SystemProperties_get_int },
    { "native_get_long", "(Ljava/lang/String;J)J",
      (void*) SystemProperties_get_long },
    { "native_get_boolean", "(Ljava/lang/String;Z)Z",
      (void*) SystemProperties_get_boolean },
    { "native_set", "(Ljava/lang/String;Ljava/lang/String;)V",
      (void*) SystemProperties_set },
    { "native_add_change_callback", "()V",
      (void*) SystemProperties_add_change_callback },
};
 
int register_android_os_SystemProperties(JNIEnv *env)
{
    return RegisterMethodsOrDie(env, "android/os/SystemProperties", method_table,
                                NELEM(method_table));
}

register_android_os_SystemProperties方法是在AndroidRuntime.cpp中调用的,RegisterMethodsOrDie可以简单的理解为把method_table数组里的方法一一对应起来。在android_os_SystemProperties.cpp中可以发现最终实现是不区分SystemProperties_get_int或 SystemProperties_getS,基本所有的方法都是调用property_get和property_set方法来实现的

property_get和property_set是在properties.c里实现的,如下

//system/core/libcutils/properties.c
int property_get(const char *key, char *value, const char *default_value)
{
    int len;
 
    len = __system_property_get(key, value);
    if(len > 0) {
        return len;
    }
    if(default_value) {
        len = strlen(default_value);
        if (len >= PROPERTY_VALUE_MAX) {
            len = PROPERTY_VALUE_MAX - 1;
        }
        memcpy(value, default_value, len);
        value[len] = '

免责声明:内容来源于网络,仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇IDEA如何安装lombok日常编码规范(Java版)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

JavaMail发送邮件(超详细)

一:邮件发送的基本概念   本文我将阐述使用JavaMail方式发送和接收Email的详细说明,本博客本着以后遇到类似的邮件发送需求可以直接把代码粘过去直接使用,快捷方便省时间,对于刚接触的JavaMail的朋友们还是把文章过一遍,虽然本文不是最好的,但是我可以保证你能成功发送邮件;   关于邮件还有一些基本知识我将在下面简单介绍,关于邮件发送和接收的流程...

Golang 里的 AES、DES、3DES 加解密,支持 ECB、CBC 等多种模式组合

Openssl encryption:OpenSSL库的功能包装,用于对称和非对称加密和解密。 AES-ECB AES-CBC DES-ECB DES-CBC 3DES-ECB 3DES-CBC 安装 go get -u github.com/thinkoner/openssl 用法 AES 密钥的长度可以是16/24/32个字符(128/192/2...

Java Web----------response&&request

1.response 代表响应, 可以理解为一个空的箱子,我们在里面填入要发送到浏览器的内容. 服务器会把这些内容组装成http响应. 1.1 响应首行 协议/版本号 状态码 状态码描述 添加状态码 和 描述 void setStatus(int sc) 只设置状态码 void setStatus(int sc, String sm)...

UITextView 和 UITextField限制字符数和表情符号

UITextField限制字符数 - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string{ int pMaxLength = 12; NSInteg...

C# 共享内存(转)

以下是一个C#操作内存的一个类,只要将下面的类添加到相应的项目中,该项目就可以对内存进行直接操作! using System.Runtime.InteropServices;    //添加如下命名空间 需要是用 DllImport public class ShareMemLib    {    [DllImport("user32.dll", Char...

Redis设置过期时间

一般添加值之后,不设置过期时间的话,ttl某个键会显示-1,表示此时并没有设置过期时间 当设置了过期时间之后,则会显示剩余过期秒数 设置过期时间的做法 #region 设置过期时间 //设置多少秒 client.Set<string>("name",...