一、Android4.4属性系统系列文章
Android4.4属性系统-初始化
Android4.4属性系统-系统服务
Android4.4属性系统-内存空间共享
Android4.4属性系统-属性获取
Android4.4属性系统-属性设置
Android4.4-属性的使用总结
二、写在前面-如何阅读本系列文章
本系列文章大部分是对源码的解析和注释,所以读起来枯燥无味,并且杂乱,这是阅读系统源码无法避免的,如果你有条件,可以点击下载Android4.4源码,阅读源码可以使用eclise,AndroidStudio,vim等。
文章的章节安卓是按照代码模块区分的,例如init进程代码,libcutils代码是按章节来区分,但不同模块的代码之间是有关联的,阅读时需要经常跳转,通过搜索功能进行页内搜索即可
三、属性服务解析
本章节介绍属性系统服务的详细启动过程,Android 属性系统服务实际上是由 init 进程维护的,通过创建一个 socket来监听相应的属性获取和设置的请求,最终需要由 init 进程来处理。
在Android4.4属性系统-初始化中介绍过,在init.c的main()函数中,调用了如下代码启动系统服务
//设置函数指针property_service_init_action,服务名称为"property_service_init" queue_builtin_action(property_service_init_action, "property_service_init");
之后在for循环中执行了execute_one_command()
,调用了每个ation的函数指针,property_service_init_action()
也被调用,继而开始启动属性系统服务。
3.1系统服务的启动init.c
property_service_init_action开始启动属性系统服务,注释中写明在读取属性文件时会触发属性服务运行,该函数最终会调用start_property_service
运行属性服务,主要是创建一个 socket 并监听。
system/core/init/init.c
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(); return 0; }
3.2 property_service.c解析
system/core/init/property_service.c
start_property_service首先调用 load_properties_from_file 分别从 3 个文件中加载属性。PROP_PATH_SYSTEM_BUILD、PROP_PATH_SYSTEM_DEFAULT都定义在/bionic/libc/include/sys/_system_properties.h中。
void start_property_service(void) { int fd; load_properties_from_file(PROP_PATH_SYSTEM_BUILD);// /system/build.prop load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);// /system/default.prop load_override_properties(); //加载overrides属性 /* Read persistent properties after all default values have been loaded. */ load_persistent_properties(); //加载完所有default属性后开始加载持久化属性 /*创建一个socket,create_socket定义在util.c *参数<"property_service", scoket类型,0666 表示权限, uid, gid> * PROP_SERVICE_NAME定义在_system_properties.h中;,对应的 socket 文件为”/dev/socket/property_service” * uid=0,gid=0表示 root 用户和组 */ 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); //监听创建的 socket,listen 函数定义于/bionic/libc/unistd/socketcalls.c 文件中 listen(fd, 8); //将 property_set_fd 赋值为创建的 socket 文件描述符 property_set_fd = fd; } //加载可覆盖的属性,PROP_PATH_LOCAL_OVERRIDE定义在_system_properties.h static void load_override_properties() { #ifdef ALLOW_LOCAL_PROP_OVERRIDE char debuggable[PROP_VALUE_MAX]; int ret; ret = property_get("ro.debuggable", debuggable); if (ret && (strcmp(debuggable, "1") == 0)) { load_properties_from_file(PROP_PATH_LOCAL_OVERRIDE);// /data/local.prop } #endif /* ALLOW_LOCAL_PROP_OVERRIDE */ } //从指定文件中加载属性 static void load_properties_from_file(const char *fn) { char *data; unsigned sz; //打开文件,读取内容 data = read_file(fn, &sz); if(data != 0) { //打开成功,加载property load_properties(data); //关闭文件 free(data); } } //解析property数据,逐行解析 static void load_properties(char *data) { char *key, *value, *eol, *sol, *tmp; sol = data; //sol指向文件开始 //char *strchr(const char *str, int c)返回在字符串 str 中第一次出现字符 c 的位置,实现逐行读取 while((eol = strchr(sol, '\n'))) { //返回换行符的指针 key = sol; //key表示行首的指针 *eol++ = 0; //相关于*eol=0; eol++; '\n'替换为0,此时eol为下一行的行首指针 sol = eol; //赋值给sol value = strchr(key, '='); //截取一行中'='前面的字符,value指向'=' if(value == 0) continue; //如果=前面为空,跳过 *value++ = 0; //’=‘替换为0,value指向原'='后面的内容,即实际的value字节地址 while(isspace(*key)) key++; //去掉一行前面的空格 if(*key == '#') continue; //如果是'#'开头,表示注释,跳过 tmp = value - 2; //value-2为key的末尾地址 while((tmp > key) && isspace(*tmp)) *tmp-- = 0; //判断key末尾是否有空白符,如果有替换为0 while(isspace(*value)) value++; //判断value起始内容中是否有空白符,如果有替换为0 tmp = eol - 2; //判断value末尾内容中是否有空白符,如果有替换为0 while((tmp > value) && isspace(*tmp)) *tmp-- = 0; //设置属性,关键调用 property_set(key, value); } } //设置属性,参数<key,value> int property_set(const char *name, const char *value) { prop_info *pi; int ret; size_t namelen = strlen(name); //key 长度 size_t valuelen = strlen(value); //value长度 if (!is_legal_property_name(name, namelen)) return -1; //判断prop name是否合法 if (valuelen >= PROP_VALUE_MAX) return -1; //判断value长度是否合法,PROP_VALUE_MAX=96 pi = (prop_info*) __system_property_find(name); //查询prop是否存在 if(pi != 0) { //如果系统已经存在该property /* ro.* properties may NEVER be modified once set */ if(!strncmp(name, "ro.", 3)) return -1; //ro-read only,ro开头是不允许修改的,返回-1 __system_property_update(pi, value, valuelen); //更新property } else { //如果系统中不存在,添加新的property 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) { //如果以net.开头 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. */ //'net.change'属性是一个特殊属性,用于更新任何'net.*'属性名称时的跟踪,它只能在这里更新。其值包含最后更新的'net.*'属性。 property_set("net.change", name); } else if (persistent_properties_loaded && // persistent_properties_loaded 在执行完load_persistent_properties()后置为1 strncmp("persist.", name, strlen("persist.")) == 0) {//persist.开头的 /* * Don't write properties to disk until after we have read all default properties * to prevent them from being overwritten by default values. */ //在读取完所有的default properties之后再写入硬盘,避免它们被default 值覆盖 write_persistent_property(name, value); } else if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) { //如果是selinux.reload_policy=1 selinux_reload_policy(); //重新加载selinux } property_changed(name, value); //property_changed位于init.c return 0; } //判断property字符串是否合法 static bool is_legal_property_name(const char* name, size_t namelen) { size_t i; bool previous_was_dot = false; if (namelen >= PROP_NAME_MAX) return false; if (namelen < 1) return false; if (name[0] == '.') return false; if (name[namelen - 1] == '.') return false; /* Only allow alphanumeric, plus '.', '-', or '_' */ /* Don't allow ".." to appear in a property name */ for (i = 0; i < namelen; i++) { if (name[i] == '.') { if (previous_was_dot == true) return false; previous_was_dot = true; continue; } previous_was_dot = false; if (name[i] == '_' || name[i] == '-') continue; if (name[i] >= 'a' && name[i] <= 'z') continue; if (name[i] >= 'A' && name[i] <= 'Z') continue; if (name[i] >= '0' && name[i] <= '9') continue; return false; } return true; }
load_persistent_properties
加载持久化属性,这些属性存在于/data/property 目录下,该目录下
存放了以 persist 开头的属性文件,如系统语言、国家编码等,如下图
可以看到文件名就是属性名,文件内容就是属性的value值。
//持久化属性存储路径 #define PERSISTENT_PROPERTY_DIR "/data/property" //加载持久化属性 static void load_persistent_properties() { DIR* dir = opendir(PERSISTENT_PROPERTY_DIR); int dir_fd; struct dirent* entry; char value[PROP_VALUE_MAX]; int fd, length; struct stat sb; if (dir) { dir_fd = dirfd(dir); //返回参数dir所指向的目录文件的文件描述符 while ((entry = readdir(dir)) != NULL) { /readdir打开目录,返回下个目录进入节点 //d_name 文件名,不是以persist.开头跳过 if (strncmp("persist.", entry->d_name, strlen("persist."))) continue; #if HAVE_DIRENT_D_TYPE if (entry->d_type != DT_REG) //判断文件类型,DT_REG常规文件 continue; #endif /* open the file and read the property value */ //打开文件 fd = openat(dir_fd, entry->d_name, O_RDONLY | O_NOFOLLOW); if (fd < 0) { ERROR("Unable to open persistent property file \"%s\" errno: %d\n", entry->d_name, errno); continue; } if (fstat(fd, &sb) < 0) { //获取文件状态,写入到sb中 ERROR("fstat on property file \"%s\" failed errno: %d\n", entry->d_name, errno); close(fd); continue; } // File must not be accessible to others, be owned by root/root, and // not be a hard link to any other file. /*文件不允许root/root用户/组之外的用户有访问权限,也不允许硬链接 *st_mode 表示文件权限和文件类型信息 * 如果存在以下几种情况是不允许读取的 */ if (((sb.st_mode & (S_IRWXG | S_IRWXO)) != 0) //如果属组或者其它用户有读写权限 || (sb.st_uid != 0) //uid!=0,不是root用户 || (sb.st_gid != 0) //不是root组 || (sb.st_nlink != 1)) { //该文件上硬连接的个数不是1(应该只有root一个) ERROR("skipping insecure property file %s (uid=%lu gid=%lu nlink=%d mode=%o)\n", entry->d_name, sb.st_uid, sb.st_gid, sb.st_nlink, sb.st_mode); close(fd); continue; } length = read(fd, value, sizeof(value) - 1); //从文件中读取value值 if (length >= 0) { value[length] = 0; property_set(entry->d_name, value); //设置property,文件名即为属性名 } else { ERROR("Unable to read persistent property file %s errno: %d\n", entry->d_name, errno); } close(fd); } closedir(dir); } else { ERROR("Unable to open persistent property directory %s errno: %d\n", PERSISTENT_PROPERTY_DIR, errno); } //设置标志位为1 persistent_properties_loaded = 1; }
system/core/init/util.c
/* * create_socket - creates a Unix domain socket in ANDROID_SOCKET_DIR * ("/dev/socket") as dictated in init.rc. This socket is inherited by the * daemon. We communicate the file descriptor's value via the environment * variable ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo"). */ //参数<套接字名称, socket类型,权限,用户,组> //调用时传入参数<"property_service", SOCK_STREAM(流套接字) , 0666, 0, 0> int create_socket(const char *name, int type, mode_t perm, uid_t uid, gid_t gid) { struct sockaddr_un addr; int fd, ret; char *secon; //创建socket,PF_UNIX表示进程间通信,0表示自动选取使用TCP还是UDP fd = socket(PF_UNIX, type, 0); if (fd < 0) { ERROR("Failed to open socket '%s': %s\n", name, strerror(errno)); return -1; } memset(&addr, 0 , sizeof(addr)); //初始化socket地址为0 addr.sun_family = AF_UNIX; //AF_UNIX用于同一台机器上的进程间通信,AF_INET为ipv4 snprintf(addr.sun_path, sizeof(addr.sun_path), ANDROID_SOCKET_DIR"/%s", name); ret = unlink(addr.sun_path); if (ret != 0 && errno != ENOENT) { ERROR("Failed to unlink old socket '%s': %s\n", name, strerror(errno)); goto out_close; } secon = NULL; if (sehandle) { ret = selabel_lookup(sehandle, &secon, addr.sun_path, S_IFSOCK); if (ret == 0) setfscreatecon(secon); } //对套接字进行地址和端口的绑定,才能进行数据的接收和发送操作 //位于socketcalls.c文件中 ret = bind(fd, (struct sockaddr *) &addr, sizeof (addr)); if (ret) { ERROR("Failed to bind socket '%s': %s\n", name, strerror(errno)); goto out_unlink; } setfscreatecon(NULL); freecon(secon); chown(addr.sun_path, uid, gid); //修改用户属性 chmod(addr.sun_path, perm); //修改权限 INFO("Created socket '%s' with mode '%o', user '%d', group '%d'\n", addr.sun_path, perm, uid, gid); return fd; out_unlink: unlink(addr.sun_path); out_close: close(fd); return -1; }
3.3 bionic/libc 库
bionic/libc/include/sys/_system_properties.h
#define PROP_AREA_MAGIC 0x504f5250 #define PROP_AREA_VERSION 0xfc6ed0ab #define PROP_AREA_VERSION_COMPAT 0x45434f76 #define PROP_PATH_RAMDISK_DEFAULT "/default.prop" #define PROP_PATH_SYSTEM_BUILD "/system/build.prop" #define PROP_PATH_SYSTEM_DEFAULT "/system/default.prop" #define PROP_PATH_LOCAL_OVERRIDE "/data/local.prop" #define PROP_PATH_FACTORY "/factory/factory.prop" //定义映射内存空间的大小128K #define PA_SIZE (128 * 1024) //prop service的名称 #define PROP_SERVICE_NAME "property_service" //prop内存映射文件 #define PROP_FILENAME "/dev/__properties__"
socketcalls.c在项目源码中没有找到,应该是被展讯做修改后,当作预置库放到源码中了。
下面是从AOSP下载的Android4.4源码
bionic/libc/unistd/socketcalls.c
//定义了SOCKET行为结构体 enum { SYS_SOCKET = 1, SYS_BIND, SYS_CONNECT, SYS_LISTEN, SYS_ACCEPT, SYS_GETSOCKNAME, SYS_GETPEERNAME, SYS_SOCKETPAIR, SYS_SEND, SYS_RECV, SYS_SENDTO, SYS_RECVFROM, SYS_SHUTDOWN, SYS_SETSOCKOPT, SYS_GETSOCKOPT, SYS_SENDMSG, SYS_RECVMSG }; //socket生成函数,参数<地址域(faminly), socket类型,协议> int socket(int domain, int type, int protocol) { unsigned long t[3]; t[0] = (unsigned long) domain; t[1] = (unsigned long) type; t[2] = (unsigned long) protocol; return (int) __socketcall( SYS_SOCKET, t ); } //绑定地址和端口,参数<socket, 地址和商品,地址长度> int bind(int sockfd, const struct sockaddr *my_addr, socklen_t addrlen) { unsigned long t[3]; t[0] = (unsigned long) sockfd; t[1] = (unsigned long) my_addr; t[2] = (unsigned long) addrlen; return (int) __socketcall( SYS_BIND, t ); } //监听端口,参数<scoket句柄,backlog 为请求队列的最大长度> // listen() 函数让套接字进入被动监听状态,再调用 accept() 函数,就可以随时响应客户端的请求 int listen(int s, int backlog) { unsigned long t[2]; t[0] = (unsigned long) s; t[1] = (unsigned long) backlog; return (int) __socketcall( SYS_LISTEN, t ); }
可以看到上面这几个函数调用的都是__socketcall(),而这个函数是在哪里定义的就找不到喽~有知道的大神还请指点一二, 先谢过了!
四、补充知识点
4.1 fstat函数
文中使用到的sb.st_mode解释如下:
st_mode域是需要一些宏予以配合才能使用的。其实,通俗说,这些宏就是一些特定位置为1的二进制数的外号,我们使用它们和st_mode进行”&”操作,从而就可以得到某些特定的信息。
用于解释st_mode标志的掩码包括:
S_IFMT:文件类型
S_IRWXU:属主的读/写/执行权限,可以分成S_IXUSR, S_IRUSR, S_IWUSR
S_IRWXG:属组的读/写/执行权限,可以分成S_IXGRP, S_IRGRP, S_IWGRP
S_IRWXO:其他用户的读/写/执行权限,可以分为S_IXOTH, S_IROTH, S_IWOTH
4.2 socket函数
int socket(int af, int type, int protocol);
af 为地址族(Address Family),也就是 IP 地址类型,比较常用的为AF_INET或AF_UNIX。AF_UNIX用于同一台机器上的进程间通信,AF_INET对于IPV4协议的TCP和UDP ,AF_INET6对应于IPV6。
AF_INET和PF_INET的值是相同的
五、总结,放一张图
六、参考
Android属性系统
Linux 内存映射函数 mmap
trie
Linux 内存映射函数 mmap()函数详解