Android4.4属性配置SystemProperties使用笔记

简介: 笔记

本文简单介绍SytemProperties的调用流程

涉及到的源文件:

frameworks/base/core/java/android/os/SystemProperties.java

frameworks/base/core/jni/android_os_SystemProperties.cpp

system/core/libcutils/properties.c

bionic/libc/bionic/system_properties.c


一、frameworks层java接口


frameworks/base/core/java/android/os/SystemProperties.java

以set和get方法举例

//定义key和value的最大长度
public static final int PROP_NAME_MAX = 31;
public static final int PROP_VALUE_MAX = 91;
/**
 * Get the value for the given key.
 * @return if the key isn't found, return def if it isn't null, or an empty string otherwise
 * @throws IllegalArgumentException if the key exceeds 32 characters
 */
public static String get(String key, String def) {
    //判断key的长度
    if (key.length() > PROP_NAME_MAX) {
        throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
    }
    //调用JNI方法
    return native_get(key, def);
}
/**
 * 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);
    }
    //判断value的长度
    if (val != null && val.length() > PROP_VALUE_MAX) {
        throw new IllegalArgumentException("val.length > " +
            PROP_VALUE_MAX);
    }
    //调用JNI方法
    native_set(key, val);
}

Java的方法中调用的是native对应的get和set方法


二、framworks层JNI接口


frameworks/base/core/jni/android_os_SystemProperties.cpp

2.1 方法映射表

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

根据方法映射表,可以看到native_get和native_set对应着的方法实现。


2.2 native_get和native_set的实现

//native_get对应SystemProperties_getS
static jstring SystemProperties_getS(JNIEnv *env, jobject clazz,
                                      jstring keyJ)
{
    return SystemProperties_getSS(env, clazz, keyJ, NULL);
}
//SystemProperties_getS实际调用了SystemProperties_getSS
static jstring SystemProperties_getSS(JNIEnv *env, jobject clazz, jstring keyJ, jstring defJ)
{   
    int len;
    const char* key;
    char buf[PROPERTY_VALUE_MAX];
    jstring rvJ = NULL;
    if (keyJ == NULL) {
        jniThrowNullPointerException(env, "key must not be null.");
        goto error;
    }
    //将jstring类型变成一个char *类型
    key = env->GetStringUTFChars(keyJ, NULL);
    //关键代码:调用property_get方法,返回的数据写入bug,在properties.c中实现
    len = property_get(key, buf, ""); 
    //做一些错误处理
    if ((len <= 0) && (defJ != NULL)) {
        rvJ = defJ;
    } else if (len >= 0) {
        rvJ = env->NewStringUTF(buf);
    } else {
        rvJ = env->NewStringUTF("");
    }
    //调用ReleaseStringUTFChars函数通知JVM这块内存已经不使用
    env->ReleaseStringUTFChars(keyJ, key);
error:
    return rvJ;
} 
//native_set方法对应SystemProperties_set
static void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ, jstring valJ)
{
    LOGD("SystemProperties_set,keyJ=%s,valJ=%s", keyJ, valJ);
    int err;
    const char* key;
    const char* val;
    if (keyJ == NULL) {
        jniThrowNullPointerException(env, "key must not be null.");
        return ;
    }
    //将jstring类型变成一个char *类型
    key = env->GetStringUTFChars(keyJ, NULL);
    if (valJ == NULL) {
        val = "";       /* NULL pointer not allowed here */
    } else {
        val = env->GetStringUTFChars(valJ, NULL);
    }
    //关键代码:调用property_set,在properties.c中实现
    err = property_set(key, val);
    //调用ReleaseStringUTFChars函数通知JVM这块内存已经不使用
    env->ReleaseStringUTFChars(keyJ, key);
    //一些错误处理
    if (valJ != NULL) {
        env->ReleaseStringUTFChars(valJ, val);
    }
    if (err < 0) {
         jniThrowException(env, "java/lang/RuntimeException",
                           "failed to set system property");
     }
 }

从上面的分析中可以看到,JNI接口的set和get方法已经流转到properties.c中


