http://blog.csdn.net/kc58236582/article/details/51939322
属性在Android中非常重要,我们基本的不多介绍了,主要说下其用法,原理等。
一、java层获取属性
在Java层主要通过SystemProperties这个类来访问android的系统属性,通过一系列的native函数。
- public class SystemProperties
- {
- ......
- public static String get(String key) {
- if (key.length() > PROP_NAME_MAX) {
- throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
- }
- return native_get(key);
- }
- public static String get(String key, String def) {
- if (key.length() > PROP_NAME_MAX) {
- throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
- }
- return native_get(key, def);
- }
- public static int getInt(String key, int def) {
- if (key.length() > PROP_NAME_MAX) {
- throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
- }
- return native_get_int(key, def);
- }
- public static long getLong(String key, long def) {
- if (key.length() > PROP_NAME_MAX) {
- throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
- }
- return native_get_long(key, def);
- }
- public static boolean getBoolean(String key, boolean def) {
- if (key.length() > PROP_NAME_MAX) {
- throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
- }
- return native_get_boolean(key, def);
- }
- 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);
- }
我们再来看下android_os_SystemProperties.cpp中的这些native函数,注意都是静态的,因为在java层也是静态调用。
- static jboolean SystemProperties_get_boolean(JNIEnv *env, jobject clazz,
- jstring keyJ, jboolean defJ)
- {
- int len;
- const char* key;
- char buf[PROPERTY_VALUE_MAX];
- jboolean result = defJ;
- if (keyJ == NULL) {
- jniThrowNullPointerException(env, "key must not be null.");
- goto error;
- }
- key = env->GetStringUTFChars(keyJ, NULL);
- len = property_get(key, buf, "");
- if (len == 1) {
- char ch = buf[0];
- if (ch == '0' || ch == 'n')
- result = false;
- else if (ch == '1' || ch == 'y')
- result = true;
- } else if (len > 1) {
- if (!strcmp(buf, "no") || !strcmp(buf, "false") || !strcmp(buf, "off")) {
- result = false;
- } else if (!strcmp(buf, "yes") || !strcmp(buf, "true") || !strcmp(buf, "on")) {
- result = true;
- }
- }
- env->ReleaseStringUTFChars(keyJ, key);
- error:
- return result;
- }
- 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文件中的下面函数
- int property_set(const char *key, const char *value)
- {
- return __system_property_set(key, value);
- }
- 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] = '\0';
- }
- return len;
- }
- int __system_property_get(const char *name, char *value)
- {
- const prop_info *pi = __system_property_find(name);//从共享内存上获取相应的属性内存
- if (pi != 0) {
- return __system_property_read(pi, 0, value);//从属性内存中读取属性内容
- } else {
- value[0] = 0;
- return 0;
- }
- }
- int __system_property_set(const char *key, const char *value)
- {
- if (key == 0) return -1;
- if (value == 0) value = "";
- if (strlen(key) >= PROP_NAME_MAX) return -1;
- if (strlen(value) >= PROP_VALUE_MAX) return -1;
- prop_msg msg;
- memset(&msg, 0, sizeof msg);
- msg.cmd = PROP_MSG_SETPROP;
- strlcpy(msg.name, key, sizeof msg.name);
- strlcpy(msg.value, value, sizeof msg.value);
- const int err = send_prop_msg(&msg);
- if (err < 0) {
- return err;
- }
- return 0;
- }
有一点需要注意java中还有一个函数System.getProperty获取的不是系统属性,不要混淆了。
二、c层获取属性
c层获取属性我们就是通过上面的property_set和property_get方法
- int property_set(const char *key, const char *value)
- {
- return __system_property_set(key, value);
- }
- 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] = '\0';
- }
- return len;
- }
三、init进程处理属性
系统中的每个进程都可以调用这些函数来读取和修改属性。读取属性值对任何进程都是没有限制的,直接由本进程从共享区中读取;但是修改属性值则必须通过init进程完成,同时进程还需要检查请求的进程是否有权限修改该属性值。
属性值成功修改后,init进程会检查init.rc中是否定义了该属性值的触发器。如果有定义,就执行该触发器下的命令。看下面:
- on property:sys.lc.amtmode=0
- class_start core
- class_start main
- class_start late_start
- start lc-oms-sa
3.1 属性分类
我们看下属性的一些分类:
1.ro前缀的,"ro."这样的属性是只读属性,一旦设置,属性值不能再改变了。
2.persist前缀的,"persist."这样的属性改变会写入目录data/property下与属性名相同的文件中。再次开机时这些值会被init进程读取出来,因此关机再启动也是生效的。
3.NET前缀的,"net."这样的属性当它改变时,属性"net.change"将会被自动设置为最后修改的属性名
4.属性"ctl.start" "ctl.stop"和 "ctl.restart"属性控制类属性,用于启动和停止服务的。使用ctl.start启动服务时,系统将会启动结果放在名为"init.svc.服务名”属性中。
3.2 创建属性系统共享空间
property_init 主要是在__system_property_area_init函数中创建了共享内存
- void property_init() {
- if (property_area_initialized) {
- return;
- }
- property_area_initialized = true;
- if (__system_property_area_init()) {
- return;
- }
- pa_workspace.size = 0;
- pa_workspace.fd = open(PROP_FILENAME, O_RDONLY | O_NOFOLLOW | O_CLOEXEC);
- if (pa_workspace.fd == -1) {
- ERROR("Failed to open %s: %s\n", PROP_FILENAME, strerror(errno));
- return;
- }
- }
我们来看__system_property_area_init函数,最后是在map_prop_area_rw函数中调用了mmap创建了共享内存
- int __system_property_area_init()
- {
- return map_prop_area_rw();
- }
- static int map_prop_area_rw()
- {
- /* dev is a tmpfs that we can use to carve a shared workspace
- * out of, so let's do that...
- */
- const int fd = open(property_filename,//文件/dev/__properties__,应该是匿名映射,没有实际文件
- O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444);
- if (fd < 0) {
- if (errno == EACCES) {
- /* for consistency with the case where the process has already
- * mapped the page in and segfaults when trying to write to it
- */
- abort();
- }
- return -1;
- }
- if (ftruncate(fd, PA_SIZE) < 0) {
- close(fd);
- return -1;
- }
- pa_size = PA_SIZE;
- pa_data_size = pa_size - sizeof(prop_area);
- compat_mode = false;
- void *const memory_area = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);//内存映射
- if (memory_area == MAP_FAILED) {
- close(fd);
- return -1;
- }
- prop_area *pa = new(memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION);
- /* plug into the lib property services */
- __system_property_area__ = pa;
- close(fd);
- return 0;
- }
共享内存使用名称为如下的设备文件创建。
- #define PROP_FILENAME "/dev/__properties__"
3.3 init进程处理属性
在init进程中的主函数中:在解析init.rc之前,先调用了start_property_service函数
- property_load_amt_defaults(amt_mode);
- property_load_boot_defaults();
- start_property_service();
- init_parse_config_file("/init.rc");
start_property_service函数创建了socket,然后监听,并且调用register_epoll_handler函数把socket的fd放入了epoll中
- void start_property_service() {
- property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
- 0666, 0, 0, NULL);
- if (property_set_fd == -1) {
- ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
- exit(1);
- }
- listen(property_set_fd, 8);
- register_epoll_handler(property_set_fd, handle_property_set_fd);
- }
register_epoll_handler函数就是把fd放入epoll中
- void register_epoll_handler(int fd, void (*fn)()) {
- epoll_event ev;
- ev.events = EPOLLIN;
- ev.data.ptr = reinterpret_cast<void*>(fn);
- if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &ev) == -1) {
- ERROR("epoll_ctl failed: %s\n", strerror(errno));
- }
- }
先我们就来解析下handle_property_set_fd函数
- static void handle_property_set_fd()
- {
- prop_msg msg;
- int s;
- int r;
- struct ucred cr;
- struct sockaddr_un addr;
- socklen_t addr_size = sizeof(addr);
- socklen_t cr_size = sizeof(cr);
- char * source_ctx = NULL;
- struct pollfd ufds[1];
- const int timeout_ms = 2 * 1000; /* Default 2 sec timeout for caller to send property. */
- int nr;
- if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {//获取对端socket的fd
- return;
- }
- /* Check socket options here */
- if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
- close(s);
- ERROR("Unable to receive socket options\n");
- return;
- }
- ufds[0].fd = s;
- ufds[0].events = POLLIN;
- ufds[0].revents = 0;
- nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms));
- if (nr == 0) {
- ERROR("sys_prop: timeout waiting for uid=%d to send property message.\n", cr.uid);
- close(s);
- return;
- } else if (nr < 0) {
- ERROR("sys_prop: error waiting for uid=%d to send property message: %s\n", cr.uid, strerror(errno));
- close(s);
- return;
- }
- r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));//获取socket数据
- if(r != sizeof(prop_msg)) {
- ERROR("sys_prop: mis-match msg size received: %d expected: %zu: %s\n",
- r, sizeof(prop_msg), strerror(errno));
- close(s);
- return;
- }
- switch(msg.cmd) {
- case PROP_MSG_SETPROP:
- msg.name[PROP_NAME_MAX-1] = 0;
- msg.value[PROP_VALUE_MAX-1] = 0;
- if (!is_legal_property_name(msg.name, strlen(msg.name))) {
- ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name);
- close(s);
- return;
- }
- getpeercon(s, &source_ctx);
- if(memcmp(msg.name,"ctl.",4) == 0) {//ctl类型
- // Keep the old close-socket-early behavior when handling
- // ctl.* properties.
- close(s);
- if (check_control_mac_perms(msg.value, 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, 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);
- }
- // Note: bionic's property client code assumes that the
- // property server will not close the socket until *AFTER*
- // the property is written to memory.
- close(s);
- }
- freecon(source_ctx);
- break;
- default:
- close(s);
- break;
- }
- }
我们先来看看处理ctl类型的属性,ctl.start ctl.stop ctl.restart.
- 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);
- }
- }
最后调用了下面这三个函数来处理这些命令。总的是先看看有没有这个service,然后调用start stop 或者是restart相关函数。
- static void msg_start(const char *name)
- {
- struct service *svc = NULL;
- char *tmp = NULL;
- char *args = NULL;
- if (!strchr(name, ':'))
- svc = service_find_by_name(name);
- else {
- tmp = strdup(name);
- if (tmp) {
- args = strchr(tmp, ':');
- *args = '\0';
- args++;
- svc = service_find_by_name(tmp);
- }
- }
- if (svc) {
- service_start(svc, args);
- } else {
- ERROR("no such service '%s'\n", name);
- }
- if (tmp)
- free(tmp);
- }
- static void msg_stop(const char *name)
- {
- struct service *svc = service_find_by_name(name);
- if (svc) {
- service_stop(svc);
- } else {
- ERROR("no such service '%s'\n", name);
- }
- }
- static void msg_restart(const char *name)
- {
- struct service *svc = service_find_by_name(name);
- if (svc) {
- service_restart(svc);
- } else {
- ERROR("no such service '%s'\n", name);
- }
- }
下面我们再来看property_set函数调用了property_set_impl函数来设置属性
- int property_set(const char* name, const char* value) {
- int rc = property_set_impl(name, value);
- if (rc == -1) {
- ERROR("property_set(\"%s\", \"%s\") failed\n", name, value);
- }
- return rc;
- }
- static int property_set_impl(const char* name, const char* value) {
- size_t namelen = strlen(name);
- size_t valuelen = strlen(value);
- if (!is_legal_property_name(name, namelen)) return -1;
- if (valuelen >= PROP_VALUE_MAX) return -1;
- if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) {
- if (selinux_reload_policy() != 0) {
- ERROR("Failed to reload policy\n");
- }
- } else if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
- if (restorecon_recursive(value) != 0) {
- ERROR("Failed to restorecon_recursive %s\n", value);
- }
- }
- prop_info* pi = (prop_info*) __system_property_find(name);
- if(pi != 0) {
- /* ro.* properties may NEVER be modified once set */
- if(!strncmp(name, "ro.", 3)) return -1;//ro文件,直接退出
- __system_property_update(pi, value, valuelen);//更新属性数据到共享内存
- } else {
- int rc = __system_property_add(name, namelen, value, valuelen);//增加属性
- if (rc < 0) {
- return rc;
- }
- }
- /* 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);//net类型的属性,改变后需要写属性到net.change
- } 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);//persist类型的属性写入到data/property目录下以属性名命名的文件
- }
- property_changed(name, value);
- return 0;
- }
我们先看下write_persistent_property函数,将属性在data/property目录下创建以属性名命名的文件,然后写入属性值。写入方式是先做了一个临时文件,成功后改名。
- static void write_persistent_property(const char *name, const char *value)
- {
- char tempPath[PATH_MAX];
- char path[PATH_MAX];
- int fd;
- snprintf(tempPath, sizeof(tempPath), "%s/.temp.XXXXXX", PERSISTENT_PROPERTY_DIR);
- fd = mkstemp(tempPath);//做临时文件
- if (fd < 0) {
- ERROR("Unable to write persistent property to temp file %s: %s\n", tempPath, strerror(errno));
- return;
- }
- write(fd, value, strlen(value));//写入数据
- fsync(fd);
- close(fd);
- snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name);
- if (rename(tempPath, path)) {//改名
- unlink(tempPath);
- ERROR("Unable to rename persistent property file %s to %s\n", tempPath, path);
- }
- }
property_changed函数就是看有哪些满足属性的触发器,然后放入执行队列中。最后在init的循环中,执行触发器相应的命令
- void property_changed(const char *name, const char *value)
- {
- if (property_triggers_enabled)
- queue_property_triggers(name, value);
- }
- void queue_property_triggers(const char *name, const char *value)
- {
- struct listnode *node, *node2;
- struct action *act;
- struct trigger *cur_trigger;
- bool match;
- int name_length;
- list_for_each(node, &action_list) {
- act = node_to_item(node, struct action, alist);
- match = !name;
- list_for_each(node2, &act->triggers) {
- cur_trigger = node_to_item(node2, struct trigger, nlist);
- if (!strncmp(cur_trigger->name, "property:", strlen("property:"))) {
- const char *test = cur_trigger->name + strlen("property:");
- if (!match) {
- name_length = strlen(name);
- if (!strncmp(name, test, name_length) &&
- test[name_length] == '=' &&
- (!strcmp(test + name_length + 1, value) ||
- !strcmp(test + name_length + 1, "*"))) {
- match = true;
- continue;
- }
- }
- const char* equals = strchr(test, '=');
- if (equals) {
- char prop_name[PROP_NAME_MAX + 1];
- char value[PROP_VALUE_MAX];
- int length = equals - test;
- if (length <= PROP_NAME_MAX) {
- int ret;
- memcpy(prop_name, test, length);
- prop_name[length] = 0;
- /* does the property exist, and match the trigger value? */
- ret = property_get(prop_name, value);
- if (ret > 0 && (!strcmp(equals + 1, value) ||
- !strcmp(equals + 1, "*"))) {
- continue;
- }
- }
- }
- }
- match = false;
- break;
- }
- if (match) {
- action_add_queue_tail(act);//最后将满足的触发器加入执行队列中
- }
- }
- }
3.3 读取文件中属性
我们先来看init.rc中的下面触发器
3.3.1 /system/build.prop
- on load_system_props_action
- load_system_props
而load_system_props_action是在late-init中触发的
- on late-init
- trigger early-fs
- trigger fs
- trigger post-fs
- # Load properties from /system/ + /factory after fs mount. Place
- # this in another action so that the load will be scheduled after the prior
- # issued fs triggers have completed.
- trigger load_system_props_action
我们再来看load_system_props的处理,其中PROP_PATH_SYSTEM_BUILD就是/system/build.prop文件,load_properties_from_file函数最后会调用property_set函数设置属性
- void load_system_props() {
- load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL);
- load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL);
- load_properties_from_file(PROP_PATH_FACTORY, "ro.*");
- load_recovery_id_prop();
- }
3.3.2 /data/local.prop /data/property
同样persist类型的属性如下:
- on load_persist_props_action
- load_persist_props
- start logd
- start logd-reinit
也是在late-init触发,最后调用load_persist_props
- on late-init
- trigger early-fs
- trigger fs
- trigger post-fs
- # Load properties from /system/ + /factory after fs mount. Place
- # this in another action so that the load will be scheduled after the prior
- # issued fs triggers have completed.
- trigger load_system_props_action
- # Now we can mount /data. File encryption requires keymaster to decrypt
- # /data, which in turn can only be loaded when system properties are present
- trigger post-fs-data
- trigger load_persist_props_action
load_persist_props函数调用了load_override_properties load_persistent_properties来去读属性值
- void load_persist_props(void) {
- load_override_properties();
- /* Read persistent properties after all default values have been loaded. */
- load_persistent_properties();//读取data/property/下面persist类型的属性
- }
- static void load_override_properties() {
- if (ALLOW_LOCAL_PROP_OVERRIDE) {
- char debuggable[PROP_VALUE_MAX];
- int ret = property_get("ro.debuggable", debuggable);
- if (ret && (strcmp(debuggable, "1") == 0)) {
- load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE, NULL);
- }
- }
- }