android 1号进程进程init进程在开机的时候就会调用property_init函数,至于init是怎么起来的,这里不是重点,所以暂时先不介绍,property_init的具体flow如下:
system/core/init/init.c
void property_init(void)
{
init_property_area();
}
system/core/init/property_service.c
static int init_property_area(void)
{
if (property_area_inited)
return -1;
if(__system_property_area_init())
return -1;
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.c
int __system_property_area_init()
{
return map_prop_area_rw();
}
static int map_prop_area_rw()
{
prop_area *pa;
int fd;
int ret;
/* dev is a tmpfs that we can use to carve a shared workspace
* out of, so let's do that...
*/
fd = open(property_filename, 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;
}
ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
if (ret < 0)
goto out;
if (ftruncate(fd, PA_SIZE) < 0)
goto out;
pa_size = PA_SIZE;
pa_data_size = pa_size - sizeof(prop_area);
compat_mode = false;
pa = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if(pa == MAP_FAILED)
goto out;
memset(pa, 0, pa_size);
pa->magic = PROP_AREA_MAGIC;
pa->version = PROP_AREA_VERSION;
/* reserve root node */
pa->bytes_used = sizeof(prop_bt);
/* plug into the lib property services */
__system_property_area__ = pa;
close(fd);
return 0;
out:
close(fd);
return -1;
}
看到open(property_filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC |O_EXCL, 0444);
和 pa = mmap(NULL, pa_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
我们知道文件property_filename (即 /dev/__properties__)被打开,并以读写可共享的方式
映射到了内存。(其他全局变量,如pa_data_size和__system_property_area__ 暂放置一边,
也不细节的讲述get_fd_from_env() )。
property_set/property_get位于libcutils.so库,任何进程若要调用这两个函数,需要链接libcutils.so。libcutils.so调用了__system_property_set和__system_Property_get函数,这两个函数位于bionic库中,生成的库文件为libc_common.so。
bionic/libc/bionic/system_properties.c
int __system_property_set(const char *key, const char *value)
{
int err;
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;
}
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)) {
// We successfully wrote to the property server but now we
// wait for the property server to finish its work. It
// acknowledges its completion by closing the socket so we
// poll here (on nothing), waiting for the socket to close.
// If you 'adb shell setprop foo bar' you'll see the POLLHUP
// once the socket closes. Out of paranoia we cap our poll
// at 250 ms.
pollfds[0].fd = s;
pollfds[0].events = 0;
r = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */));
if (r == 1 && (pollfds[0].revents & POLLHUP) != 0) {
result = 0;
} else {
// Ignore the timeout and treat it like a success anyway.
// The init process is single-threaded and its property
// service is sometimes slow to respond (perhaps it's off
// starting a child process or something) and thus this
// times out and the caller thinks it failed, even though
// it's still getting around to it. So we fake it here,
// mostly for ctl.* properties, but we do try and wait 250
// ms so callers who do read-after-write can reliably see
// what they've written. Most of the time.
// TODO: fix the system properties design.
result = 0;
}
}
close(s);
return result;
}
property_service.c收到这个msg后,会根据msg.name和msg.value来设置具体的property,在这路需要说明一下,如果根据name能够找到对应的prop_info信息,那么就去update对应的value值,如果找不到对应的prop_info信息,那么就去add一个prop_info。具体flow如下:
system/core/libcutils/properties.c
int property_set(const char *key, const char *value)
{
char sendBuf[1+PROPERTY_KEY_MAX+PROPERTY_VALUE_MAX];
char recvBuf[1];
int result = -1;
//ALOGV("PROPERTY SET [%s]: [%s]\n", key, value);
pthread_once(&gInitOnce, init);
if (gPropFd < 0)
return -1;
if (strlen(key) >= PROPERTY_KEY_MAX) return -1;
if (strlen(value) >= PROPERTY_VALUE_MAX) return -1;
memset(sendBuf, 0xdd, sizeof(sendBuf)); // placate valgrind
sendBuf[0] = (char) kSystemPropertySet;
strcpy(sendBuf+1, key);
strcpy(sendBuf+1+PROPERTY_KEY_MAX, value);
pthread_mutex_lock(&gPropertyFdLock);
if (write(gPropFd, sendBuf, sizeof(sendBuf)) != sizeof(sendBuf)) {
pthread_mutex_unlock(&gPropertyFdLock);
return -1;
}
if (read(gPropFd, recvBuf, sizeof(recvBuf)) != sizeof(recvBuf)) {
pthread_mutex_unlock(&gPropertyFdLock);
return -1;
}
pthread_mutex_unlock(&gPropertyFdLock);
if (recvBuf[0] != 1)
return -1;
return 0;
}