三、kernel lib库层请求接口


3.1 接口调用 properties.c

system/core/libcutils/properties.c

//get方法,参数:<key,buf,def_value>
int property_get(const char *key, char *value, const char *default_value)
{   
    int len;
    //关键调用,位于bionic/libc/bionic/system_properties.c
    len = __system_property_get(key, value);
    if(len > 0) {
        return len;
    }
    if(default_value) {
        len = strlen(default_value);
        memcpy(value, default_value, len + 1);
    }
    return len;
}
//set方法,参数<key,value>
int property_set(const char *key, const char *value)
{   
    ALOGV("property_set, key=%s,value=%s", key, value);
    //关键调用,位于bionic/libc/bionic/system_properties.c
    return __system_property_set(key, value);
}


3.1.1 相关定义

  1. 属性操作类型定义
    system/core/include/cutils/properties.h

//定义操作类型
enum {
    kSystemPropertyUnknown = 0,
    kSystemPropertyGet,
    kSystemPropertySet,
    kSystemPropertyList
};
  1. key,value长度定义
    system/core/include/cutils/properties.h

/* System properties are *small* name value pairs managed by the
** property service.  If your data doesn't fit in the provided
** space it is not appropriate for a system property.
**
** WARNING: system/bionic/include/sys/system_properties.h also defines
**          these, but with different names.  (TODO: fix that)
*/
#define PROPERTY_KEY_MAX   PROP_NAME_MAX
#define PROPERTY_VALUE_MAX  PROP_VALUE_MAX

bionic/libc/include/sys/system_properties.h

#define PROP_NAME_MAX   32
#define PROP_VALUE_MAX  92

pthread_once:在多线程环境中,有些事仅需要执行一次。通常当初始化应用程序时,可以比较容易地将其放在main函数中。但当你写一个库时,就不能在main里面初始化了,你可以用静态初始化,但使用一次初始化(pthread_once)会比较容易些


3.2 发送请求消息Socket客户端 system_properties.c

bionic/libc/bionic/system_properties.c

//get方法,参数<key, buff>
int __system_property_get(const char *name, char *value)
{
    //检查该key值是否已经存在
    const prop_info *pi = __system_property_find(name);
    if(pi != 0) {
         //如果存在,调用__system_property_read,返回值存储在pi中
        return __system_property_read(pi, 0, value);
    } else {//不存在则返回0
        value[0] = 0;
        return 0;
    }
}
//get的调用,参数<prop_info,key, buff>
int __system_property_read(const prop_info *pi, char *name, char *value)
{
    unsigned serial, len;
    if (__predict_false(compat_mode)) {
        return __system_property_read_compat(pi, name, value);
    }
    for(;;) {
        serial = pi->serial;
        while(SERIAL_DIRTY(serial)) {
            __futex_wait((volatile void *)&pi->serial, serial, 0);
            serial = pi->serial;
        }
        len = SERIAL_VALUE_LEN(serial);
        memcpy(value, pi->value, len + 1);
        ANDROID_MEMBAR_FULL();
        if(serial == pi->serial) {
            if(name != 0) {
                strcpy(name, pi->name);
            }
            return len;
        }
    }
}
//set方法
int __system_property_set(const char *key, const char *value)
{
    printf("__system_property_set--bianjb,key=%s,value=%s", key, value);
    int err;
    //prop_msg为要发送给服务端的消息句柄
    prop_msg msg;
    if(key == 0) return -1;
    if(value == 0) value = "";
    //判断key,value长度是否合法
    if(strlen(key) >= PROP_NAME_MAX) return -1;
    if(strlen(value) >= PROP_VALUE_MAX) return -1;
    memset(&msg, 0, sizeof msg);
    //设置Prop操作类型
    msg.cmd = PROP_MSG_SETPROP;
    //设置消息name字段
    strlcpy(msg.name, key, sizeof msg.name);
    //设置消息value字段
    strlcpy(msg.value, value, sizeof msg.value);
    //发送消息
    err = send_prop_msg(&msg);
    if(err < 0) {
        return err;
    }
    return 0;
}

