属性变更的请求时init事件循环处理的另一个事件,在Android平台中,为了让运行中的所有进程共享系统运行时所需要的各种设置值,系统开辟了属性存储区域,并提供了访问该区域的API。属性由键(key)与值(value)构成,其表现形式为“键=值”。在Linux系统中,属性服务主要用来设置环境变量,提供各进程访问设定的环境变量值。在Android平台中,在访问属性值时,添加了访问权限控制,增强了访问的安全性。系统中所有运行中的进程都可以访问属性值,但仅有init进程才能修改属性值。其他进程修改属性值时,必须向init进程提出请求,最终由init进程负责修改属性值。在此过程中,init进程会先检查各属性的访问权限,而后再修改属性值,当属性值更改后,若定义在init.rc文件中的某个特定条件得到满足,则与此条件相匹配的动作就会发生,每个动作都有一个触发器,决定动作的执行时间,记录在“on property”关键字后的命令即被执行。
属性设计框架
属性系统是Android的一个重要特性。它作为一个服务运行,管理系统配置和状态。每个属性是一个键值对(key/value pair),其类型都是字符串。属性被大量使用在Android系统中,用来记录系统设置或进程之间的信息交换。属性是在整个系统中全局可见的。每个进程可以get/set属性。
在系统初始化时,Android将分配一个共享内存区来存储的属性。这些是由“init”守护进程完成的,其源代码位于:device/system/init。“init”守护进程将启动一个属性服务。属性服务在“init”守护进程中运行。每一个客户端想要设置属性时,必须连接属性服务,再向其发送信息。属性服务将会在共享内存区中修改和创建属性。任何客户端想获得属性信息,可以从共享内存直接读取。这提高了读取性能。
属性系统的上层架构如下图所示
图中有3个进程、一组永久属性文件和一块共享内存区域。共享内存区域是所有属性记录的存储所在。只有property服务进程才可以写入共享内存区域,它负责从永久文件中加载属性记录并将它们保存在共享内存中。
consumer进程将共享内存加载到其自身的虚拟地址空间并直接访问这些属性。setter进程同样将共享内存加载到其自身的虚拟地址空间,但其不能直接写该内存。当setter试图增加或者更新一个属性时,它将该属性通过unix domain socket发送至property服务。property服务代表setter进程将该属性写入共享内存和永久文件中。
property服务运行于init进程中。init进程首先创建一个共享内存区域,并保存一个指向该区域的描述符fd。init进程将该区域通过使用了MAP_SHARED标志的mmap映射至它自身的虚拟地址空间,这样,任何对于该区域的更新对于所有进程都是可见的。fd和区域大小被存储在一个名为ANDROID_PROPERTY_WORKSPACE的变量中。任何其他进程,比如consumer和setter将使用这个变量来获得fd和尺寸,这样它们就能mmap这个区域到它们自身的虚拟地址空间中。该共享内存区域如下图所示。
当启动属性服务时,将从以下文件中加载默认属性:
/ default.prop
/system/build.prop
/system/default.prop
/data/local.prop
属性将会以上述顺序加载。后加载的属性将覆盖原先的值。
启动property服务。在这一步中,一个unix domain socket服务被创建。此socket的路径是/dev/socket/property_service,该路径对于其他客户端进程是熟知的。最后,init进程调用poll来等待该socket上的连接事件。在consumer一边,当它初始化libc(bionic/libc/bionic/libc_common.c __libc_init_common 函数),它将从环境变量中返回fd和尺寸,并映射共享内存到其自身的地址空间(bionic/libc/bionic/system_properties.c __system_properties_init 函数)。在这之后,libcutils可以想读取普通内存那样为consumer读取属性。目前,属性是不能够被删除的。也就是说,一旦添加了一个属性,它将不能够被删除,其键也不能够被改变。
几种特殊的属性:
1.ro.属性,它表示只读属性,它一旦被设置就不能被修改;
2.net.属性,顾名思义,就是与网络相关的属性,net.属性中有一个特殊的属性:net.change,它记录了每一次最新设置和更新的net.属性,也就是每次设置和更新net.属性时则会自动的更新net.change属性,net.change属性的value就是这个被设置或者更新的net属性的name。例如我们更新了属性net.bt.name的值,由于net有属性发生了变化,那么属性服务就会自动更新net.change,将其值设置为net.bt.name。
3.persist.属性,以文件的形式保存在/data/property路径下。persist.属性由于将其保存在了用户空间中,所以在property_init中是不能对其更新的,只能将其更新过程交给用户来处理。
4.ctl.属性,虽然是以属性的形式来进行设置,其实它的目的是为了启动或关闭它指定的service
属性“ ctrl.start ”和“ ctrl.stop ”是用来启动和停止服务。每一项服务必须在/init.rc中定义.系统启动时,init守护进程将解析init.rc和启动属性服务。一旦收到设置“ ctrl.start ”属性的请求,属性服务将使用该属性值作为服务名找到该服务,启动该服务。这项服务的启动结果将会放入“ init.svc.<服务名>“属性中 。客户端应用程序可以轮询那个属性值,以确定结果。
如何读取/设置属性
1、 native code
当编写本地应用程序时,可以使用property_get和property_set 这两个API来读取/设置属性。要使用它们,我们需要include cutils/properties.h,并链接libcutils库。
int property_get(const char *key, char *value, const char *default_value);
int property_set(const char *key, const char *value);
默认情况下,设置属性只会使"init"守护程序写入共享内存,它不会执行任何脚本或二进制程序。但是,您可以将您的想要的实现的操作与init.rc中某个属性的变化相关联.例如,在默认的init.rc中有:
# adbd on at boot in emulator
on property:ro.kernel.qemu=1
start adbd
on property:persist.service.adb.enable=1
start adbd
on property:persist.service.adb.enable=0
stop adbd
这样,如果你设置persist.service.adb.enable为1 ,"init"守护程序就知道需要采取行动:开启adbd服务。
2、 java code
在Java包中提供有System.getProperty和System.setProperty方法。但值得注意的是,尽管这两个API在语义上等同native函数,但其将数据存储于完全不同的位置。实际上,dalvik VM使用一个哈希表来存储这些属性。所以,用这两个API存储的属性是独立的,不能存取native属性,反之亦然。
然而Android有一个内部隐藏类(@hide,对SDK不可见)android.os.SystemProperties来操纵native属性。其通过jni来存取native属性库。
3、 shell脚本
Android toolbox程序提供了两个工具: setprop和getprop获取和设置属性。其使用方法:
getprop <属性名>
setprop <属性名><<属性值>
可以通过命令adb shell :
getprop查看手机上所有属性状态值。
或者 getprop init.svc.bootanim制定查看某个属性状态
使用setprop init.svc.bootanim start 设置某个属性的状态
property_service.c:
1)PA_COUNT_MAX指定了系统(共享内存区域中)最多能存储多少个属性。
2)PROP_NAME_MAX指定了一个属性的key最大允许长度;
3)PROP_VALUE_MAX则指定了value的最大允许长度。
属性服务启动过程
在 android之Init进程启动过程源码分析一文中介绍了整个Init进程的启动流程,init进程的主要任务之一就是启动Android系统的属性服务,接下来将详细介绍属性服务的启动流程:
- int main(int argc, char **argv)
-
- .............
-
- property_init();
- .............
- is_charger = !strcmp(bootmode, "charger");
-
- INFO("property init\n");
- if (!is_charger)
- property_load_boot_defaults();
-
- queue_builtin_action(property_service_init_action, "property_service_init");
-
- queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
- 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;
- }
- .................
-
- 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();
- .............
- }
- }
- }
- return 0;
Android 属性系统的整个流程:
1)初始化属性存储空间
property_init()函数用来初始化属性域,首先在内存中开辟一块共享区域,而后将其用着匿名共享内存,外部进程可以访问这块共享内存域,获取属性值,但它们不能通过直接访问共享内存域的方式来更改属性值。一个进程若想更改属性值,必须先向init进程提交属性变更请求,由init进程更改共享内存中的属性值。
- void property_init(void)
- {
- init_property_area();
- }
-
- static int init_property_area(void)
- {
- prop_area *pa;
- if(pa_info_array)
- return -1;
-
- if(init_workspace(&pa_workspace, PA_SIZE))
- return -1;
-
- fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);
-
- pa_info_array = (void*) (((char*) pa_workspace.data) + PA_INFO_START);
-
- pa = pa_workspace.data;
-
- memset(pa, 0, PA_SIZE);
-
- pa->magic = PROP_AREA_MAGIC;
-
- pa->version = PROP_AREA_VERSION;
-
- __system_property_area__ = pa;
- property_area_inited = 1;
- return 0;
- }
属性共享内存区初始化
- static int init_workspace(workspace *w, size_t size)
- {
- void *data;
- int fd;
-
-
-
-
- fd = open("/dev/__properties__", O_RDWR | O_CREAT, 0600);
- if (fd < 0)
- return -1;
-
- if (ftruncate(fd, size) < 0)
- goto out;
-
- data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if(data == MAP_FAILED)
- goto out;
- close(fd);
- fd = open("/dev/__properties__", O_RDONLY);
- if (fd < 0)
- return -1;
-
- unlink("/dev/__properties__");
- w->data = data;
- w->size = size;
- w->fd = fd;
- return 0;
-
- out:
- close(fd);
- return -1;
- }
属性域的起始1024字节作为属性域头,用来保存管理属性表所需要的一些值,其余的31616个字节空间被划分成247块,每块大小为128字节,用于保存属性值。在属性域完成初始化之后,就会从指定的文件中读取初始化值,并设置属性值。
属性系统的存储空间分配图如下:
2)初始化系统属性
- queue_builtin_action(property_service_init_action, "property_service_init");
- static int property_service_init_action(int nargs, char **args)
- {
-
- start_property_service();
- return 0;
- }
start_property_service()函数在创建套接字之前,先读取存储在各个属性文件中的属性,并设置属性值。当所有系统初始属性值设置完毕后,load_persistent_properties()函数开始读取保存在/data/property目录下的属性值。该目录中保存着系统运行中其他进程新生成的属性值或更改的属性值,属性的key用作文件名,value保存在文件中。在属性初始值设置完毕后,就会创建名为/dev/socket/property_service的套接字。
- void start_property_service(void)
- {
- int fd;
-
- load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
-
- load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);
- #ifdef ALLOW_LOCAL_PROP_OVERRIDE
-
- load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE);
- #endif
-
- load_persistent_properties();
-
- fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);
- if(fd < 0) return;
-
- fcntl(fd, F_SETFD, FD_CLOEXEC);
- fcntl(fd, F_SETFL, O_NONBLOCK);
-
- listen(fd, 8);
-
- property_set_fd = fd;
- }
初始化系统属性的工作主要是从属性文件中读取系统属性到属性存储空间,因为属性存储空间是一块匿名共享内存,所有的进程都可以访问,这样就加快了属性的读取速度;同时创建并启动属性服务端socket;从属性文件中读取系统属性的实现如下:
- static void load_properties_from_file(const char *fn)
- {
- char *data;
- unsigned sz;
-
- data = read_file(fn, &sz);
- if(data != 0) {
-
- load_properties(data);
- free(data);
- }
- }
属性文件的部分内容如下:
- ro.com.android.dataroaming=false
- persist.msms.phone_count=2
- persist.blcr.enable=0
- persist.msms.phone_default=0
- dalvik.vm.heapstartsize=5m
- dalvik.vm.heapgrowthlimit=64m
属性文件内容解析:
- static void load_properties(char *data)
- {
- char *key, *value, *eol, *sol, *tmp;
- sol = data;
-
- while((eol = strchr(sol, '\n'))) {
- key = sol;
- *eol++ = 0;
- sol = eol;
-
- value = strchr(key, '=');
- if(value == 0) continue;
- *value++ = 0;
-
- while(isspace(*key)) key++;
- if(*key == '#') continue;
- tmp = value - 2;
- while((tmp > key) && isspace(*tmp)) *tmp-- = 0;
-
- while(isspace(*value)) value++;
- tmp = eol - 2;
- while((tmp > value) && isspace(*tmp)) *tmp-- = 0;
-
- property_set(key, value);
- }
- }
最后调用property_set函数来设置解析得到的系统属性,属性的设置过程在接下来将进行详细分析!
3)属性服务端设置属性
在前面的属性系统的设计框架中介绍了属性的正真设置是在Init进程进行的,通过属性服务来设置相关系统属性,或者控制相关进程的运行状态,如下图所示:
Init进程通过poll系统调用来监控ufds句柄池中的事件,当某一进程需要设置某一属性时,将通过socket向Init进程发起请求,请求过程在接下来分析,当Init接收到请求时,poll系统调用返回,并根据传过来的系统属性及属性值设置相应的属性,设置过程如下:
- 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);
-
- if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {
- return;
- }
-
- if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
- close(s);
- ERROR("Unable to recieve socket options\n");
- return;
- }
-
- r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), 0));
- if(r != sizeof(prop_msg)) {
- ERROR("sys_prop: mis-match msg size recieved: %d expected: %d errno: %d\n",r, sizeof(prop_msg), 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(memcmp(msg.name,"ctl.",4) == 0) {
-
- close(s);
-
- if (check_control_perms(msg.value, cr.uid, cr.gid)) {
-
- 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, cr.uid, cr.gid)) {
-
- property_set((char*) msg.name, (char*) msg.value);
- } else {
- ERROR("sys_prop: permission denied uid:%d name:%s\n",cr.uid, msg.name);
- }
-
- close(s);
- }
- break;
- default:
-
- close(s);
- break;
- }
- }
1.服务状态控制属性设置
以ctl开头的属性是服务运行状态控制属性,对于这类属性,根据ctl后面的start或stop来控制服务进程的启动与停止,在启动或停止服务进程前首先会检查control_perms数组中的服务是否具有相应权限:
- static int check_control_perms(const char *name, unsigned int uid, unsigned int gid) {
- int i;
-
- if (uid == AID_SYSTEM || uid == AID_ROOT)
- return 1;
-
-
- for (i = 0; control_perms[i].service; i++) {
- if (strcmp(control_perms[i].service, name) == 0) {
- if ((uid && control_perms[i].uid == uid) ||
- (gid && control_perms[i].gid == gid)) {
- return 1;
- }
- }
- }
- return 0;
- }
需要指定权限的服务存放在数组control_perms中:
- control_perms[] = {
- { "dumpstate",AID_SHELL, AID_LOG },
- { "ril-daemon",AID_RADIO, AID_RADIO },
- {NULL, 0, 0 }
- };
如果想要应用有权限启动/关闭某Native Service:需要具有system/root权限
比如要停止zygote进程,可以使用property_set("ctl.stop", "zygote")来实现,进程运行状态是通过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_stop(arg);
- msg_start(arg);
- } else {
- ERROR("unknown control msg '%s'\n", msg);
- }
- }
对于进程的启动,使用msg_start来完成:
- static void msg_start(const char *name)
- {
- struct service *svc;
- char *tmp = NULL;
- char *args = NULL;
-
- if (!strchr(name, ':'))
-
- svc = service_find_by_name(name);
- else {
- tmp = strdup(name);
- 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);
- }
首先根据服务名称从service_list链表中查找对应的service,然后通过调用service_start函数来启动:
- void service_start(struct service *svc, const char *dynamic_args)
- {
- struct stat s;
- pid_t pid;
- int needs_console;
- int n;
-
-
-
-
- svc->flags &= (~(SVC_DISABLED|SVC_RESTARTING|SVC_RESET));
- svc->time_started = 0;
-
-
-
-
-
-
- if (svc->flags & SVC_RUNNING) {
- return;
- }
- needs_console = (svc->flags & SVC_CONSOLE) ? 1 : 0;
- if (needs_console && (!have_console)) {
- ERROR("service '%s' requires console\n", svc->name);
- svc->flags |= SVC_DISABLED;
- return;
- }
- if (stat(svc->args[0], &s) != 0) {
- ERROR("cannot find '%s', disabling '%s'\n", svc->args[0], svc->name);
- svc->flags |= SVC_DISABLED;
- return;
- }
- if ((!(svc->flags & SVC_ONESHOT)) && dynamic_args) {
- ERROR("service '%s' must be one-shot to use dynamic args, disabling\n",
- svc->args[0]);
- svc->flags |= SVC_DISABLED;
- return;
- }
- NOTICE("starting '%s'\n", svc->name);
-
- pid = fork();
-
- if (pid == 0) {
- struct socketinfo *si;
- struct svcenvinfo *ei;
- char tmp[32];
- int fd, sz;
- umask(077);
-
- if (properties_inited()) {
-
- get_property_workspace(&fd, &sz);
-
- sprintf(tmp, "%d,%d", dup(fd), sz);
- add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
- }
-
- for (ei = svc->envvars; ei; ei = ei->next)
- add_environment(ei->name, ei->value);
-
- for (si = svc->sockets; si; si = si->next) {
- int socket_type = (!strcmp(si->type, "stream") ? SOCK_STREAM :(!strcmp(si->type, "dgram") ? SOCK_DGRAM : SOCK_SEQPACKET));
-
- int s = create_socket(si->name, socket_type,si->perm, si->uid, si->gid);
-
- if (s >= 0) {
- publish_socket(si->name, s);
- }
- }
- if (svc->ioprio_class != IoSchedClass_NONE) {
- if (android_set_ioprio(getpid(), svc->ioprio_class, svc->ioprio_pri)) {
- ERROR("Failed to set pid %d ioprio = %d,%d: %s\n",getpid(), svc->ioprio_class, svc->ioprio_pri, strerror(errno));
- }
- }
-
- if (needs_console) {
- setsid();
- open_console();
- } else {
- zap_stdio();
- }
- setpgid(0, getpid());
-
-
- if (svc->gid) {
- if (setgid(svc->gid) != 0) {
- ERROR("setgid failed: %s\n", strerror(errno));
- _exit(127);
- }
- }
- if (svc->nr_supp_gids) {
- if (setgroups(svc->nr_supp_gids, svc->supp_gids) != 0) {
- ERROR("setgroups failed: %s\n", strerror(errno));
- _exit(127);
- }
- }
- if (svc->uid) {
- if (setuid(svc->uid) != 0) {
- ERROR("setuid failed: %s\n", strerror(errno));
- _exit(127);
- }
- }
-
- if (!dynamic_args) {
- if (execve(svc->args[0], (char**) svc->args, (char**) ENV) < 0) {
- ERROR("cannot execve('%s'): %s\n", svc->args[0], strerror(errno));
- }
- } else {
- char *arg_ptrs[INIT_PARSER_MAXARGS+1];
- int arg_idx = svc->nargs;
- char *tmp = strdup(dynamic_args);
- char *next = tmp;
- char *bword;
-
-
- memcpy(arg_ptrs, svc->args, (svc->nargs * sizeof(char *)));
-
- while((bword = strsep(&next, " "))) {
- arg_ptrs[arg_idx++] = bword;
- if (arg_idx == INIT_PARSER_MAXARGS)
- break;
- }
- arg_ptrs[arg_idx] = '\0';
- execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
- }
- _exit(127);
- }
-
- if (pid < 0) {
- ERROR("failed to start '%s'\n", svc->name);
- svc->pid = 0;
- return;
- }
-
- svc->time_started = gettime();
- svc->pid = pid;
- svc->flags |= SVC_RUNNING;
-
- if (properties_inited())
- notify_service_state(svc->name, "running");
- }
以属性的方式记录服务的运行状态:
- void notify_service_state(const char *name, const char *state)
- {
- char pname[PROP_NAME_MAX];
- int len = strlen(name);
- if ((len + 10) > PROP_NAME_MAX)
- return;
-
- snprintf(pname, sizeof(pname), "init.svc.%s", name);
-
- property_set(pname, state);
- }
2.系统属性设置
在设置系统属性前,依然要检查property_perms属性数组中的属性是否有指定的权限
- static int check_perms(const char *name, unsigned int uid, unsigned int gid)
- {
- int i;
- if (uid == 0)
- return 1;
- if(!strncmp(name, "ro.", 3))
- name +=3;
-
- for (i = 0; property_perms[i].prefix; i++) {
- int tmp;
- if (strncmp(property_perms[i].prefix, name,strlen(property_perms[i].prefix)) == 0) {
- if ((uid && property_perms[i].uid == uid) ||
- (gid && property_perms[i].gid == gid)) {
- return 1;
- }
- }
- }
- return 0;
- }
需要指定权限的属性存放在数组property_perms中
- 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 },
- { "persist.msms", 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 },
- { "service.", AID_SYSTEM, 0 },
- { "wlan.", AID_SYSTEM, 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.sys.", AID_SYSTEM, 0 },
- { "persist.service.", AID_SYSTEM, 0 },
- { "persist.security.", AID_SYSTEM, 0 },
- #ifdef BOARD_HAVE_BLUETOOTH_BCM
- { "service.brcm.bt.", AID_BLUETOOTH, 0 },
- { "service.brcm.bt.", AID_SYSTEM, 0 },
- { "persist.service.brcm.bt.", AID_BLUETOOTH, 0 },
- { "persist.service.brcm.bt.", AID_SYSTEM, 0 },
- { "brcm.fm_state", AID_SYSTEM, 0 },
- { "brcm.fm_state", AID_BLUETOOTH, 0 },
- { "brcm.bt_state", AID_SYSTEM, 0 },
- { "brcm.bt_state", AID_BLUETOOTH, 0 },
- #endif
- { NULL, 0, 0 }
- };
通过property_set函数来设置系统属性,这里的property_set函数是property_service.c中定义的函数,请仔细区分接下来客户进程设置属性的函数property_set,他们是完全不相同的函数调用:
- int property_set(const char *name, const char *value)
- {
- prop_area *pa;
- prop_info *pi;
-
- int namelen = strlen(name);
- int valuelen = strlen(value);
-
- if(namelen >= PROP_NAME_MAX) return -1;
- if(valuelen >= PROP_VALUE_MAX) return -1;
- if(namelen < 1) return -1;
-
- pi = (prop_info*) __system_property_find(name);
-
- if(pi != 0) {
-
- if(!strncmp(name, "ro.", 3)) return -1;
-
- pa = __system_property_area__;
- update_prop_info(pi, value, valuelen);
- pa->serial++;
- __futex_wake(&pa->serial, INT32_MAX);
-
- } else {
- pa = __system_property_area__;
- if(pa->count == PA_COUNT_MAX) return -1;
-
- pi = pa_info_array + pa->count;
- pi->serial = (valuelen << 24);
- memcpy(pi->name, name, namelen + 1);
- memcpy(pi->value, value, valuelen + 1);
- pa->toc[pa->count] =(namelen << 24) | (((unsigned) pi) - ((unsigned) pa));
- pa->count++;
- pa->serial++;
- __futex_wake(&pa->serial, INT32_MAX);
- }
-
- if (strncmp("net.", name, strlen("net.")) == 0) {
- if (strcmp("net.change", name) == 0) {
- return 0;
- }
-
-
-
-
-
- property_set("net.change", name);
-
- } else if (persistent_properties_loaded && strncmp("persist.", name, strlen("persist.")) == 0) {
-
-
-
-
- write_persistent_property(name, value);
- }
-
- property_changed(name, value);
- return 0;
- }
property_set()函数,更改属性值,并调用property_changed()函数处理在init.rc文件中记录着的某个属性改变后要采取的动作,动作执行条件以“on property:<key> = <value>” 的形式给出,当某个条件相关的键值被设定后,与该条件相关的触发器就会被触发。
- void property_changed(const char *name, const char *value)
- {
- if (property_triggers_enabled)
- queue_property_triggers(name, value);
- }
例如:
# adbd on at boot in emulator
on property:ro.kernel.qemu=1
start adbd
当属性ro.kernel.qemu=1时,启动adb服务,queue_property_triggers()函数处理属性触发的动作:
- void queue_property_triggers(const char *name, const char *value)
- {
- 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:"))) {
- const char *test = act->name + strlen("property:");
- int name_length = strlen(name);
-
- if (!strncmp(name, test, name_length) &&
- test[name_length] == '=' &&
- (!strcmp(test + name_length + 1, value) ||
- !strcmp(test + name_length + 1, "*"))) {
- action_add_queue_tail(act);
- }
- }
- }
- }
遍历action_list链表,查找以property:属性名命名形式的action,并添加到action_queue队列尾。
客户进程初始化属性匿名共享内存
共享内存空间fd size作为环境变量传递给新创建进程,将系统属性内存空间映射到当前进程虚拟空间:进程在启动时,会加载动态库bionic libc库:
\bionic\libc\bionic\libc_init_dynamic.c中:
- void __libc_preinit(void)
- {
- __libc_init_common(elfdata);
- }
-
- void __libc_init_common(uintptr_t *elfdata)
- {
- __system_properties_init();
- }
-
- int __system_properties_init(void)
- {
- prop_area *pa; int s, fd; unsigned sz; char *env;
-
-
- env = getenv("ANDROID_PROPERTY_WORKSPACE");
-
- fd = atoi(env);
- sz = atoi(env + 1);
-
- pa = mmap(0, sz, PROT_READ, MAP_SHARED, fd, 0);
-
- __system_property_area__ = pa;
- }
客户进程读取或设置属性
framework通过SystemProperties接口操作系统属性,SystemProperties通过JNI调用访问系统属性。
\frameworks\base\core\java\android\os\ SystemProperties.java:
- public class SystemProperties
- {
-
- private static native String native_get(String key, String def);
- private static native void native_set(String key, String def);
-
- public static String get(String key, String def) {
- return native_get(key, def);
- }
-
- public static void set(String key, String val) {
- native_set(key, val);
- }
- }
获取系统属性
因为在属性服务启动时就已经将系统属性从属性文件中加载到共享内存中,因此系统属性的读取其实是从属性系统的匿名共享内存中读取的。
\frameworks\base\core\jni\android_os_SystemProperties.cpp
- static jstring SystemProperties_getS(JNIEnv *env, jobject clazz,
- jstring keyJ)
- {
- return SystemProperties_getSS(env, clazz, keyJ, NULL);
- }
调用另一个JNI函数来实现
- 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;
- }
- key = env->GetStringUTFChars(keyJ, NULL);
- len = property_get(key, buf, "");
- if ((len <= 0) && (defJ != NULL)) {
- rvJ = defJ;
- } else if (len >= 0) {
- rvJ = env->NewStringUTF(buf);
- } else {
- rvJ = env->NewStringUTF("");
- }
- env->ReleaseStringUTFChars(keyJ, key);
- error:
- return rvJ;
- }
根据键获取属性值
- 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);
- memcpy(value, default_value, len + 1);
- }
- 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;
- }
- }
查找属性信息
- const prop_info *__system_property_find(const char *name)
- {
- prop_area *pa = __system_property_area__;
- unsigned count = pa->count;
- unsigned *toc = pa->toc;
- unsigned len = strlen(name);
- prop_info *pi;
- while(count--) {
- unsigned entry = *toc++;
- if(TOC_NAME_LEN(entry) != len) continue;
-
- pi = TOC_TO_INFO(pa, entry);
- if(memcmp(name, pi->name, len)) continue;
-
- return pi;
- }
- return 0;
- }
进程启动后已经将系统属性数据读取到相应的共享内存中,保存在全局变量__system_property_area__;
- int __system_property_read(const prop_info *pi, char *name, char *value)
- {
- unsigned serial, len;
- 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);
- if(serial == pi->serial) {
- if(name != 0) {
- strcpy(name, pi->name);
- }
- return len;
- }
- }
- }
设置系统属性
- 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 = "";
- } 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");
- }
- }
设置属性值
- int property_set(const char *key, const char *value)
- {
- return __system_property_set(key, value);
- }
- int __system_property_set(const char *key, const char *value)
- {
- int err;
- int tries = 0;
- int update_seen = 0;
- prop_msg msg;
- 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;
-
- memset(&msg, 0, sizeof msg);
-
- msg.cmd = PROP_MSG_SETPROP;
-
- strlcpy(msg.name, key, sizeof msg.name);
- strlcpy(msg.value, value, sizeof msg.value);
-
- err = send_prop_msg(&msg);
- if(err < 0) {
- return err;
- }
- return 0;
- }
通过名为"property_service"的socket向Android属性服务发送消息,Android属性服务驻留在init进程中,当消息发出后,init进程接收到属性服务socket的消息,并处理调用handle_property_set_fd函数来真正设置属性,属性服务设置属性的过程在属性服务端设置属性小节中已经介绍了。
- 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));
-
- 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;
-
- if(TEMP_FAILURE_RETRY(connect(s, (struct sockaddr *) &addr, alen) < 0)) {
- close(s);
- return result;
- }
-
- r = TEMP_FAILURE_RETRY(send(s, msg, sizeof(prop_msg), 0));
- if(r == sizeof(prop_msg)) {
-
-
-
-
-
-
-
- pollfds[0].fd = s;
- pollfds[0].events = 0;
-
- r = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 ));
- if (r == 1 && (pollfds[0].revents & POLLHUP) != 0) {
- result = 0;
- } else {
-
-
-
-
-
-
-
-
-
-
- result = 0;
- }
- }
- close(s);
- return result;
- }
对于客户端进程来说这样就算完成了属性的设置,其实属性的设置工作被转交给了init进程的属性系统来完成的,init进程的属性系统在前面中已经详细介绍了。Android的属性系统到此基本上就介绍完了。