属性服务初始化
这个主要是初始化系统属性区域,加载内核和设备树的属性配置,并设置默认的启动属性。它是 Android 启动过程中非常关键的一部分,用于确保系统属性在启动时被正确加载和管理。这些属性会影响系统配置、启动行为、安全策略等各个方面:
加载并初始化了以下属性相关内容:
- /dev/properties/property_info 的初始化并映射至内存
- 加载内核设备树的属性信息
- 加载内核命令行的属性信息
- 加载启动配置的属性信息
- 加载系统启动的默认属性信息
- 属性服务初始化入口:
PropetyInit()


PropetyInit()代码:
c
void PropertyInit() {
// 设置selinux 日志回调
selinux_callback cb;
// 回调内容就是一些打印日志
cb.func_audit = PropertyAuditCallback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
// 创建属性目录
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
// 创建序列化的属性信息
CreateSerializedPropertyInfo();
// 属性共享内存初始化, 位于bionic/libc/system_properties
if (__system_property_area_init()) {
LOG(FATAL) << "Failed to initialize property area";
}
// 加载序列化属性信息的默认路径文件,并运行mmap 映射到内存,直接操作内存,效率更高
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();
// 处理来自命令行的属性
ProcessKernelCmdline();
// 处理来自boot 的属性
ProcessBootconfig();
// Propagate the kernel variables to internal variables
// used by init as well as the current required properties.
ExportKernelBootProps();
PropertyLoadBootDefaults();
}
- selinux 回调
c
// 设置selinux 日志回调
selinux_callback cb;
// 回调内容就是一些打印日志
cb.func_audit = PropertyAuditCallback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
static int PropertyAuditCallback(void* data, security_class_t /*cls*/, char* buf, size_t len) {
auto* d = reinterpret_cast<PropertyAuditData*>(data);
if (!d || !d->name || !d->cr) {
LOG(ERROR) << "AuditCallback invoked with null data arguments!";
return 0;
}
snprintf(buf, len, "property=%s pid=%d uid=%d gid=%d", d->name, d->cr->pid, d->cr->uid,
d->cr->gid);
return 0;
}
- 创建
/dev/__propertities__ 目录
c
// 创建属性目录
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
- 创建序列化的属性信息
CreateSerializedPropertyInfo
c
bool LoadPropertyInfoFromFile(const std::string& filename,
std::vector<PropertyInfoEntry>* property_infos) {
auto file_contents = std::string();
// 读取属性文件
if (!ReadFileToString(filename, &file_contents)) {
PLOG(ERROR) << "Could not read properties from '" << filename << "'";
return false;
}
auto errors = std::vector<std::string>{};
bool require_prefix_or_exact = SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__;
// 解析属性文件
ParsePropertyInfoFile(file_contents, require_prefix_or_exact, property_infos, &errors);
// Individual parsing errors are reported but do not cause a failed boot, which is what
// returning false would do here.
for (const auto& error : errors) {
LOG(ERROR) << "Could not read line from '" << filename << "': " << error;
}
return true;
}
// 获取不同分区及不同目录下的属性上下文文件并加载到 property_infos
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, since we don't always have all of these partitions.
// 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("/dev/selinux/apex_property_contexts", R_OK) != -1) {
LoadPropertyInfoFromFile("/dev/selinux/apex_property_contexts", &property_infos);
}
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 (access("/vendor/etc/selinux/vendor_property_contexts", R_OK) != -1) {
LoadPropertyInfoFromFile("/vendor/etc/selinux/vendor_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);
}
} else {
if (!LoadPropertyInfoFromFile("/plat_property_contexts", &property_infos)) {
return;
}
LoadPropertyInfoFromFile("/system_ext_property_contexts", &property_infos);
LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos);
LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
LoadPropertyInfoFromFile("/dev/selinux/apex_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;
}
// 将序列化的数据写入property_info 文件
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);
}
- 属性共享内存初始化
__system_property_area_init
c
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int __system_property_area_init() {
bool fsetxattr_failed = false;
return system_properties.AreaInit(PROP_FILENAME, &fsetxattr_failed) && !fsetxattr_failed ? 0 : -1;
}
// 属性共享内存初始化, 位于bionic/libc/system_properties
if (__system_property_area_init()) {
LOG(FATAL) << "Failed to initialize property area";
}
- 加载序列化属性信息的默认路径文件
property_info_area.LoadDefaultPath()
c
bool PropertyInfoAreaFile::LoadDefaultPath() {
return LoadPath("/dev/__properties__/property_info");
}
bool PropertyInfoAreaFile::LoadPath(const char* filename) {
int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
struct stat fd_stat;
if (fstat(fd, &fd_stat) < 0) {
close(fd);
return false;
}
if ((fd_stat.st_uid != 0) || (fd_stat.st_gid != 0) ||
((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) ||
(fd_stat.st_size < static_cast<off_t>(sizeof(PropertyInfoArea)))) {
close(fd);
return false;
}
auto mmap_size = fd_stat.st_size;
void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd, 0);
if (map_result == MAP_FAILED) {
close(fd);
return false;
}
auto property_info_area = reinterpret_cast<PropertyInfoArea*>(map_result);
if (property_info_area->minimum_supported_version() > 1 ||
property_info_area->size() != mmap_size) {
munmap(map_result, mmap_size);
close(fd);
return false;
}
close(fd);
mmap_base_ = map_result;
mmap_size_ = mmap_size;
return true;
}
// 加载序列化属性信息的默认路径文件,并运行mmap 映射到内存,直接操作内存,效率更高
if (!property_info_area.LoadDefaultPath()) {
LOG(FATAL) << "Failed to load serialized property info file";
}
- 处理来自内核设备树的参数
ProcessKernelDt
c
static void ProcessKernelDt() {
if (!is_android_dt_value_expected("compatible", "android,firmware")) {
return;
}
// 打开dt 目录
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;
// 读取文件内容:就是属性值存放到dt_file
android::base::ReadFileToString(file_name, &dt_file);
// 替换符号
std::replace(dt_file.begin(), dt_file.end(), ',', '.');
// 设置属性ro.boot.xxx
InitPropertySet("ro.boot."s + dp->d_name, dt_file);
}
}
- 处理来自命令行的属性
ProcessKernelCmdline()
c
uint32_t InitPropertySet(const std::string& name, const std::string& value) {
ucred cr = {.pid = 1, .uid = 0, .gid = 0};
std::string error;
auto result = HandlePropertySetNoSocket(name, value, kInitContext, cr, &error);
if (result != PROP_SUCCESS) {
LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
}
return result;
}
void ImportKernelCmdline(const std::function<void(const std::string&, const std::string&)>& fn) {
std::string cmdline;
android::base::ReadFileToString("/proc/cmdline", &cmdline);
for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
std::vector<std::string> pieces = android::base::Split(entry, "=");
if (pieces.size() == 2) {
fn(pieces[0], pieces[1]);
}
}
}
constexpr auto ANDROIDBOOT_PREFIX = "androidboot."sv;
static void ProcessKernelCmdline() {
ImportKernelCmdline([&](const std::string& key, const std::string& value) {
if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
}
});
}
- 处理来自boot 的属性
ProcessBootconfig()
c
static void ProcessBootconfig() {
ImportBootconfig([&](const std::string& key, const std::string& value) {
if (StartsWith(key, ANDROIDBOOT_PREFIX)) {
InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
}
});
}
- 导出内核变量
ExportKernelBootProps()
c
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);
}
}
- 加载默认启动属性
PropertyLoadBootDefaults()
c
void PropertyLoadBootDefaults() {
// We read the properties and their values into a map, in order to always allow properties
// loaded in the later property files to override the properties in loaded in the earlier
// property files, regardless of if they are "ro." properties or not.
std::map<std::string, std::string> properties;
if (IsRecoveryMode()) {
if (auto res = load_properties_from_file("/prop.default", nullptr, &properties);
!res.ok()) {
LOG(ERROR) << res.error();
}
}
// /<part>/etc/build.prop is the canonical location of the build-time properties since S.
// Falling back to /<part>/defalt.prop and /<part>/build.prop only when legacy path has to
// be supported, which is controlled by the support_legacy_path_until argument.
const auto load_properties_from_partition = [&properties](const std::string& partition,
int support_legacy_path_until) {
auto path = "/" + partition + "/etc/build.prop";
if (load_properties_from_file(path.c_str(), nullptr, &properties).ok()) {
return;
}
// To read ro.<partition>.build.version.sdk, temporarily load the legacy paths into a
// separate map. Then by comparing its value with legacy_version, we know that if the
// partition is old enough so that we need to respect the legacy paths.
std::map<std::string, std::string> temp;
auto legacy_path1 = "/" + partition + "/default.prop";
auto legacy_path2 = "/" + partition + "/build.prop";
load_properties_from_file(legacy_path1.c_str(), nullptr, &temp);
load_properties_from_file(legacy_path2.c_str(), nullptr, &temp);
bool support_legacy_path = false;
auto version_prop_name = "ro." + partition + ".build.version.sdk";
auto it = temp.find(version_prop_name);
if (it == temp.end()) {
// This is embarassing. Without the prop, we can't determine how old the partition is.
// Let's be conservative by assuming it is very very old.
support_legacy_path = true;
} else if (int value;
ParseInt(it->second.c_str(), &value) && value <= support_legacy_path_until) {
support_legacy_path = true;
}
if (support_legacy_path) {
// We don't update temp into properties directly as it might skip any (future) logic
// for resolving duplicates implemented in load_properties_from_file. Instead, read
// the files again into the properties map.
load_properties_from_file(legacy_path1.c_str(), nullptr, &properties);
load_properties_from_file(legacy_path2.c_str(), nullptr, &properties);
} else {
LOG(FATAL) << legacy_path1 << " and " << legacy_path2 << " were not loaded "
<< "because " << version_prop_name << "(" << it->second << ") is newer "
<< "than " << support_legacy_path_until;
}
};
// Order matters here. The more the partition is specific to a product, the higher its
// precedence is.
LoadPropertiesFromSecondStageRes(&properties);
// system should have build.prop, unlike the other partitions
if (auto res = load_properties_from_file("/system/build.prop", nullptr, &properties);
!res.ok()) {
LOG(WARNING) << res.error();
}
load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);
load_properties_from_file("/system_dlkm/etc/build.prop", nullptr, &properties);
// TODO(b/117892318): uncomment the following condition when vendor.imgs for aosp_* targets are
// all updated.
// if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {
load_properties_from_file("/vendor/default.prop", nullptr, &properties);
// }
load_properties_from_file("/vendor/build.prop", nullptr, &properties);
load_properties_from_file("/vendor_dlkm/etc/build.prop", nullptr, &properties);
load_properties_from_file("/odm_dlkm/etc/build.prop", nullptr, &properties);
load_properties_from_partition("odm", /* support_legacy_path_until */ 28);
load_properties_from_partition("product", /* support_legacy_path_until */ 30);
if (access(kDebugRamdiskProp, R_OK) == 0) {
LOG(INFO) << "Loading " << kDebugRamdiskProp;
if (auto res = load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
!res.ok()) {
LOG(WARNING) << res.error();
}
}
for (const auto& [name, value] : properties) {
std::string error;
if (PropertySetNoSocket(name, value, &error) != PROP_SUCCESS) {
LOG(ERROR) << "Could not set '" << name << "' to '" << value
<< "' while loading .prop files" << error;
}
}
property_initialize_ro_product_props();
property_initialize_build_id();
property_derive_build_fingerprint();
property_derive_legacy_build_fingerprint();
property_initialize_ro_cpu_abilist();
property_initialize_ro_vendor_api_level();
update_sys_usb_config();
}
总结