后面就是property_service接收到消息后的处理了,后面会继续更新。

目录
相关文章
|
3月前
|
Web App开发 安全 程序员
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
多年的互联网寒冬在今年尤为凛冽,坚守安卓开发愈发不易。面对是否转行或学习新技术的迷茫,安卓程序员可从三个方向进阶:1)钻研谷歌新技术,如Kotlin、Flutter、Jetpack等;2)拓展新功能应用,掌握Socket、OpenGL、WebRTC等专业领域技能;3)结合其他行业,如汽车、游戏、安全等,拓宽职业道路。这三个方向各有学习难度和保饭碗指数,助你在安卓开发领域持续成长。
87 1
FFmpeg开发笔记(五十五)寒冬里的安卓程序员可进阶修炼的几种姿势
|
3月前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
119 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
3月前
|
编译器 Android开发
配置环境变量,使CMakeLists.txt可直接使用Android NDK工具链编译项目
配置环境变量,使CMakeLists.txt可直接使用Android NDK工具链编译项目
|
3月前
|
Java Android开发 Windows
玩转安卓之配置gradle-8.2.1
为安卓开发配置Gradle 8.2.1,包括下载和解压Gradle、配置环境变量、修改配置文件以增加国内镜像,以及在Android Studio中配置Gradle和JDK的过程。
128 0
玩转安卓之配置gradle-8.2.1
|
5月前
|
JavaScript 前端开发 Java
FFmpeg开发笔记(四十七)寒冬下安卓程序员的几个技术转型发展方向
IT寒冬使APP开发门槛提升,安卓程序员需转型。选项包括:深化Android开发,跟进Google新技术如Kotlin、Jetpack、Flutter及Compose;研究Android底层框架,掌握AOSP;转型Java后端开发,学习Spring Boot等框架;拓展大前端技能,掌握JavaScript、Node.js、Vue.js及特定框架如微信小程序、HarmonyOS;或转向C/C++底层开发,通过音视频项目如FFmpeg积累经验。每条路径都有相应的书籍和技术栈推荐,助你顺利过渡。
127 3
FFmpeg开发笔记(四十七)寒冬下安卓程序员的几个技术转型发展方向
|
5月前
|
Android开发
Android 配置蓝牙遥控器键值
本文详细介绍了Android系统中配置蓝牙遥控器键值的步骤,包括查看设备号、配置键位映射文件(kl文件)、部署kl文件以及调试过程,确保蓝牙遥控器的按键能正确映射到Android系统对应的按键功能。
336 1
|
5月前
|
搜索推荐 Android开发
学习AOSP安卓系统源代码,需要什么样的电脑?不同配置的电脑,其编译时间有多大差距?
本文分享了不同价位电脑配置对于编译AOSP安卓系统源代码的影响,提供了从6000元到更高价位的电脑配置实例,并比较了它们的编译时间,以供学习AOSP源代码时电脑配置选择的参考。
315 0
学习AOSP安卓系统源代码,需要什么样的电脑?不同配置的电脑,其编译时间有多大差距?
|
5月前
|
Ubuntu Android开发
安卓系统调试与优化:(一)bootchart 的配置和使用
本文介绍了如何在安卓系统中配置和使用bootchart工具来分析系统启动时间,包括安装工具、设备端启用bootchart、PC端解析数据及分析结果的详细步骤。
245 0
安卓系统调试与优化:(一)bootchart 的配置和使用
|
5月前
|
编解码 安全 Ubuntu
Android Selinux 问题处理笔记
这篇文章是关于处理Android系统中SELinux权限问题的笔记,介绍了如何通过分析SELinux拒绝的日志、修改SELinux策略文件,并重新编译部署来解决权限问题,同时提供了一些SELinux的背景知识和实用工具。
149 0
|
5月前
|
监控 安全 API
Android项目架构设计问题之保证线上用户不会进入到本地配置页面如何解决
Android项目架构设计问题之保证线上用户不会进入到本地配置页面如何解决
36 0