一、Android4.4属性系统系列文章
Android4.4属性系统-初始化
Android4.4属性系统-系统服务
Android4.4属性系统-内存空间共享
Android4.4属性系统-属性获取
Android4.4属性系统-属性设置
Android4.4-属性的使用总结
二、写在前面-如何阅读本系列文章
本系列文章大部分是对源码的解析和注释,所以读起来枯燥无味,并且杂乱,这是阅读系统源码无法避免的,如果你有条件,可以点击下载Android4.4源码,阅读源码可以使用eclise,AndroidStudio,vim等。
文章的章节安卓是按照代码模块区分的,例如init进程代码,libcutils代码是按章节来区分,但不同模块的代码之间是有关联的,阅读时需要经常跳转,通过搜索功能进行页内搜索即可
三、安卓属性获取
在 Android 应用程序开发中,可以通过 android.os.Build 类来访问一些属性信息,如设备品牌,设备名称,CPU 信息等。我们以访问 Build.java相关接口 来分析 Android 系统中是如何获取属性信息的。
3.1 frameworks接口
3.1.1 Build.java
源码路径frameworks/base/core/java/android/os/Build.java
以下是截取了一小段代码
/** Value used for when a build property is unknown. */ public static final String UNKNOWN = "unknown"; /** Either a changelist number, or a label like "M4-rc20". */ public static final String ID = getString("ro.build.id"); /** A build ID string meant for displaying to the user */ public static final String DISPLAY = getString("ro.build.display.id"); /** The name of the overall product. */ public static final String PRODUCT = getString("ro.product.name"); /** The name of the industrial design. */ public static final String DEVICE = getString("ro.product.device"); /** The name of the underlying board, like "goldfish". */ public static final String BOARD = getString("ro.product.board"); private static String getString(String property) { return SystemProperties.get(property, UNKNOWN); } private static long getLong(String property) { try { return Long.parseLong(SystemProperties.get(property)); } catch (NumberFormatException e) { return -1; } }
可以看到Build.java中定义常量字段在构造函数之前确定,调用的是getString()
方法,我们以Build.PRODUCT为例,进行探究。
代码流转到SystemProperties.java
3.1.2 SystemProperties.java
源码 路径:frameworks/base/core/java/android/os/SystemProperties.java
private static native String native_get(String key); //调用的naveti方法,key="ro.product.name" private static native String native_get(String key, String def); private static native int native_get_int(String key, int def); private static native long native_get_long(String key, long def); private static native boolean native_get_boolean(String key, boolean def); private static native void native_set(String key, String def); private static native void native_add_change_callback(); /** * 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 */ //key="ro.product.name" public static String get(String key) { if (key.length() > PROP_NAME_MAX) { throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX); } return native_get(key); }
一般来说调用 Native 函数,需要在 static 块中加载对应的库,即调用 System.loadLibrary,但是在 SystemProperties 类中并没有显示的加载对应的库。原来在 Zygote 启动过程中会调用startReg 来注册 JNI 函数,native_get 这个 JNI 函数就是在此时注册的。关于注册过程可以简单分析如下。
3.1.3 native函数注册过程
源码路径frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, char* const argv[]) { bool zygote = false; //是否是zygote进程 ... while (i < argc) { ... } else if (strcmp(arg, "--zygote") == 0) { zygote = true; //是zygote进程 niceName = "zygote"; } else if (strcmp(arg, "--start-system-server") == 0) { ... } if (zygote) { //启动Zygote runtime.start("com.android.internal.os.ZygoteInit", startSystemServer ? "start-system-server" : ""); } else if (className) { ... } }
源码路径frameworks/base/core/jni/AndroidRuntime.cpp
//注册函数数组 static const RegJNIRec gRegJNI[] = { .... REG_JNI(register_android_os_SystemProperties), //注册函数 .... } void AndroidRuntime::start(const char* className, const char* options) { ... /* * Register android functions. * startReg 完成相关 Native 函数的注册,该函数最终调用 register_jni_procs 完成注册过程 */ if (startReg(env) < 0) { ALOGE("Unable to register all android natives\n"); return; } ... } /* * Register android native functions with the VM. */ /*static*/ int AndroidRuntime::startReg(JNIEnv* env) { ... if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) { env->PopLocalFrame(NULL); ... } /*register_jni_procs 会遍历传入的 RegJNIRec 数组并调用相应的函数进行注册,这个数组定义在 *AndroidRuntime.cpp 中名为 gRegJNI,其中包括了 register_android_os_SystemProperties,这个 *函数定义在/frameworks/base/core/jni/android_os_SystemProperties.cpp 中 */ static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env) { for (size_t i = 0; i < count; i++) { if (array[i].mProc(env) < 0) { return -1; } } return 0; }
3.2 JNI接口
源码 路径frameworks/base/core/jni/android_os_SystemProperties.cpp
JNI代码中,有方法映射表,native_get映射为SystemProperties_getS方法
#include "cutils/properties.h" //方法映射表 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 }, }; //keyJ="ro.product.name" //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; } /*JNI注册函数,通过 AndroidRuntime::registerNativeMethods 注册 method_table 数组参数中定义的 Native 函数。 *关于 registerNativeMethods,有兴趣的可以继续分析,此处就忽略了,后续会另外开篇进行分析 */ int register_android_os_SystemProperties(JNIEnv *env) { return AndroidRuntime::registerNativeMethods( env, "android/os/SystemProperties", method_table, NELEM(method_table)); }
3.3 libc库层
3.3.1 内核c库层
源码路径system/core/libcutils/properties.c
#include <cutils/properties.h> //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; }
3.3.2 bionic库层
源码路径bionic/libc/bionic/system_properties.c
该函数通过__system_property_find 函数在系统属性内存区域查询是否存在 name 参数指定的属性,如果查询到则会通过__system_property_read 读取属性信息。关于__system_property_find函数,前文已经分析过了。__system_property_read 通过获取的内存地址,从内存中读取属性信息。
//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); } //prop_info 中的 serial,其高8 位表示该 prop_info 中 name 的长度,而低 24 位表示该 prop_info 被更新的次数 for(;;) { serial = pi->serial; while(SERIAL_DIRTY(serial)) { __futex_wait((volatile void *)&pi->serial, serial, 0); serial = pi->serial; } len = SERIAL_VALUE_LEN(serial); //获取prop的name长度 memcpy(value, pi->value, len + 1); //获取value值 ANDROID_MEMBAR_FULL(); if(serial == pi->serial) { if(name != 0) { strcpy(name, pi->name); } return len; } } }
四、放图
五、参考
Android属性系统
Linux 内存映射函数 mmap
trie-特里结构
Linux 内存映射函数 mmap()函数详解