在init进程启动的第二阶段,调用PropertyInit 对属性系统进行初始化
int SecondStageMain(int argc, char** argv) {//省略PropertyInit();//省略
}
PropertyInit函数在system\core\init\property_service.cpp 中实现
void PropertyInit() {//省略mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH); //1CreateSerializedPropertyInfo();//2if (__system_property_area_init()) {//3LOG(FATAL) << "Failed to initialize property area";}if (!property_info_area.LoadDefaultPath()) {LOG(FATAL) << "Failed to load serialized property info file";}// If arguments are passed both on the command line and in DT,// properties set in DT always have priority over the command-line ones.ProcessKernelDt(); //4ProcessKernelCmdline();//5// Propagate the kernel variables to internal variables// used by init as well as the current required properties.ExportKernelBootProps();//6PropertyLoadBootDefaults();//7
}
注释1处在dev下创建__properties__文件夹。注释2处会收集读取各个分区下的property_contexts文件,将读取到的信息系列化之后,写到/dev/properties/property_info文件中。注释3处会读取/dev/properties/property_info文件构建ContextNode 数组,并在/dev/__properties__目录下,创建属性文件.注释4处处理内核dts中的属性信息。注释5处理cmdline中的属性信息。注释6导出一些属性给另外的属性赋值。注释7加载系统默认的属性文件。
接下来一项一项的来分析
CreateSerializedPropertyInfo
void CreateSerializedPropertyInfo() {auto property_infos = std::vector<PropertyInfoEntry>();if (access("/system/etc/selinux/plat_property_contexts", R_OK) != -1) {if (!LoadPropertyInfoFromFile("/system/etc/selinux/plat_property_contexts",&property_infos)) {return;}// Don't check for failure here, so we always have a sane list of properties.// E.g. In case of recovery, the vendor partition will not have mounted and we// still need the system / platform properties to function.if (access("/system_ext/etc/selinux/system_ext_property_contexts", R_OK) != -1) {LoadPropertyInfoFromFile("/system_ext/etc/selinux/system_ext_property_contexts",&property_infos);}if (!LoadPropertyInfoFromFile("/vendor/etc/selinux/vendor_property_contexts",&property_infos)) {// Fallback to nonplat_* if vendor_* doesn't exist.LoadPropertyInfoFromFile("/vendor/etc/selinux/nonplat_property_contexts",&property_infos);}if (access("/product/etc/selinux/product_property_contexts", R_OK) != -1) {LoadPropertyInfoFromFile("/product/etc/selinux/product_property_contexts",&property_infos);}if (access("/odm/etc/selinux/odm_property_contexts", R_OK) != -1) {LoadPropertyInfoFromFile("/odm/etc/selinux/odm_property_contexts", &property_infos);}} //省略auto serialized_contexts = std::string();auto error = std::string();if (!BuildTrie(property_infos, "u:object_r:default_prop:s0", "string", &serialized_contexts,&error)) {LOG(ERROR) << "Unable to serialize property contexts: " << error;return;}constexpr static const char kPropertyInfosPath[] = "/dev/__properties__/property_info";if (!WriteStringToFile(serialized_contexts, kPropertyInfosPath, 0444, 0, 0, false)) {PLOG(ERROR) << "Unable to write serialized property infos to file";}selinux_android_restorecon(kPropertyInfosPath, 0);
- 初始化property_infos 动态数组
- 调用LoadPropertyInfoFromFile分别读取各目录下的property_contexts文件,将读取到的信息构建PropertyInfoEntry并放入property_infos 数组
- BuildTrie对数组进行系列化(构建字典树并对字典树进行TrieBuilerNode 对象系列化),
- 将系列化后的信息通过WriteStringToFile写进/dev/properties/property_info文件
__system_property_area_init
__system_property_area_init的实现在bionic\libc\bionic\system_property_api.cpp文件中
//#define PROP_FILENAME "/dev/__properties__"
int __system_property_area_init() {bool fsetxattr_failed = false;return system_properties.AreaInit(PROP_FILENAME, &fsetxattr_failed) && !fsetxattr_failed ? 0 : -1;
}
AreaInit函数实现在bionic\libc\system_properties\system_properties.cpp文件中
bool SystemProperties::AreaInit(const char* filename, bool* fsetxattr_failed) {if (strlen(filename) >= PROP_FILENAME_MAX) {return false;}strcpy(property_filename_, filename);contexts_ = new (contexts_data_) ContextsSerialized();if (!contexts_->Initialize(true, property_filename_, fsetxattr_failed)) {return false;}initialized_ = true;return true;
}
先初始化一个ContextsSerialized对象,然后调用其Initialize函数。注意第一个参数。为true时,则会创建所有的/dev/properties/property_info/u:object_r:*:s0属性安全上下文文件,并在mmap时具有可读写权限。为false时,则不会创建文件且在映射时,只有可读的权限。
接着看一下Initialize函数,实现在bionic\libc\system_properties\contexts_serialized.cpp文件中
bool ContextsSerialized::Initialize(bool writable, const char* filename, bool* fsetxattr_failed) {filename_ = filename;if (!InitializeProperties()) {return false;}if (writable) {mkdir(filename_, S_IRWXU | S_IXGRP | S_IXOTH);bool open_failed = false;if (fsetxattr_failed) {*fsetxattr_failed = false;}for (size_t i = 0; i < num_context_nodes_; ++i) {if (!context_nodes_[i].Open(true, fsetxattr_failed)) {open_failed = true;}}if (open_failed || !MapSerialPropertyArea(true, fsetxattr_failed)) { //映射具有读写权限FreeAndUnmap();return false;}} else {if (!MapSerialPropertyArea(false, nullptr)) { //映射只有读的权限FreeAndUnmap();return false;}}return true;
}
在InitializeProperties函数中,会加载/dev/properties/property_info文件,并映射一块内存,然后通过一个 for 循环,将这块内存初始化为一个 ContextNode 的数组结构,每个 ContextNode 对象中的信息保存了一个安全上下文信息和文件路径信息。
完成上面的工作之后,通过for循环,调用每个ContextNode 的Open函数,在Open函数中,会根据属性安全上下文创建属性文件,如dev/properties/u:object_r:hwservicemanager_prop:s0,并映射它,将映射的地址保存在ContextNode 中。需要说明的是,相同安全上下文的不同属性,都会存放在同一片共享内存中,内存的名字就是其安全上下文(比如属性的安全上下文名字为adbd_config_prop,则存放在dev/properties/u:object_r:adbd_config_prop:s0文件中)。
ls -lh dev/__properties__/
-r--r--r-- 1 root root 128K 2017-01-01 20:00 u:object_r:adbd_config_prop:s0
-r--r--r-- 1 root root 128K 2017-01-01 20:00 u:object_r:adbd_prop:s0
-r--r--r-- 1 root root 128K 2017-01-01 20:00 u:object_r:apexd_prop:s0
-r--r--r-- 1 root root 128K 2017-01-01 20:00 u:object_r:apk_verity_prop:s0
-r--r--r-- 1 root root 128K 2017-01-01 20:00 u:object_r:audio_prop:s0
ProcessKernelDt
static void ProcessKernelDt() {if (!is_android_dt_value_expected("compatible", "android,firmware")) {return;}std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);if (!dir) return;std::string dt_file;struct dirent* dp;while ((dp = readdir(dir.get())) != NULL) {if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") ||!strcmp(dp->d_name, "name")) {continue;}std::string file_name = get_android_dt_dir() + dp->d_name;android::base::ReadFileToString(file_name, &dt_file);std::replace(dt_file.begin(), dt_file.end(), ',', '.');InitPropertySet("ro.boot."s + dp->d_name, dt_file);}
}
首先检查compatible属性的值是"android,firmware"如果不是,函数直接返回。如果是的话,再打开目录下的dts文件,并读取其内容。其中 get_android_dt_dir是在cmdline中指定
static std::string init_android_dt_dir() {// Use the standard procfs-based path by defaultstd::string android_dt_dir = kDefaultAndroidDtDir;// The platform may specify a custom Android DT path in kernel cmdlineImportKernelCmdline([&](const std::string& key, const std::string& value) {if (key == "androidboot.android_dt_dir") {android_dt_dir = value;}});LOG(INFO) << "Using Android DT directory " << android_dt_dir;return android_dt_dir;
}// FIXME: The same logic is duplicated in system/core/fs_mgr/
const std::string& get_android_dt_dir() {// Set once and saves time for subsequent calls to this functionstatic const std::string kAndroidDtDir = init_android_dt_dir();return kAndroidDtDir;
}
比如androidboot.android_dt_dir目录下有cpu.dts和dispaly.dts,满足条件的话,则会将两个文件的内容分别设置ro.boot.cpu和ro.boot.dispaly
ProcessKernelCmdline
static void ProcessKernelCmdline() {bool for_emulator = false;ImportKernelCmdline([&](const std::string& key, const std::string& value) {if (key == "qemu") {for_emulator = true;} else if (StartsWith(key, "androidboot.")) {InitPropertySet("ro.boot." + key.substr(12), value);}});if (for_emulator) {ImportKernelCmdline([&](const std::string& key, const std::string& value) {// In the emulator, export any kernel option with the "ro.kernel." prefix.InitPropertySet("ro.kernel." + key, value);});}
}
解析cmdline中的数据设置其属性。如:androidboot.mode=normal 会转化成:ro.boot.mode=normal
ExportKernelBootProps
static void ExportKernelBootProps() {constexpr const char* UNSET = "";struct {const char* src_prop;const char* dst_prop;const char* default_value;} prop_map[] = {// clang-format off{ "ro.boot.serialno", "ro.serialno", UNSET, },{ "ro.boot.mode", "ro.bootmode", "unknown", },{ "ro.boot.baseband", "ro.baseband", "unknown", },{ "ro.boot.bootloader", "ro.bootloader", "unknown", },{ "ro.boot.hardware", "ro.hardware", "unknown", },{ "ro.boot.revision", "ro.revision", "0", },// clang-format on};for (const auto& prop : prop_map) {std::string value = GetProperty(prop.src_prop, prop.default_value);if (value != UNSET) InitPropertySet(prop.dst_prop, value);}
}
如果系统中有ro.boot.serialno这个属性,则将其值也设置给ro.serialno
PropertyLoadBootDefaults
加载系统默认的属性文件。参考初识Android 属性
总结
在初始化属性系统时,会加载各个分区的property_contexts文件,并进行TrieBuilerNode 对象系列化,然后将系列化的信息写入到dev/properties/propert_info文件中。根据该文件信息(name,安全上下文),在dev/properties/目录下创建内存文件,并进行映射,地址保存在ContextNode 中。创建的内存文件的名字是属性的安全上下文。ContextNode 和TrieBuilerNode 是一一对应的。内存创建好之后,还会根据dts,cmdline中的信息,生成属性。最后加载各个分区默认的属性文件。
以下为简略的逻辑图,每个内存文件为128k