您的位置:首页 > 移动开发 > Android开发

深入讲解Android Property机制

2017-04-17 02:12 405 查看
 
深入讲解Android Property机制

侯亮

1      概述

     Android系统(本文以Android 4.4为准)的属性(Property)机制有点儿类似Windows系统的注册表,其中的每个属性被组织成简单的键值对(key/value)供外界使用。

     我们可以通过在adb shell里敲入getprop命令来获取当前系统的所有属性内容,而且,我们还可以敲入类似“getprop
属性名”的命令来获取特定属性的值。另外,设置属性值的方法也很简单,只需敲入“setprop
属性名 新值”命令即可。

可是问题在于我们不想只认识到这个层次,我们希望了解更多一些Property机制的运作机理,而这才是本文关心的重点。

     说白了,Property机制的运作机理可以汇总成以下几句话:
1)  系统一启动就会从若干属性脚本文件中加载属性内容;
2)  系统中的所有属性(key/value)会存入同一块共享内存中;
3)  系统中的各个进程会将这块共享内存映射到自己的内存空间,这样就可以直接读取属性内容了;
4)  系统中只有一个实体可以设置、修改属性值,它就是属性服务(Property Service);
5)  不同进程只可以通过socket方式,向属性服务发出修改属性值的请求,而不能直接修改属性值;
6)  共享内存中的键值内容会以一种字典树的形式进行组织。

Property机制的示意图如下:


 

2      Property Service

2.1  init进程里的Property Service

Property Service实体其实是在init进程里启动的。我们知道,init是Linux系统中用户空间的第一个进程。它负责创建系统中最关键的几个子进程,比如zygote等等。在本节中,我们主要关心init进程是如何启动Property
Service的。

我们查看core/init/Init.c文件,可以看到init进程的main()函数,它里面和property相关的关键动作有:
1)间接调用__system_property_area_init():打开属性共享内存,并记入__system_property_area变量;
2)间接调用init_workspace():只读打开属性共享内存,并记入环境变量;
3)根据init.rc,异步激发property_service_init_action(),该函数中会:

    l  加载若干属性文本文件,将具体属性、属性值记入属性共享内存;

    l  创建并监听socket;
4)根据init.rc,异步激发queue_property_triggers_action(),将刚刚加载的属性对应的激发动作,推入action列表。

main()中的调用关系如下:



 

2.1.1   初始化属性共享内存

     我们可以看到,在init进程的main()函数里,辗转打开了一个内存文件“/dev/__properties__”,并把它设定为128KB大小,接着调用mmap()将这块内存映射到init进程空间了。这个内存的首地址被记录在__system_property_area__全局变量里,以后每添加或修改一个属性,都会基于这个__system_property_area__变量来计算位置。

      初始化属性内存块时,为什么要两次open那个/dev/__properties__文件呢?我想原因是这样的:第一次open的句柄,最终是给属性服务自己用的,所以需要有读写权限;而第二次open的句柄,会被记入pa_workspace.fd,并在合适时机添加进环境变量,供其他进程使用,因此只能具有读取权限。

第一次open时,执行的代码如下:

fd = open(property_filename,
O_RDWR | O_CREAT |
O_NOFOLLOW | O_CLOEXEC |
O_EXCL, 0444);
传给open()的参数标识里指明了O_RDWR,表示用“读写方式”打开文件。另外O_NOFOLLOW标识主要是为了防止我们打开“符号链接”,不过我们知道,__properties__文件并不是符号链接,所以当然可以成功open。O_CLOEXEC标识是为了保证一种独占性,也就是说当init进程打开这个文件时,此时就算其他进程也open这个文件,也会在调用exec执行新程序时自动关闭该文件句柄。O_EXCL标识和O_CREATE标识配合起来,表示如果文件不存在,则创建之,而如果文件已经存在,那么open就会失败。第一次open动作后,会给__system_property_area__赋值,然后程序会立即close刚打开的句柄。

第二次open动作发生在接下来的init_workspace()函数里。此时会再一次打开__properties__文件,这次却是以只读模式打开的:

int fd = open(PROP_FILENAME,
O_RDONLY | O_NOFOLLOW);

打开的句柄记录在pa_workspace.fd处,以后每当init进程执行socket命令,并调用service_start()时,会执行类似下面的句子:

get_property_workspace(&fd, &sz);   // 读取pa_workspace.fd
sprintf(tmp, "%d,%d", dup(fd), sz);
add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);

void start_property_service(void)
{
int fd;

load_properties_from_file(PROP_PATH_SYSTEM_BUILD);
load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);

/* Read vendor-specific property runtime overrides. */
vendor_load_properties();

load_override_properties();
/* Read persistent properties after all default values have been loaded. */
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;
}

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;


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);
}
}
}
}
}
}


