一、Android4.4属性系统系列文章
Android4.4属性系统-初始化
Android4.4属性系统-系统服务
Android4.4属性系统-内存空间共享
Android4.4属性系统-属性获取
Android4.4属性系统-属性设置
Android4.4-属性的使用总结
二、写在前面-如何阅读本系列文章
本系列文章大部分是对源码的解析和注释,所以读起来枯燥无味,并且杂乱,这是阅读系统源码无法避免的,如果你有条件,可以点击下载Android4.4源码,阅读源码可以使用eclise,AndroidStudio,vim等。
文章的章节安卓是按照代码模块区分的,例如init进程代码,libcutils代码是按章节来区分,但不同模块的代码之间是有关联的,阅读时需要经常跳转,通过搜索功能进行页内搜索即可
三、属性设置
Android 除了提供属性获取函数外,当然还可以进行属性的设置操作,在 Java 层可以通过调用 SystemProperties.set(String key, String val)方法进行属性设置。
3.1 frameworks接口
源码路径frameworks/base/core/java/android/os/Build.java
/** * 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); }
首先对需要设置的属性的名称以及值做长度检查,再调用 Native函数native_set。native_set对应的C++层函数为SystemProperties_set(/frameworks/base/core/jni/android_os_SystemProperties.cpp)
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 }, }; //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"); } }
调用了 property_set 函数,该函数统一定义在/system/core/libcutils/properties.c 文件中
3.3 libc库层
3.3.1 内核c库层
源码路径system/core/libcutils/properties.c
#include <cutils/properties.h> //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); }
调用了__system_property_set 函数,该函数位于/bionic/libc/bionic/system_properties.c 文件
3.3.2 bionic库层
源码路径bionic/libc/bionic/system_properties.c
该函数通过__system_property_find 函数在系统属性内存区域查询是否存在 name 参数指定的属性,如果查询到则会通过__system_property_read 读取属性信息。关于__system_property_find函数,前文已经分析过了。__system_property_read 通过获取的内存地址,从内存中读取属性信息。
//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); //send_prop_msg 函数向属性系统服务的 socket发送属性设置请求信息 err = send_prop_msg(&msg); if(err < 0) { return err; } return 0; } //发送消息 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)); // property_service_socket 值为”/dev/socket/property_service” //正是前文分析 Android 属性系统服务所监听的 socket namelen = strlen(property_service_socket); strlcpy(addr.sun_path, property_service_socket, sizeof addr.sun_path); addr.sun_family = AF_LOCAL; alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1; //connect尝试连接这个服务端 socket if(TEMP_FAILURE_RETRY(connect(s, (struct sockaddr *) &addr, alen)) < 0) { close(s); return result; } //send向该 socket 发送消息 r = TEMP_FAILURE_RETRY(send(s, msg, sizeof(prop_msg), 0)); if(r == sizeof(prop_msg)) { //我们成功写入了属性服务器,但现在我们等待属性服务器完成其工作。它通过关闭套接字来确认它的完成,所以我们在这里 //轮询(什么都没有),等待套接字关闭。如果你'adb shell setprop foo bar',你会在套接字关闭后看到POLLHUP。出于偏 //执,我们将调查结果限制在250毫秒。 pollfds[0].fd = s; pollfds[0].events = 0; r = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */)); if (r == 1 && (pollfds[0].revents & POLLHUP) != 0) { result = 0; } else { //忽略超时并将其视为成功。 init进程是单线程的,它的属性服务有时响应很慢(可能是关闭启动子进程或其他东西),因此 //这时超时并且调用者认为它失败了,即使它仍然可以解决它。所以我们在这里伪造它,主要是为了ctl.*属性,但是我们尝 //试等待250毫秒,因此大多数时候执行read-after-write的调用者可以可靠地看到他们写的内容。 result = 0; } } close(s); return result; }
prop_msg消息结构体定义在bionic/libc/include/sys/_system_properties.h
,源码如下:
#define PROP_MSG_SETPROP 1 struct prop_msg { unsigned cmd; char name[PROP_NAME_MAX]; char value[PROP_VALUE_MAX]; };
上面的分析完成了向socket服务器发送请求消息的过程,那么服务器端是如何接收并处理的呢。
3.3 socket服务端接收请求消息并处理
在 init.c 文件中 main 函数的无限 for 循环中有对上述 socket 事件的处理,关键代码如下
源码位置system/core/init/init.c
int main(int argc, char **argv) { .... for(;;) { int nr, i, timeout = -1; execute_one_command(); restart_processes(); //设置需要监听的 socket 文件描述符以及 socket 请求的事件 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; } .... //调用 poll 检查是否有期望的 soket 事件发生 nr = poll(ufds, fd_count, timeout); if (nr <= 0) continue; for (i = 0; i < fd_count; i++) { if (ufds[i].revents == POLLIN) { //两个if判断所发生的的 socket 事件是否是来自于属性服务的socket if (ufds[i].fd == get_property_set_fd()) handle_property_set_fd(); //处理相应的 socket 请求,定义在property_service.c else if (ufds[i].fd == get_keychord_fd()) handle_keychord(); else if (ufds[i].fd == get_signal_fd()) handle_signal(); } } } //处理控制消息,handle_control_message 根据属性名称来启动、停止或者重启服务 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_restart(arg); } else { ERROR("unknown control msg '%s'\n", msg); } } //prop发生改变 void property_changed(const char *name, const char *value) { if (property_triggers_enabled) //是否开启了prop触发器 queue_property_triggers(name, value); } static int queue_property_triggers_action(int nargs, char **args) { queue_all_property_triggers(); //定义在init_parser.c /* enable property triggers */ property_triggers_enabled = 1; return 0; }
源码路径system/core/init/init_parser.c
void queue_all_property_triggers() { struct listnode *node; struct action *act; list_for_each(node, &action_list) { act = node_to_item(node, struct action, alist); if (!strncmp(act->name, "property:", strlen("property:"))) { /* parse property name and value syntax is property:<name>=<value> */ const char* name = act->name + strlen("property:"); const char* equals = strchr(name, '='); if (equals) { char prop_name[PROP_NAME_MAX + 1]; char value[PROP_VALUE_MAX]; int length = equals - name; if (length > PROP_NAME_MAX) { ERROR("property name too long in trigger %s", act->name); } else { memcpy(prop_name, name, length); prop_name[length] = 0; /* does the property exist, and match the trigger value? */ property_get(prop_name, value); if (!strcmp(equals + 1, value) ||!strcmp(equals + 1, "*")) { action_add_queue_tail(act); } } } } } }