基于 Android 6.0 的源碼,分析系統(tǒng)屬性的初始化以及存儲。
上一篇結(jié)尾有提到屬性系統(tǒng)的框架,本文就針對系統(tǒng)屬性的初始化以及存儲分析一波

說到屬性系統(tǒng)就不得不提一下 init 進(jìn)程,ini t是 Linux 系統(tǒng)中用戶空間的第一個進(jìn)程,進(jìn)程號為1。Kernel 啟動后,在用戶空間,啟動 init 進(jìn)程,并調(diào)用 init 中的 main() 方法執(zhí)行 init 進(jìn)程的職責(zé)。對于 init 進(jìn)程的功能分為4部分:
- 分析和運行所有的 init.rc 文件;
- 生成設(shè)備驅(qū)動節(jié)點; (通過rc文件創(chuàng)建)
- 處理子進(jìn)程的終止(signal 方式);
- 提供屬性服務(wù)
我們就從 init 的 main() 方法開始 (已省略部分與系統(tǒng)屬性無關(guān)的代碼)
syste/core/init/init.cpp
int main(int argc, char** argv) {
......
// 創(chuàng)建一塊共享的內(nèi)存空間,用于屬性服務(wù)
property_init();
......
// 加載default.prop文件
property_load_boot_defaults();
// 啟動屬性系統(tǒng)服務(wù)(通過Socket通訊)
start_property_service();
......
// 解析 init.rc 文件
init_parse_config_file("/init.rc");
......
// 根據(jù)屬性的當(dāng)前狀態(tài)運行所有屬性觸發(fā)器。
queue_builtin_action(queue_property_triggers_action, "queue_property_triggers");
......
while (true) {
......
epoll_event ev;
//循環(huán) 等待事件發(fā)生
int nr = TEMP_FAILURE_RETRY(epoll_wait(epoll_fd, &ev, 1, timeout));
if (nr == -1) {
ERROR("epoll_wait failed: %s\n", strerror(errno));
} else if (nr == 1) {
((void (*)()) ev.data.ptr)();
}
}
return 0;
}
加載default.prop文件
syste/core/init/property_service.cpp
void property_load_boot_defaults() {
// PROP_PATH_RAMDISK_DEFAULT = "/default.prop"
load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);
}
/*
* Filter is used to decide which properties to load: NULL loads all keys,
* "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
*/
static void load_properties_from_file(const char* filename, const char* filter) {
Timer t;
std::string data;
// 讀取 filename 對應(yīng)文件內(nèi)容到 data
if (read_file(filename, &data)) {
data.push_back('\n');
// 解析 data
load_properties(&data[0], filter);
}
NOTICE("(Loading properties from %s took %.2fs.)\n", filename, t.duration());
}
/*
* Filter is used to decide which properties to load: NULL loads all keys,
* "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
*/
static void load_properties(char *data, const char *filter)
{
char *key, *value, *eol, *sol, *tmp, *fn;
size_t flen = 0;
if (filter) {
flen = strlen(filter);
}
sol = data;
// 逐行解析
while ((eol = strchr(sol, '\n'))) {
key = sol;
*eol++ = 0;
sol = eol;
while (isspace(*key)) key++;
// 如果是以 '#' 開頭則跳過
if (*key == '#') continue;
tmp = eol - 2;
while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;
// 如果是以 import 開頭則繼續(xù)解析該文件
if (!strncmp(key, "import ", 7) && flen == 0) {
fn = key + 7;
while (isspace(*fn)) fn++;
key = strchr(fn, ' ');
if (key) {
*key++ = 0;
while (isspace(*key)) key++;
}
// 解析被 import 的文件
load_properties_from_file(fn, key);
} else {
value = strchr(key, '=');
if (!value) continue;
*value++ = 0;
tmp = value - 2;
while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;
while (isspace(*value)) value++;
if (flen > 0) {
if (filter[flen - 1] == '*') {
if (strncmp(key, filter, flen - 1)) continue;
} else {
if (strcmp(key, filter)) continue;
}
}
// 設(shè)置系統(tǒng)屬性
property_set(key, value);
}
}
}
系統(tǒng)性的保存
這里又有 property_set,前面的文章也講了設(shè)置系統(tǒng)屬性,不過那是客戶端,現(xiàn)在我們看一下服務(wù)端又做什么些什么。
syste/core/init/property_service.cpp
int property_set(const char* name, const char* value) {
int rc = property_set_impl(name, value);
if (rc == -1) {
ERROR("property_set(\"%s\", \"%s\") failed\n", name, value);
}
return rc;
}
static int property_set_impl(const char* name, const char* value) {
size_t namelen = strlen(name);
size_t valuelen = strlen(value);
// 合法性校驗
if (!is_legal_property_name(name, namelen)) return -1;
if (valuelen >= PROP_VALUE_MAX) return -1;
// 如果屬性的名稱等于“selinux.reload_policy”,并且前面給它設(shè)置的值等于1
if (strcmp("selinux.reload_policy", name) == 0 && strcmp("1", value) == 0) {
// 重新加載SEAndroid策略
if (selinux_reload_policy() != 0) {
ERROR("Failed to reload policy\n");
}
} else if (strcmp("selinux.restorecon_recursive", name) == 0 && valuelen > 0) {
// 恢復(fù)SELinux文件屬性即恢復(fù)文件的安全上下文
if (restorecon_recursive(value) != 0) {
ERROR("Failed to restorecon_recursive %s\n", value);
}
}
// 從屬性內(nèi)存區(qū)域中查詢名稱為name的屬性
prop_info* pi = (prop_info*) __system_property_find(name);
// 如果查詢結(jié)果不為空,即已經(jīng)存在
if(pi != 0) {
/* ro.* properties may NEVER be modified once set */
// 如果 name 以 'ro.' 開頭則直接返回
if(!strncmp(name, "ro.", 3)) return -1;
// 更新屬性
__system_property_update(pi, value, valuelen);
} else {
// 如果不存在,則添加屬性
int rc = __system_property_add(name, namelen, value, valuelen);
if (rc < 0) {
return rc;
}
}
/* If name starts with "net." treat as a DNS property. */
// 如果屬性的名稱是以“net.”開頭,但是又不等于“net.change”,那么就將名稱為“net.change”的屬性設(shè)置為1,表示網(wǎng)絡(luò)屬性發(fā)生了變化
if (strncmp("net.", name, strlen("net.")) == 0) {
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.
*/
property_set("net.change", name);
}
// 如果屬性的名稱是以“persist.”開頭,那么就表示該屬性應(yīng)該是持久化儲存到文件中去
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.
*/
write_persistent_property(name, value);
}
// 發(fā)送一個名稱為name的屬性發(fā)生了變化的通知。以便init進(jìn)程可以執(zhí)行在啟動腳本init.rc中配置的操作。
property_changed(name, value);
return 0;
}
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: %s\n", tempPath, strerror(errno));
return;
}
write(fd, value, strlen(value));
fsync(fd);
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);
}
}
函數(shù)property_set首先是調(diào)用函數(shù)__system_property_find檢查名稱為name的屬性是否已經(jīng)存在屬性內(nèi)存區(qū)域中:
- 如果存在的話,那么就會得到一個類型為prop_info的結(jié)構(gòu)體pi,表示接下來要做的是修改屬性。這時候就會調(diào)用我們在上面分析的函數(shù)update_prop_info進(jìn)指定的屬性進(jìn)行修改。
- 如果不存在的話,那么指針pi的值就會等于NULL。表示接下來要做的增加屬性。這時候只需要在屬性內(nèi)存區(qū)域的屬性值列表pa_info_array的最后增加一項即可。
增加或者修改完成屬性之后,還要進(jìn)行以下的檢查:
- 如果屬性的名稱是以“net.”開頭,但是又不等于“net.change”,那么就將名稱為“net.change”的屬性設(shè)置為1,表示網(wǎng)絡(luò)屬性發(fā)生了變化。
- 如果屬性的名稱是以“persist.”開頭,那么就表示該屬性應(yīng)該是持久化儲存到文件中去,因此就會調(diào)用函數(shù)write_persistent_property執(zhí)行這個操作,以便系統(tǒng)下次啟動后,可以將該屬性的初始值設(shè)置為系統(tǒng)上次關(guān)閉時的值。
- 如果屬性的名稱等于“selinux.reload_policy”,并且前面給它設(shè)置的值等于1,那么就表示要重新加載SEAndroid策略,這是通過調(diào)用函數(shù)selinux_reload_policy來實現(xiàn)的。
最后,函數(shù)property_set調(diào)用另外一個函數(shù)property_changed發(fā)送一個名稱為name的屬性發(fā)生了變化的通知。以便init進(jìn)程可以執(zhí)行在啟動腳本init.rc中配置的操作。
啟動屬性系統(tǒng)服務(wù)
屬性服務(wù)就是通過 socket 接收客戶端傳來的設(shè)置系統(tǒng)屬性的請求,
void start_property_service() {
property_set_fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
0666, 0, 0, NULL);
if (property_set_fd == -1) {
ERROR("start_property_service socket creation failed: %s\n", strerror(errno));
exit(1);
}
listen(property_set_fd, 8);
register_epoll_handler(property_set_fd, handle_property_set_fd);
}
static void handle_property_set_fd()
{
prop_msg msg;
int s;
int r;
struct ucred cr;
struct sockaddr_un addr;
socklen_t addr_size = sizeof(addr);
socklen_t cr_size = sizeof(cr);
char * source_ctx = NULL;
struct pollfd ufds[1];
const int timeout_ms = 2 * 1000; /* Default 2 sec timeout for caller to send property. */
int nr;
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;
}
ufds[0].fd = s;
ufds[0].events = POLLIN;
ufds[0].revents = 0;
nr = TEMP_FAILURE_RETRY(poll(ufds, 1, timeout_ms));
if (nr == 0) {
ERROR("sys_prop: timeout waiting for uid=%d to send property message.\n", cr.uid);
close(s);
return;
} else if (nr < 0) {
ERROR("sys_prop: error waiting for uid=%d to send property message: %s\n", cr.uid, strerror(errno));
close(s);
return;
}
r = TEMP_FAILURE_RETRY(recv(s, &msg, sizeof(msg), MSG_DONTWAIT));
if(r != sizeof(prop_msg)) {
ERROR("sys_prop: mis-match msg size received: %d expected: %zu: %s\n",
r, sizeof(prop_msg), strerror(errno));
close(s);
return;
}
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);
if(memcmp(msg.name,"ctl.",4) == 0) {
// Keep the old close-socket-early behavior when handling
// ctl.* properties.
close(s);
if (check_control_mac_perms(msg.value, 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, 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;
}
}
加載其他屬性
開頭有講到,init.cpp 的 main() 方法會去解析 init.rc。在 init.r 中,也會觸發(fā)加載系統(tǒng)屬性,代碼如下:
on late-init
trigger early-fs
trigger fs
trigger post-fs
trigger post-fs-data
# Load properties from /system/ + /factory after fs mount. Place
# this in another action so that the load will be scheduled after the prior
# issued fs triggers have completed.
trigger load_all_props_action
# Remove a file to wake up anything waiting for firmware.
trigger firmware_mounts_complete
trigger early-boot
trigger boot
# Load properties from /system/ + /factory after fs mount.
on load_all_props_action
load_all_props
start logd
start logd-reinit
load_all_props 會調(diào)用到 property_service.cpp 中的 load_all_props 方法。
void load_all_props() {
load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL);
load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL);
load_properties_from_file(PROP_PATH_FACTORY, "ro.*");
load_override_properties();
/* Read persistent properties after all default values have been loaded. */
load_persistent_properties();
load_recovery_id_prop();
}
#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"
屬性觸發(fā)器
參考:
http://blog.csdn.net/luoshengyang/article/details/38102011
http://blog.csdn.net/kc58236582/article/details/51939322