源码路径system/core/init/property_service.c
//处理客户端的连接请求 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); char * source_ctx = NULL; //accept接受连接请求 if ((s = accept(property_set_fd, (struct sockaddr *) &addr, &addr_size)) < 0) { return; } /* Check socket options here */ if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) { close(s); ERROR("Unable to receive socket options\n"); return; } //recv从 socket 中获取数据,由于 send_prop_msg 发送的数据位 prop_msg,因此此处获得的数据便是一个 prop_msg 结构体 r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), 0)); if(r != sizeof(prop_msg)) { ERROR("sys_prop: mis-match msg size received: %d expected: %d errno: %d\n", r, sizeof(prop_msg), errno); close(s); return; } //根据 prop_msg 成员cmd 值做相应操作,对于属性设置请求来说,cmd 值为 PROP_MSG_SETPROP,而实际上 //handle_property_set_fd 函数也只是处理属性设置请求 switch(msg.cmd) { case PROP_MSG_SETPROP: msg.name[PROP_NAME_MAX-1] = 0; msg.value[PROP_VALUE_MAX-1] = 0; if (!is_legal_property_name(msg.name, strlen(msg.name))) { ERROR("sys_prop: illegal property name. Got: \"%s\"\n", msg.name); close(s); return; } getpeercon(s, &source_ctx); /*判断需要设置的属性名称是否以”ctl.”开头, *ctl.start 可以用来启动服务, *ctl.stop 用来停止服务, *ctl.restart 可以用来重启服务 */ if(memcmp(msg.name,"ctl.",4) == 0) { // Keep the old close-socket-early behavior when handling ctl.* properties. close(s); //检查控制权限,通过后就可以控制属性服务的启动和停止 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); } // Note: bionic's property client code assumes that the // property server will not close the socket until *AFTER* // the property is written to memory. close(s); } freecon(source_ctx); break; default: close(s); break; } } /* 白名单一 * White list of UID that are allowed to start/stop services. * Currently there are no user apps that require. */ struct { const char *service; unsigned int uid; unsigned int gid; } control_perms[] = { { "dumpstate",AID_SHELL, AID_LOG }, { "ril-daemon",AID_RADIO, AID_RADIO }, {NULL, 0, 0 } }; /* * Checks permissions for starting/stoping system services. * AID_SYSTEM and AID_ROOT are always allowed. * * Returns 1 if uid allowed, 0 otherwise. *权限检查的基本思路是:若进程的 uid 属于 system 或者 root,则通过;若进程在 control_perms *白名单中,也可以通过检查,但是目前为止,没有用户应用程序存在于该白名单中。因此只 *有具有 system 或者 root 权限才可以启动、停止以及重启服务。 */ 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; } //白名单二,AID定义在`system/core/include/private/android_filesystem_config.h` /* White list of permissions for setting property services. */ struct { const char *prefix; unsigned int uid; unsigned int gid; } 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.radio", AID_SYSTEM, 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 }, { "sys.powerctl", AID_SHELL, 0 }, { "service.", AID_SYSTEM, 0 }, { "wlan.", AID_SYSTEM, 0 }, { "bluetooth.", AID_BLUETOOTH, 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.msms.", AID_RADIO, 0 },//SPRD: add for dsds { "persist.msms.", AID_SYSTEM, 0 }, { "persist.service.", AID_SYSTEM, 0 }, { "persist.security.", AID_SYSTEM, 0 }, { "media.", AID_MEDIA, 0 }, { "persist.service.bdroid.", AID_BLUETOOTH, 0 }, { "selinux." , AID_SYSTEM, 0 }, { "persist.modem.", AID_RADIO, 0 },//SPRD: add for lte { NULL, 0, 0 } }; /* * Checks permissions for setting system properties. * Returns 1 if uid allowed, 0 otherwise. */ 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; //断是否具有 root 权限,若有通过检查 if (uid == 0) return check_mac_perms(name, sctx); app_id = multiuser_get_app_id(uid); if (app_id == AID_BLUETOOTH) { uid = app_id; } //与 check_control_perms 一样,property_perms 也是一个可以进行属性设置的白名单。若通过权限检查,则会调用 //property_set来进行属性设置操作,该函数在前文已有分析,此处略去 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; } //最终真正的设置属性的函数 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; pi = (prop_info*) __system_property_find(name); //获取系统中的prop if(pi != 0) { //如果已经存在该prop /* ro.* properties may NEVER be modified once set */ if(!strncmp(name, "ro.", 3)) return -1; //ro.开头的不允许修改,read only __system_property_update(pi, value, valuelen); //更新prop } else { //如果不存在,则添加 ret = __system_property_add(name, namelen, value, valuelen); //添加prop if (ret < 0) { ERROR("Failed to set '%s'='%s'\n", name, value); return ret; } } /* If name starts with "net." treat as a DNS property. */ //如果key是以net.开关的,当作DNS属性处理 if (strncmp("net.", name, strlen("net.")) == 0) { if (strcmp("net.change", name) == 0) { //如果是net.change属性,直接返回 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 &&strncmp("persist.", name, strlen("persist.")) == 0) { /* * Don't write properties to disk until after we have read all default properties * to prevent them from being overwritten by default values. */ //如果持久化属性已经加载完毕并且属性以persist.开头 write_persistent_property(name, value); } else if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) { //如果是selinux.reload_policy=1 selinux_reload_policy(); } property_changed(name, value); //prop改变,发出通知,定义在init.c中 return 0; } //判断prop name是否含有非法字符 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; //长度小于1非法 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; } //写入持久化prop,persist类型的prop要生成对应的文件,路径为/data/property static void write_persistent_property(const char *name, const char *value) { char tempPath[PATH_MAX]; char path[PATH_MAX]; int fd; snprintf(tempPath, sizeof(tempPath), "%s/.temp.XXXXXX", PERSISTENT_PROPERTY_DIR); fd = mkstemp(tempPath); //建立 临时文件 if (fd < 0) { ERROR("Unable to write persistent property to temp file %s errno: %d\n", tempPath, errno); return; } write(fd, value, strlen(value)); close(fd); snprintf(path, sizeof(path), "%s/%s", PERSISTENT_PROPERTY_DIR, name); if (rename(tempPath, path)) { unlink(tempPath); ERROR("Unable to rename persistent property file %s to %s\n", tempPath, path); } }
可以看到property_set函数又调用了__system_property_update,__system_property_add等函数,它们定义在
bionic/libc/bionic/system_properties.c
源码如下:
//更新属性 int __system_property_update(prop_info *pi, const char *value, unsigned int len) { prop_area *pa = __system_property_area__; //获取静态成员__system_property_area__,拿到共享内存区的地址 if (len >= PROP_VALUE_MAX) return -1; //prop_info->serial,其高8位表示该prop_info中name的长度,而低24位表示该prop_info被更新的次数 pi->serial = pi->serial | 1; ANDROID_MEMBAR_FULL(); memcpy(pi->value, value, len + 1); //赋值value ANDROID_MEMBAR_FULL(); pi->serial = (len << 24) | ((pi->serial + 1) & 0xffffff); __futex_wake(&pi->serial, INT32_MAX); pa->serial++; //访问次数加+,pa->serial表示该属性访问次数 __futex_wake(&pa->serial, INT32_MAX); return 0; } //添加prop int __system_property_add(const char *name, unsigned int namelen, const char *value, unsigned int valuelen) { prop_area *pa = __system_property_area__; const prop_info *pi; if (namelen >= PROP_NAME_MAX) //检查name长度 return -1; if (valuelen >= PROP_VALUE_MAX) //检查value长度 return -1; if (namelen < 1) return -1; //查询prop应该存储的内存位置 pi = find_property(root_node(), name, namelen, value, valuelen, true); if (!pi) return -1; pa->serial++; //访问次数加+,pa->serial表示该属性访问次数 __futex_wake(&pa->serial, INT32_MAX); return 0; } //前面介绍过了,属性的存储结构为特里结构+二叉树结构,所以查找属性其实就是 //特里结构+二叉树结构的遍历 static const prop_info *find_property(prop_bt *trie, const char *name, uint8_t namelen, const char *value, uint8_t valuelen, bool alloc_if_needed) { const char *remaining_name = name; while (true) { char *sep = strchr(remaining_name, '.'); bool want_subtree = (sep != NULL); uint8_t substr_size; prop_bt *root; if (want_subtree) { substr_size = sep - remaining_name; } else { substr_size = strlen(remaining_name); } if (!substr_size) return NULL; if (trie->children) { root = to_prop_obj(trie->children); } else if (alloc_if_needed) { root = new_prop_bt(remaining_name, substr_size, &trie->children); } else { root = NULL; } if (!root) return NULL; trie = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed); if (!trie) return NULL; if (!want_subtree) break; remaining_name = sep + 1; } if (trie->prop) { return to_prop_obj(trie->prop); } else if (alloc_if_needed) { return new_prop_info(name, namelen, value, valuelen, &trie->prop); } else { return NULL; } }
AID用户定义
system/core/include/private/android_filesystem_config.h
/* This is the master Users and Groups config for the platform. * DO NOT EVER RENUMBER */ #define AID_ROOT 0 /* traditional unix root user */ #define AID_SYSTEM 1000 /* system server */ #define AID_RADIO 1001 /* telephony subsystem, RIL */ #define AID_BLUETOOTH 1002 /* bluetooth subsystem */ #define AID_GRAPHICS 1003 /* graphics devices */ #define AID_INPUT 1004 /* input devices */ #define AID_AUDIO 1005 /* audio devices */ #define AID_CAMERA 1006 /* camera devices */ #define AID_LOG 1007 /* log devices */ #define AID_COMPASS 1008 /* compass device */ #define AID_MOUNT 1009 /* mountd socket */ #define AID_WIFI 1010 /* wifi subsystem */ #define AID_ADB 1011 /* android debug bridge (adbd) */ #define AID_INSTALL 1012 /* group for installing packages */ #define AID_MEDIA 1013 /* mediaserver process */ #define AID_DHCP 1014 /* dhcp client */ #define AID_SDCARD_RW 1015 /* external storage write access */ #define AID_VPN 1016 /* vpn system */ #define AID_KEYSTORE 1017 /* keystore subsystem */ #define AID_USB 1018 /* USB devices */ #define AID_DRM 1019 /* DRM server */ #define AID_MDNSR 1020 /* MulticastDNSResponder (service discovery) */ #define AID_GPS 1021 /* GPS daemon */ #define AID_UNUSED1 1022 /* deprecated, DO NOT USE */ #define AID_MEDIA_RW 1023 /* internal media storage write access */ #define AID_MTP 1024 /* MTP USB driver access */ #define AID_UNUSED2 1025 /* deprecated, DO NOT USE */ #define AID_DRMRPC 1026 /* group for drm rpc */ #define AID_NFC 1027 /* nfc subsystem */ #define AID_SDCARD_R 1028 /* external storage read access */ #define AID_CLAT 1029 /* clat part of nat464 */ #define AID_LOOP_RADIO 1030 /* loop radio devices */ #define AID_MEDIA_DRM 1031 /* MediaDrm plugins */ #define AID_PACKAGE_INFO 1032 /* access to installed package details */ #define AID_SDCARD_PICS 1033 /* external storage photos access */ #define AID_SDCARD_AV 1034 /* external storage audio/video access */ #define AID_SDCARD_ALL 1035 /* access all users external storage */ /* SPRD: add for storage manager @{ */ #define AID_INTERNAL_WR 1051 /* write permission for internal storage */ #define AID_EXTERNAL_WR 1052 /* write permission for external storage */ /* @} */ #define AID_SHELL 2000 /* adb and debug shell user */ #define AID_CACHE 2001 /* cache access */ #define AID_DIAG 2002 /* access to diagnostic resources */ /* The 3000 series are intended for use as supplemental group id's only. * They indicate special Android capabilities that the kernel is aware of. */ #define AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */ #define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm or l2cap sockets */ #define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */ #define AID_NET_RAW 3004 /* can create raw INET sockets */ #define AID_NET_ADMIN 3005 /* can configure interfaces and routing tables. */ #define AID_NET_BW_STATS 3006 /* read bandwidth statistics */ #define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */ #define AID_NET_BT_STACK 3008 /* bluetooth: access config files */ #define AID_THEME 9001 /* SPRD: add for theme setting */ #define AID_MISC 9998 /* access to misc storage */ #define AID_NOBODY 9999 #define AID_APP 10000 /* first app user */ #define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */ #define AID_ISOLATED_END 99999 /* end of uids for fully isolated sandboxed processes */ #define AID_USER 100000 /* offset for uid ranges for each user */ #define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */ #define AID_SHARED_GID_END 59999 /* start of gids for apps in each user to share */
需要重点关注的几个:
#define AID_ROOT 0 /* traditional unix root user */ #define AID_SYSTEM 1000 /* system server */ #define AID_RADIO 1001 /* telephony subsystem, RIL */ #define AID_USER 100000 /* offset for uid ranges for each user */ #define AID_USER 100000 /* offset for uid ranges for each user */
通过上述分析可知,Android 属性设置最终需要通过 init 进程完成,同时还有严格的权限检查,一般用户进程无法对 Android 属性进行设置。
四、上图
五、参考
Android属性系统
Linux 内存映射函数 mmap
trie-特里结构
Linux 内存映射函数 mmap()函数详解