Android System Properties
来源:互联网 发布:网络信息安全保护小组 编辑:程序博客网 时间:2024/05/02 05:07
学习System Properties
根据5.1的系统源码细节稍有不同。
转自:http://stevevallay.github.io/blog/2013/12/23/android-system-properties2/
system/core/init/init.c
property_init();
system/core/init/property_service.c
void property_init(void){ init_property_area();}
static int init_property_area(void){ if (property_area_inited) return -1; if(__system_property_area_init())//关键 return -1;//初始化workspace,得到shared memory的fd,size,data if(init_workspace(&pa_workspace, 0)) return -1; fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC); property_area_inited = 1; return 0;}
bionic/libc/bionic/system_properties.cpp
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, //static char property_filename[PATH_MAX] = PROP_FILENAME; //在头文件中#define PROP_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; } // TODO: Is this really required ? Does android run on any kernels that // don't support O_CLOEXEC ? const int ret = fcntl(fd, F_SETFD, FD_CLOEXEC);//linux里面的函数 if (ret < 0) { close(fd); 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);//用mmap映射到内存 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;//通过该变量共享,完成property_service和libc中properties相关的交互 close(fd); return 0;}
继续到init.c
init.c
INFO("property init\n"); property_load_boot_defaults();
property_service.c:
void property_load_boot_defaults(void){ load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);}
/* * Filter is used to decide which properties to load: NULL loads all keys, * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match. */static void load_properties_from_file(const char *fn, const char *filter){ char *data; unsigned sz; data = read_file(fn, &sz); if(data != 0) { load_properties(data, filter); free(data); }}
/* * Filter is used to decide which properties to load: NULL loads all keys, * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match. */static void load_properties(char *data, const char *filter){ char *key, *value, *eol, *sol, *tmp, *fn; size_t flen = 0; if (filter) { flen = strlen(filter); } sol = data; while ((eol = strchr(sol, '\n'))) { key = sol; *eol++ = 0; sol = eol; while (isspace(*key)) key++; if (*key == '#') continue; tmp = eol - 2; while ((tmp > key) && isspace(*tmp)) *tmp-- = 0; if (!strncmp(key, "import ", 7) && flen == 0) { fn = key + 7; while (isspace(*fn)) fn++; key = strchr(fn, ' '); if (key) { *key++ = 0; while (isspace(*key)) key++; } load_properties_from_file(fn, key); } else { value = strchr(key, '='); if (!value) continue; *value++ = 0; tmp = value - 2; while ((tmp > key) && isspace(*tmp)) *tmp-- = 0; while (isspace(*value)) value++; if (flen > 0) { if (filter[flen - 1] == '*') { if (strncmp(key, filter, flen - 1)) continue; } else { if (strcmp(key, filter)) continue; } } property_set(key, value);//初始化property } }}
init.c 启动property service
queue_builtin_action(property_service_init_action, "property_service_init");
static int property_service_init_action(int nargs, char **args){ /* read any property files on system or data and * fire up the property service. This must happen * after the ro.foo properties are set above so * that /data/local.prop cannot interfere with them. */ start_property_service(); if (get_property_set_fd() < 0) { ERROR("start_property_service() failed\n"); exit(1); } return 0;}
property_service.c:
void start_property_service(void){ int fd; fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0, NULL);//创建socket来接受set property的消息 if(fd < 0) return; fcntl(fd, F_SETFD, FD_CLOEXEC); fcntl(fd, F_SETFL, O_NONBLOCK); listen(fd, 8); property_set_fd = fd;}
init.c
init进程最后,进入一个无限循环,等待/dev/socket/property_service和其他fd事件,并处理
for(;;) { int nr, i, timeout = -1; execute_one_command(); restart_processes(); 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 (!signal_fd_init && get_signal_fd() > 0) { ufds[fd_count].fd = get_signal_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; fd_count++; signal_fd_init = 1; } if (!keychord_fd_init && get_keychord_fd() > 0) { ufds[fd_count].fd = get_keychord_fd(); ufds[fd_count].events = POLLIN; ufds[fd_count].revents = 0; fd_count++; keychord_fd_init = 1; } if (process_needs_restart) { timeout = (process_needs_restart - gettime()) * 1000; if (timeout < 0) timeout = 0; } if (!action_queue_empty() || cur_action) timeout = 0;#if BOOTCHART if (bootchart_count > 0) { if (timeout < 0 || timeout > BOOTCHART_POLLING_MS) timeout = BOOTCHART_POLLING_MS; if (bootchart_step() < 0 || --bootchart_count == 0) { bootchart_finish(); bootchart_count = 0; } }#endif nr = poll(ufds, fd_count, timeout); if (nr <= 0) continue; for (i = 0; i < fd_count; i++) { if (ufds[i].revents & POLLIN) { if (ufds[i].fd == get_property_set_fd()) handle_property_set_fd(); //接受/dev/socket/property_service的消息并处理 else if (ufds[i].fd == get_keychord_fd()) handle_keychord(); else if (ufds[i].fd == get_signal_fd()) handle_signal(); } } } return 0;}
property_service.c
void handle_property_set_fd(){ prop_msg msg; int s; int r; int res; 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) { 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. err=%d %s\n", cr.uid, errno, strerror(errno)); close(s); return; } r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT)); if(r != sizeof(prop_msg)) { ERROR("sys_prop: mis-match msg size received: %d expected: %zu errno: %d\n", r, sizeof(prop_msg), errno); close(s); return; } switch(msg.cmd) { case PROP_MSG_SETPROP: //处理set proper请求 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) { // 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); //如果有permission,则设置property } 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; }}
check_perms不同的property_set需要不同的权限
/* 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 }, { "net.qmi", AID_RADIO, 0 }, { "net.lte", AID_RADIO, 0 }, { "net.cdma", AID_RADIO, 0 }, { "ril.", AID_RADIO, 0 }, { "gsm.", AID_RADIO, 0 }, { "persist.radio", AID_RADIO, 0 }, { "net.dns", AID_RADIO, 0 }, { "sys.usb.config", AID_RADIO, 0 }, { "net.", AID_SYSTEM, 0 }, { "dev.", AID_SYSTEM, 0 }, { "runtime.", AID_SYSTEM, 0 }, { "hw.", AID_SYSTEM, 0 }, { "sys.", AID_SYSTEM, 0 }, { "sys.powerctl", AID_SHELL, 0 }, { "service.", AID_SYSTEM, 0 }, { "wlan.", AID_SYSTEM, 0 }, { "gps.", AID_GPS, 0 }, { "bluetooth.", AID_BLUETOOTH, 0 }, { "dhcp.", AID_SYSTEM, 0 }, { "dhcp.", AID_DHCP, 0 }, { "debug.", AID_SYSTEM, 0 }, { "debug.", AID_SHELL, 0 }, { "log.", AID_SHELL, 0 }, { "service.adb.root", AID_SHELL, 0 }, { "service.adb.tcp.port", AID_SHELL, 0 }, { "persist.logd.size",AID_SYSTEM, 0 }, { "persist.sys.", AID_SYSTEM, 0 }, { "persist.service.", AID_SYSTEM, 0 }, { "persist.security.", AID_SYSTEM, 0 }, { "persist.gps.", AID_GPS, 0 }, { "persist.service.bdroid.", AID_BLUETOOTH, 0 }, { "selinux." , AID_SYSTEM, 0 }, { "wc_transport.", AID_BLUETOOTH, AID_SYSTEM }, { "build.fingerprint", AID_SYSTEM, 0 }, { "partition." , AID_SYSTEM, 0}, { NULL, 0, 0 }};
property_set:
int property_set(const char *name, const char *value){ prop_info *pi; int ret; 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; pi = (prop_info*) __system_property_find(name); if(pi != 0) {//如果有个这个property /* ro.* properties may NEVER be modified once set */ if(!strncmp(name, "ro.", 3)) return -1; __system_property_update(pi, value, valuelen); } else { ret = __system_property_add(name, namelen, value, valuelen); if (ret < 0) { ERROR("Failed to set '%s'='%s'\n", name, value); return ret; } } /* 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) {//如果是persist.开头的文件,写入到/data/property/*** /* * 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); } else if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) { selinux_reload_policy(); } property_changed(name, value); return 0;}
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 errno: %d\n", tempPath, 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); }}
下面看看proper_get 直接向下到libc的__system_property_get的api
property_service.c
int __property_get(const char *name, char *value){ return __system_property_get(name, value);}
bionic/libc/bionic/system_properties.cpp
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; }}
调用__system_property_find
const prop_info *__system_property_find(const char *name){ if (__predict_false(compat_mode)) { return __system_property_find_compat(name); } return find_property(root_node(), name, strlen(name), NULL, 0, false);}
其中root_node()
static prop_bt *root_node(){ return reinterpret_cast<prop_bt*>(to_prop_obj(0));}
static void *to_prop_obj(const uint32_t off){ if (off > pa_data_size) return NULL; if (!__system_property_area__) return NULL; return (__system_property_area__->data + off);}
实际上获取__system_property_area__->data 的地址开始访问。__system_property_area__是/dev/properties map到内存的地址
在开始时就分析了,init.c进程初始化时,调用proper_service.c的proper_init(),接着进入system_properties.cpp的__system_property_area_(),再进入map_prop_area_rw(),创建property_filename(/dev/_properties_),mmap到内存,将地址赋值给__system_property_area__
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, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444); //...... void *const memory_area = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); //...... 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;}
其他进程调用__system_property_find时不可以直接访问init进程里面的__system_property_area__,他们不能共享这个地址,不同进程之间各自使用自己独享的内存空间。
其他进程也对__system_property_area__初始化了一下。
- Android/System Properties
- Android system properties
- 关于 Android System Properties
- Android System Properties
- Android System Properties
- Android System Properties Dynamic
- system properties
- System.properties
- system properties和environment properties
- Java System Properties
- Java System Properties
- Java System Properties
- java的System Properties
- MongoDB System Properties
- Shuriken Particle System Properties
- System.Properties的属性
- Basic System Properties
- System.Properties和System.getenv区别
- Linux学习进阶路线图
- C++第2次实验—标准体重计算
- HDOJ 2003 求绝对值
- 一道jAvA静态的面试题想到的
- 常见Bug
- Android System Properties
- 中介者模式
- 2. 命令行打包
- Android Service完全解析,关于服务你所需知道的一切(上)
- 使用squid实现普通的代理服务器
- LeetCode-max-points-on-a-line
- CentOs6.6 安装PHPAdmin
- TCP协议连接建立与连接断开过程(含断开时的TCP状态图)
- python