在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); //1

CreateSerializedPropertyInfo();//2

if (__system_property_area_init()) {//3

LOG(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(); //4

ProcessKernelCmdline();//5

// Propagate the kernel variables to internal variables

// used by init as well as the current required properties.

ExportKernelBootProps();//6

PropertyLoadBootDefaults();//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();

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(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 default

std::string android_dt_dir = kDefaultAndroidDtDir;

// The platform may specify a custom Android DT path in kernel cmdline

ImportKernelCmdline([&](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 function

static 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

相关链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: