Android——SystemProperties的应用

        

一.序

         前文分析了build.prop这个系统属性文件的生成Android——build.prop 解析每个属性都有一个名称和值,他们都是字符串格式。属性被大量使用在Android系统中,用来记录系统设置或进程之间的信息交换。属性是在整个系统中全局可见的。每个进程可以get/set属性,这里主要记录在java层或者c++层如果使用,以及整个system_property运作流程。


二.java层调用

  源码位于/frameworks/base/core/java/android/os/SystemProperties.java中:

get属性:
  /**
     * 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属性:
    /**
     * 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);
    }

都会调用本地接口:
private static native String native_get(String key);
private static native void native_set(String key, String def);


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

/frameworks/base/core/jni/AndroidRuntime.cpp中:

extern int register_android_os_SystemProperties(JNIEnv *env);


/frameworks/base/core/jni/android_os_SystemProperties.cpp中JNI:

static 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 },
};

以set属性为例,调用:

static void SystemProperties_set(JNIEnv *env, jobject clazz,
                                      jstring keyJ, jstring valJ)
{
    int err;
    const char* key;
    const char* val;

    if (keyJ == NULL) {
        jniThrowNullPointerException(env, "key must not be null.");
        return ;
    }
    key = env->GetStringUTFChars(keyJ, NULL);

    if (valJ == NULL) {
        val = "";       /* NULL pointer not allowed here */
    } else {
        val = env->GetStringUTFChars(valJ, NULL);
    }

    err = property_set(key, val);

    env->ReleaseStringUTFChars(keyJ, key);

    if (valJ != NULL) {
        env->ReleaseStringUTFChars(valJ, val);
    }

    if (err < 0) {
        jniThrowException(env, "java/lang/RuntimeException",
                          "failed to set system property");
    }
}

调用到/system/core/libcutils/properties.c中的property_set(key, val)。以java层的理解到这里就OK。


三.c++层的调用

/system/core/libcutils/properties.c中的:

int property_set(const char *key, const char *value)
{
    return __system_property_set(key, value);
}

到/bionic/libc/bionic/system_properties.c中:

int __system_property_set(const char *key, const char *value)
{
....


    memset(&msg, 0, sizeof msg);
    msg.cmd = PROP_MSG_SETPROP;
    strlcpy(msg.name, key, sizeof msg.name);
    strlcpy(msg.value, value, sizeof msg.value);


    err = send_prop_msg(&msg);
    if(err < 0) {
        return err;
    }


    return 0;
}

通过一个普通的TCP(SOCK_STREAM)套接字进行通讯。

static int send_prop_msg(prop_msg *msg)
{
    struct pollfd pollfds[1];
    struct sockaddr_un addr;
    socklen_t alen;
    size_t namelen;
    int s;
    int r;
    int result = -1;


    s = socket(AF_LOCAL, SOCK_STREAM, 0);
    if(s < 0) {
        return result;
    }


    memset(&addr, 0, sizeof(addr));
    namelen = strlen(property_service_socket);
...
}

以上是作为client端,通过socket向service发送消息。




四.property_service服务的启动

property_service 服务的启动是在android初始化的时候在/system/core/init/init.c时建立:


int main(int argc, char **argv)
{
    int fd_count = 0;
    struct pollfd ufds[4];
...
int property_set_fd_init = 0;
...
 queue_builtin_action(property_service_init_action, "property_service_init");
...
 if (!property_set_fd_init && get_property_set_fd() > 0) {
            ufds[fd_count].fd = get_property_set_fd();
            ufds[fd_count].events = POLLIN;
            ufds[fd_count].revents = 0;
            fd_count++;
            property_set_fd_init = 1;
        }
...
  if (ufds[i].revents == POLLIN) {
                if (ufds[i].fd == get_property_set_fd())
                    handle_property_set_fd();
...
}

由init守护进程分配一个共享内存区来存储这些属性。并且通过__libc_init(...)—— __libc_init_common(...)——__system_properties_init();

由/bionic/libc/bionic/system_properties.c中的__system_properties_init()来初始化属性系统的共享内存。


在这里启动了property_service这个系统属性服务,Android——启动过程详析 这里有启动相关细节。


从property_service_init_action调用到/system/core/init/property_service.c中的启动函数:

void start_property_service(void)
{
    int fd;


    load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
    load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);
    load_override_properties();
    /* Read persistent properties after all default values have been loaded. */
    load_persistent_properties();


    fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);
    if(fd < 0) return;
    fcntl(fd, F_SETFD, FD_CLOEXEC);
    fcntl(fd, F_SETFL, O_NONBLOCK);


    listen(fd, 8);
    property_set_fd = fd;
}

可以看到,在这里加载了系统属性文件到共享内存,文件定义在/bionic/libc/include/sys/_system_properties.h:

#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"

属性信息按照上面的顺序被加载。后加载的属性会覆盖前面的属性值(当属性名称相同的时候)。当上面加载完成后,最后加载的是驻留属性,保存在/data/property文件中.

创建了一个SOCK_STREAM套接字并进入监听listen状态!

到这里property_service已经启动完毕!



五.property_service服务消息处理

在init守护进程中监听到有属性服务的事件时调用:

void handle_property_set_fd()
{
    prop_msg msg;
...
 if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {
        return;
    }
...
 r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), 0));
...
 switch(msg.cmd) {
    case PROP_MSG_SETPROP:
...
  if(memcmp(msg.name,"ctl.",4) == 0) {
            // Keep the old close-socket-early behavior when handling
            // ctl.* properties.
            close(s);
            if (check_control_perms(msg.value, cr.uid, cr.gid, source_ctx)) {
                handle_control_message((char*) msg.name + 4, (char*) msg.value);
            } else {
                ERROR("sys_prop: Unable to %s service ctl [%s] uid:%d gid:%d pid:%d\n",
                        msg.name + 4, msg.value, cr.uid, cr.gid, cr.pid);
            }
        } else {
            if (check_perms(msg.name, cr.uid, cr.gid, source_ctx)) {
                property_set((char*) msg.name, (char*) msg.value);
            } else {
                ERROR("sys_prop: permission denied uid:%d  name:%s\n",
                      cr.uid, msg.name);
            }
...
}

接收socket请求连接,接收属性请求数,处理信息:

可以看到如果接收到的信息是以“ctl”开头,进行check_control_perms(msg.value, cr.uid, cr.gid, source_ctx)鉴权处理。

这里的传入消息值,发送消息进程的uid以及gid。

这里是控制权限数组:

/*
 * White list of UID that are allowed to start/stop services.
 * Currently there are no user apps that require.
 */
struct {
    const char *service;
    unsigned int uid;
    unsigned int gid;
} control_perms[] = {
    { "dumpstate",AID_SHELL, AID_LOG },
    { "ril-daemon",AID_RADIO, AID_RADIO },
     {NULL, 0, 0 }
};

在/system/core/include/private/android_filesystem_config.h中有各种权限的定义:

#define AID_ROOT             0  /* traditional unix root user */

#define AID_SYSTEM        1000  /* system server */

#define AID_RADIO         1001  /* telephony subsystem, RIL */
#define AID_BLUETOOTH     1002  /* bluetooth subsystem */
#define AID_GRAPHICS      1003  /* graphics devices */


有权限 就执行/system/core/init/init.c中的:

void handle_control_message(const char *msg, const char *arg)
{
    if (!strcmp(msg,"start")) {
        msg_start(arg);
    } else if (!strcmp(msg,"stop")) {
        msg_stop(arg);
    } else if (!strcmp(msg,"restart")) {
        msg_stop(arg);
        msg_start(arg);
    } else {
        ERROR("unknown control msg '%s'\n", msg);
    }
}

这个是用来 开启和关闭或者重启 服务!


一般的消息值鉴权check_perms(msg.name, cr.uid, cr.gid, source_ctx)。

一般的控制权限数组:

/* White list of permissions for setting property services. */
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 },
...
};

有权限则执行:

int property_set(const char *name, const char *value)
{
...
 if(pi != 0) {
        /* ro.* properties may NEVER be modified once set */
        if(!strncmp(name, "ro.", 3)) 
         return -1;
...
 /* If name starts with "net." treat as a DNS property. */
    if (strncmp("net.", name, strlen("net.")) == 0)  {
        if (strcmp("net.change", name) == 0) {
            return 0;
        }
       /*
        * The 'net.change' property is a special property used track when any
        * 'net.*' property name is updated. It is _ONLY_ updated here. Its value
        * contains the last updated 'net.*' property.
        */
        property_set("net.change", name);
    } else if (persistent_properties_loaded &&
            strncmp("persist.", name, strlen("persist.")) == 0) {
        /*
         * Don't write properties to disk until after we have read all default properties
         * to prevent them from being overwritten by default values.
         */
        write_persistent_property(name, value);
#ifdef HAVE_SELINUX
    } else if (strcmp("selinux.reload_policy", name) == 0 &&
               strcmp("1", value) == 0) {
        selinux_reload_policy();
#endif
    }
    property_changed(name, value);
    return 0;
}

这个property_set就是set属性真正的执行函数!


可以看到会判断是否以“ro”字符串开头,如果接收到的消息值,也就是要set的属性值以这个作为开头,就代表只读,不能被改变.


如果共享内存中有则update_prop_info(pi, value, valuelen);没有就保存到内存中。


如果属性是有“net.”字符串开头,当设置这种属性的时候,“net.change”这条属性也会被自动设置,其内容设为最后更新过的属性名,用来记录net.*属性上面的变化。


如果属性是有“persist.”字符串开头,那么就认为是驻留属性,当修改的时候同时也会写进/data/property文件中。


最后调用property_changed(name, value),通知属性已经改变,更新属性,仅仅在运行时可用的属性不需要调用这个方法,除非它们能被数据绑定。


到这里property_service服务已经大体分析完!



六.adb shell 命令

adb shell getprop    列出系统所有属性 


adb shell getprop |  grep lcd   列出包含lcd的属性 


adb shell setprop <key> <value>    修改指定的系统属性 




撰写不易,转载请注明出处http://blog.youkuaiyun.com/jscese/article/details/18700903




<think> 首先,用户的问题是:“SystemProperties.getBoolean函数的作用”,我需要用中文回答。然后,在回答完后,我必须提出3-5个相关问题,这些问题必须基于用户的问题和我的回答。相关问题的输出必须以“§§相关问题§§”开头,并且不能换行分散——意思是“§§相关问题§§”必须在第一行完整输出,然后紧接着列出问题。 回顾规则: - 回答完问题后,必须提出3-5个 §§相关问题§§。 - §§相关问题§§ 必须输出在回答的末尾。 - 第一行必须是完整的“§§相关问题§§”,然后直接列出问题。 - 禁止换行分散:意思是“§§相关问题§§”应该在一行输出,然后问题列表紧跟其后。 现在,问题主题是“SystemProperties.getBoolean函数的作用”。这似乎是Android开发中的一个函数,因为SystemPropertiesAndroid的一个类,用于访问系统属性。 回想一下,在Android中,SystemProperties.getBoolean是一个静态方法,用于获取系统属性的布尔值。系统属性是存储在设备上的键值对,可以通过adb或代码访问。 函数签名通常是:SystemProperties.getBoolean(String key, boolean defaultValue) - 它接受一个键(key)和一个默认值(defaultValue)。 - 它返回与该键关联的布尔值。如果键不存在或无法解析为布尔值,则返回默认值。 在回答时,我需要用中文解释清楚。 我的回答结构: 1. 先解释函数的作用。 2. 提供一些上下文或示例。 3. 最后,添加“§§相关问题§§”并列出3-5个相关问题。 相关问题必须基于用户的问题和我的回答。所以,我可以问关于用法、参数、异常、类似函数等。 可能的回答内容: - SystemProperties.getBoolean函数用于从Android系统中检索一个布尔类型的属性值。 - 它需要两个参数:键(key)和默认值(defaultValue)。 - 如果指定的键存在并且可以解析为布尔值,则返回该值;否则返回默认值。 - 示例:用于检查系统属性如“debug.enable”等。 现在,列出相关问题(3-5个): 1. SystemProperties.getBoolean函数的参数是什么? 2. 如果键不存在,函数会返回什么? 3. 除了getBoolean,SystemProperties类还有哪些其他获取属性的方法? 4. 如何在Android应用中使用SystemProperties类? 5. SystemProperties.getBoolean与SharedPreferences有什么不同? 确保问题数量在3到5个。 最后,输出格式: - 回答部分结束。 - 然后,直接输出“§§相关问题§§” - 紧接着列出问题,例如: §§相关问题§§ 1. 第一个问题 2. 第二个问题 3. 第三个问题 在示例中是: §§相关问题§§ 1. ··· 2. ··· 3. ··· 所以,我需要保持这种格式。 完整响应: [回答内容] §§相关问题§§ 1. ... 2. ... 3. ... </think>SystemProperties.getBoolean函数是Android系统API中的一个方法,主要用于从系统属性中检索布尔值。系统属性是Android设备上存储的键值对,用于配置或控制设备行为(如调试标志或功能开关)。该函数的签名通常为`boolean getBoolean(String key, boolean defaultValue)`,其作用如下: - **参数**: - `key`:要查询的系统属性键名(例如"debug.enable")。 - `defaultValue`:当键不存在或值无法解析为布尔值时返回的默认值。 - **返回值**: - 如果键存在且值可解析为布尔值(如"true"或"false",不区分大小写),则返回对应布尔值。 - 如果键不存在、值为空或无效(如非布尔字符串),则返回指定的`defaultValue`。 - **使用场景**:常用于系统级开发或系统应用中,例如在启动时检查特定功能是否启用(如`SystemProperties.getBoolean("ro.debuggable", false)`)。注意,此函数需要系统权限(如`android.permission.READ_SYSTEM_PROPERTIES`),普通应用可能无法直接调用。 示例代码片段: ```java boolean isDebugEnabled = SystemProperties.getBoolean("debug.enable", false); // 如果系统属性"debug.enable"为true,则返回true;否则返回false ```
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值