前面一篇主要分析了属性service端的初始化和处理过程,当一个应用程序要获取或者设置属性时,需要怎么去操作呢
获取属性可以通过调用property_get接口来完成
const char* property_get(const char *name)
{
prop_info *pi;
if(strlen(name) >= PROP_NAME_MAX) return 0;
pi = (prop_info*) __system_property_find(name);
if(pi != 0) {
return pi->value;
} else {
return 0;
}
}
我们可以看到,其也是通过__system_property_find函数查找到该属性,然后返回其value值,但是,我们应该知道这个跟init进程不是同一个进程,而他又是怎么获取到在init进程中设置的属性值呢, 这就是通过前面的共享内存
在bionic/libc/bionic/libc_init_dynamic.c文件中我们可以看到有 这样一句
/* We flag the __libc_preinit function as a constructor to ensure
* that its address is listed in libc.so's .init_array section.
* This ensures that the function is called by the dynamic linker
* as soon as the shared library is loaded.
*/
void __attribute__((constructor)) __libc_preinit(void)
__attribute__((constructor))表示这段代码将在main函数前调用,具体可以参考 http://blog.csdn.net/polisan/article/details/5031142
我们再看一下__libc_preinit函数
void __libc_preinit(void)
{
/* Read the ELF data pointer form a special slot of the
* TLS area, then call __libc_init_common with it.
*
* Note that:
* - we clear the slot so no other initializer sees its value.
* - __libc_init_common() will change the TLS area so the old one
* won't be accessible anyway.
*/
void** tls_area = (void**)__get_tls();
unsigned* elfdata = tls_area[TLS_SLOT_BIONIC_PREINIT];
tls_area[TLS_SLOT_BIONIC_PREINIT] = NULL;
__libc_init_common(elfdata);
/* Setup malloc routines accordingly to the environment.
* Requires system properties
*/
extern void malloc_debug_init(void);
malloc_debug_init();
}
继续跟踪__libc_init_common
void __libc_init_common(uintptr_t *elfdata)
{
int argc = *elfdata;
char** argv = (char**)(elfdata + 1);
char** envp = argv + argc + 1;
pthread_attr_t thread_attr;
static pthread_internal_t thread;
static void* tls_area[BIONIC_TLS_SLOTS];
/* setup pthread runtime and maint thread descriptor */
unsigned stacktop = (__get_sp() & ~(PAGE_SIZE - 1)) + PAGE_SIZE;
unsigned stacksize = 128 * 1024;
unsigned stackbottom = stacktop - stacksize;
pthread_attr_init(&thread_attr);
pthread_attr_setstack(&thread_attr, (void*)stackbottom, stacksize);
_init_thread(&thread, gettid(), &thread_attr, (void*)stackbottom);
__init_tls(tls_area, &thread);
/* clear errno - requires TLS area */
errno = 0;
/* set program name */
__progname = argv[0] ? argv[0] : "<unknown>";
/* setup environment pointer */
environ = envp;
/* setup system properties - requires environment */
__system_properties_init();
}
终于看到了__system_properties_init
int __system_properties_init(void)
{
prop_area *pa;
int s, fd;
unsigned sz;
char *env;
if(__system_property_area__ != ((void*) &dummy_props)) {
return 0;
}
env = getenv("ANDROID_PROPERTY_WORKSPACE");//获取环境变量值
if (!env) {
return -1;
}
fd = atoi(env);//获取fd
env = strchr(env, ',');
if (!env) {
return -1;
}
sz = atoi(env + 1);//获取size
pa = mmap(0, sz, PROT_READ, MAP_SHARED, fd, 0);//只读映射
if(pa == MAP_FAILED) {
return -1;
}
if((pa->magic != PROP_AREA_MAGIC) || (pa->version != PROP_AREA_VERSION)) {
munmap(pa, sz);
return -1;
}
__system_property_area__ = pa;
return 0;
}
这里从环境变量ANDROID_PROPERTY_WORKSPACE中读取共享内存的大小和fd,并把相关的值保存在__system_property_area__中,那么这个ANDROID_PROPERTY_WORKSPACE是在哪设置的 呢,我们再看一下init.c中的service_start
void service_start(struct service *svc, const char *dynamic_args)
{
struct stat s;
pid_t pid;
int needs_console;
......
if (properties_inited()) {
get_property_workspace(&fd, &sz);
sprintf(tmp, "%d,%d", dup(fd), sz);
add_environment("ANDROID_PROPERTY_WORKSPACE", tmp);
}
......
}
在启动一个service的时候,首先会判断 property_area_inited的值,如里 不为0,则调用get_property_workspace获取fd和映射的内存大小,接着调用add_environment设置ANDROID_PROPERTY_WORKSPACE的值。 这样,当我们启动一个应用程序的时候就可以获取
ANDROID_PROPERTY_WORKSPACE环境变量的值,从而实现共享了。
这样,property_get就可以很轻松的获取到我们前面设置的属性值了
2、property_set
int property_set(const char *key, const char *value)
{
prop_msg msg;
unsigned resp;
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;
msg.cmd = PROP_MSG_SETPROP;
strcpy((char*) msg.name, key);
strcpy((char*) msg.value, value);
return send_prop_msg(&msg);
}
这里只是简单的调用send_prop_msg发送PROP_MSG_SETPROP消息。
static int send_prop_msg(prop_msg *msg)
{
int s;
int r;
s = socket_local_client(PROP_SERVICE_NAME,
ANDROID_SOCKET_NAMESPACE_RESERVED,
SOCK_STREAM);
if(s < 0) return -1;
while((r = send(s, msg, sizeof(prop_msg), 0)) < 0) {
if((errno == EINTR) || (errno == EAGAIN)) continue;
break;
}
if(r == sizeof(prop_msg)) {
r = 0;
} else {
r = -1;
}
close(s);
return r;
}
建立一个到PROP_SERVICE_NAME的sock通信,然后把消息发到init进程,具体由其进行属性的设置。