void handle_property_set_fd()
{
prop_msg msg;
. . . . . .
if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) {
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) {
. . . . . .
if (check_control_perms(msg.value, cr.uid, cr.gid, 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, cr.uid, cr.gid, 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);
}
. . . . . .
close(s);
}
. . . . . .
break;
. . . . . .
}
}


        
static int check_control_perms(const char *name, unsigned int uid, unsigned int gid, char *sctx) {
int i;
if (uid == AID_SYSTEM || uid == AID_ROOT)
return check_control_mac_perms(name, sctx);

/* Search the ACL */
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 check_control_mac_perms(name, sctx);
}
}
}
return 0;
}


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);
}
}


void service_start(struct service *svc, const char *dynamic_args)
{
. . . . . .
. . . . . .
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);
. . . . . .


void service_start(struct service *svc, const char *dynamic_args)
{
. . . . . .
. . . . . .
execve(svc->args[0], (char**) arg_ptrs, (char**) ENV);
. . . . . .
. . . . . .
svc->time_started = gettime();
svc->pid = pid;
svc->flags |= SVC_RUNNING;

if (properties_inited())
notify_service_state(svc->name, "running");
}


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);
}
}


 

void service_stop(struct service *svc)
{
service_stop_or_reset(svc, SVC_DISABLED);
}


 

static void service_stop_or_reset(struct service *svc, int how)
{
/* The service is still SVC_RUNNING until its process exits, but if it has
* already exited it shoudn't attempt a restart yet. */
svc->flags &= (~SVC_RESTARTING);

if ((how != SVC_DISABLED) && (how != SVC_RESET) && (how != SVC_RESTART)) {
/* Hrm, an illegal flag.  Default to SVC_DISABLED */
how = SVC_DISABLED;
}
/* if the service has not yet started, prevent
* it from auto-starting with its class
*/
if (how == SVC_RESET) {
svc->flags |= (svc->flags & SVC_RC_DISABLED) ? SVC_DISABLED : SVC_RESET;
} else {
svc->flags |= how;
}

if (svc->pid) {
NOTICE("service '%s' is being killed\n", svc->name);
kill(-svc->pid, SIGKILL);
notify_service_state(svc->name, "stopping");
} else {
notify_service_state(svc->name, "stopped");
}
}


 
static int check_perms(const char *name, unsigned int uid, unsigned int gid, char *sctx)
{
int i;
unsigned int app_id;

if(!strncmp(name, "ro.", 3))
name +=3;

if (uid == 0)
return check_mac_perms(name, sctx);

app_id = multiuser_get_app_id(uid);
if (app_id == AID_BLUETOOTH) {
uid = app_id;
}

for (i = 0; property_perms[i].prefix; i++) {
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 check_mac_perms(name, sctx);
}
}
}

return 0;
}


void property_changed(const char *name, const char *value)
{
if (property_triggers_enabled)
queue_property_triggers(name, value);
}


void action_add_queue_tail(struct action *act)
{
if (list_empty(&act->qlist)) {
list_add_tail(&action_queue, &act->qlist);
}
}


int __system_properties_init()
{
return map_prop_area();
}


static int get_fd_from_env(void)
{
char *env = getenv("ANDROID_PROPERTY_WORKSPACE");
if (!env) {
return -1;
}
return atoi(env);
}


__noreturn void __libc_init(void* raw_args,
void (*onexit)(void),
int (*slingshot)(int, char**, char**),
structors_array_t const * const structors) {
KernelArgumentBlock args(raw_args);
__libc_init_tls(args);
__libc_init_common(args);
. . . . . .
. . . . . .
call_array(structors->preinit_array);
call_array(structors->init_array);
. . . . . .
exit(slingshot(args.argc, args.argv, args.envp));
}


       
struct prop_area {
unsigned bytes_used;
unsigned volatile serial;
unsigned magic;
unsigned version;
unsigned reserved[28];
char data[0];
};

typedef struct prop_area prop_area;

struct prop_info {
unsigned volatile serial;
char value[PROP_VALUE_MAX];
char name[0];
};

typedef struct prop_info prop_info;


 

typedef volatile uint32_t prop_off_t;
struct prop_bt {
uint8_t namelen;
uint8_t reserved[3];

prop_off_t prop;

prop_off_t left;
prop_off_t right;

prop_off_t children;

char name[0];
};

typedef struct prop_bt prop_bt;


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 },
};


【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);
}

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;
}


最终调用的还是property_get()函数。

5      尾声

至此,有关Android属性机制的大体机理就讲解完毕了,希望对大家有点儿帮助。

转载地址:https://my.oschina.net/youranhongcha/blog/389640
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android android源码