Android 12 init 属性服务
Posted pecuyu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 12 init 属性服务相关的知识,希望对你有一定的参考价值。
文章托管在gitee上 Android Notes , 同步csdn
本文基于android12 分析
在 init 的启动第二阶段,启动属性服务线程,提供相关属性服务,给其他进程提供设置属性的支持,并通知init去处理属性事件。
SecondStageMain
/// system/core/init/init.cpp
int SecondStageMain(int argc, char** argv)
...
PropertyInit(); // 属性环境相关初始化,及固有属性加载
...
StartPropertyService(&property_fd); // 启动属性服务
...
PropertyInit
/// @system/core/init/property_service.cpp
void PropertyInit()
selinux_callback cb;
cb.func_audit = PropertyAuditCallback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH); // 创建/dev/__properties__目录,权限drwx--x--x
CreateSerializedPropertyInfo(); // 创建property se contexts
if (__system_property_area_init()) // 将 /dev/__properties__/properties_serial 映射到内存, 创建 ContextNodes,映射节点
LOG(FATAL) << "Failed to initialize property area";
if (!property_info_area.LoadDefaultPath()) // 加载/dev/__properties__/property_info 映射进内存
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(); // 解析 /proc/device-tree/firmware/android/
ProcessKernelCmdline(); // 解析 /proc/cmdline , 将其中键值对满足key为androidboot.* 的,将key替换为ro.boot.*,然后添加到属性
ProcessBootconfig(); // 解析 /proc/bootconfig ,同上
// Propagate the kernel variables to internal variables
// used by init as well as the current required properties.
ExportKernelBootProps(); // 给一些kernel属性初始赋值,如果没有设置的话
// 加载默认的属性文件的属性
// 如 /system/build.prop /vendor/default.prop /vendor/build.prop
// 还会解析其他分区 如 odm、product、system_ext
PropertyLoadBootDefaults();
CreateSerializedPropertyInfo
从相关property_contexts文件读取到context信息,并将内容序列化,然后写入 /dev/properties/property_info
void CreateSerializedPropertyInfo()
auto property_infos = std::vector<PropertyInfoEntry>();
// 从 selinux 的相关 property context 解析属性上下文信息,并存入 property_infos
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("/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);
else
if (!LoadPropertyInfoFromFile("/plat_property_contexts", &property_infos))
return;
LoadPropertyInfoFromFile("/system_ext_property_contexts", &property_infos);
if (!LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos))
// Fallback to nonplat_* if vendor_* doesn't exist.
LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
LoadPropertyInfoFromFile("/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)) // 写入property_info
PLOG(ERROR) << "Unable to write serialized property infos to file";
selinux_android_restorecon(kPropertyInfosPath, 0);
PropertyInfoEntry 定义如下,它用来存储解析后的context信息
/// @system/core/property_service/libpropertyinfoserializer/include/property_info_serializer/property_info_serializer.h
struct PropertyInfoEntry
PropertyInfoEntry()
template <typename T, typename U, typename V>
PropertyInfoEntry(T&& name, U&& context, V&& type, bool exact_match)
: name(std::forward<T>(name)),
context(std::forward<U>(context)),
type(std::forward<V>(type)),
exact_match(exact_match)
std::string name;
std::string context;
std::string type;
bool exact_match;
;
查看 /system/etc/selinux/plat_property_contexts 其中一行,对应关系如下
// name context exact_match type
sys.shutdown.requested u:object_r:exported_system_prop:s0 exact string
__system_property_area_init
将 /dev/properties/properties_serial 映射到内存, 创建 ContextNodes 流程
#define PROP_FILENAME "/dev/__properties__"
/// @bionic/libc/bionic/system_property_api.cpp
__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;
SystemProperties::AreaInit
创建 ContextsSerialized 并初始化
/// @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); // filename 是 /dev/__properties__
contexts_ = new (contexts_data_) ContextsSerialized();
// 初始化 ContextsSerialized
if (!contexts_->Initialize(true, property_filename_, fsetxattr_failed))
return false;
initialized_ = true;
return true;
ContextsSerialized::Initialize
第一个参数 writable 为 true, filename 为 /dev/properties
/// @bionic/libc/system_properties/contexts_serialized.cpp
bool ContextsSerialized::Initialize(bool writable, const char* filename, bool* fsetxattr_failed)
filename_ = filename; // 此处传入的是 /dev/__properties__
if (!InitializeProperties()) // 初始化属性
return false;
if (writable)
mkdir(filename_, S_IRWXU | S_IXGRP | S_IXOTH);
bool open_failed = false;
if (fsetxattr_failed)
*fsetxattr_failed = false;
// 遍历 ContextNode 数组,将各个节点映射到内存
for (size_t i = 0; i < num_context_nodes_; ++i)
if (!context_nodes_[i].Open(true, fsetxattr_failed)) // 打开各个节点,并映射到内存,注意此处的true 表示access_rw
open_failed = true;
if (open_failed || !MapSerialPropertyArea(true, fsetxattr_failed)) // /dev/__properties__/properties_serial 映射到内存
FreeAndUnmap();
return false;
else
if (!MapSerialPropertyArea(false, nullptr))
FreeAndUnmap();
return false;
return true;
InitializeProperties
/// @bionic/libc/system_properties/contexts_serialized.cpp
bool ContextsSerialized::InitializeProperties()
if (!property_info_area_file_.LoadDefaultPath()) // 加载 property_info
return false;
if (!InitializeContextNodes()) // 创建 context 节点集合
FreeAndUnmap();
return false;
return true;
/// 加载 /dev/__properties__/property_info
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) // 获取fstat
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;
InitializeContextNodes
根据context信息创建对应的 ContextNode
/// @bionic/libc/system_properties/include/system_properties/contexts_serialized.h
android::properties::PropertyInfoAreaFile property_info_area_file_;// property info 映射文件
ContextNode* context_nodes_ = nullptr;
size_t num_context_nodes_ = 0;
size_t context_nodes_mmap_size_ = 0;
/// @bionic/libc/system_properties/contexts_serialized.cpp
bool ContextsSerialized::InitializeContextNodes()
auto num_context_nodes = property_info_area_file_->num_contexts();
auto context_nodes_mmap_size = sizeof(ContextNode) * num_context_nodes;
// We want to avoid malloc in system properties, so we take an anonymous map instead (b/31659220).
void* const map_result = mmap(nullptr, context_nodes_mmap_size, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); // 映射一个所有节点总大小空间
if (map_result == MAP_FAILED)
return false;
prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, map_result, context_nodes_mmap_size,
"System property context nodes");
context_nodes_ = reinterpret_cast<ContextNode*>(map_result);
num_context_nodes_ = num_context_nodes;
context_nodes_mmap_size_ = context_nodes_mmap_size;
// 根据每个context创建对应的 ContextNode,并存入 context_nodes_,filename_ 是Initialize函数传入的 /dev/__properties__
for (size_t i = 0; i < num_context_nodes; ++i)
new (&context_nodes_[i]) ContextNode(property_info_area_file_->context(i), filename_);
return true;
创建每个节点后,接下来是调用它的Open函数
ContextNode::Open
从上面可以知道,init中 access_rw 传入的是 true
/// @bionic/libc/system_properties/context_node.cpp
bool ContextNode::Open(bool access_rw, bool* fsetxattr_failed)
lock_.lock();
if (pa_)
lock_.unlock();
return true;
char filename[PROP_FILENAME_MAX];
// 此处构建的是 filename_/context_ , 比如 /dev/__properties__/u:object_r:vold_prop:s0
int len = async_safe_format_buffer(filename, sizeof(filename), "%s/%s", filename_, context_);
if (len < 0 || len >= PROP_FILENAME_MAX)
lock_.unlock();
return false;
if (access_rw) // 上面传入的是access_rw=true,调用map_prop_area_rw
pa_ = prop_area::map_prop_area_rw(filename, context_, fsetxattr_failed); // 映射并记录地址,设置context
else
pa_ = prop_area::map_prop_area(filename);
lock_.unlock();
return pa_;
map_prop_area_rw 函数打开相关文件,然后将其映射到内存,返回内存地址指针。
/// @bionic/libc/system_properties/prop_area.cpp
prop_area* prop_area::map_prop_area_rw(const char* filename, const char* context,
bool* fsetxattr_failed)
/* dev is a tmpfs that we can use to carve a shared workspace
* out of, so let's do that...
*/ // 注意此处rwx权限是 444 只读, 因此只有 init 才有权限去写
const int fd = open(filename, O_RDWR | O_CREAT | O_NOFOLLOW | O_CLOEXEC | O_EXCL, 0444);
if (fd < 0)
if (errno == EACCES)
/* for consistency with the case where the process has already
* mapped the page in and segfaults when trying to write to it
*/
abort();
return nullptr;
if (context) // 设置文件的context,比如 u:object_r:vold_prop:s0
if (fsetxattr(fd, XATTR_NAME_SELINUX, context, strlen(context) + 1, 0) != 0)
async_safe_format_log(ANDROID_LOG_ERROR, "libc",
"fsetxattr failed to set context (%s) for \\"%s\\"", context, filename);
/*
* fsetxattr() will fail during system properties tests due to selinux policy.
* We do not want to create a custom policy for the tester, so we will continue in
* this function but set a flag that an error has occurred.
* Init, which is the only daemon that should ever call this function will abort
* when this error occurs.
* Otherwise, the tester will ignore it and continue, albeit without any selinux
* property separation.
*/
if (fsetxattr_failed)
*fsetxattr_failed = true;
if (ftruncate(fd, PA_SIZE) < 0)
close(fd);
return nullptr;
pa_size_ = PA_SIZE;
pa_data_size_ = pa_size_ - sizeof(prop_area);
// 映射到内存,具有读写权限
void* const memory_area = mmap(nullptr, pa_size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (memory_area == MAP_FAILED)
close(fd);
return nullptr;
prop_area* pa = new (memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION);
close(fd);
return pa;
MapSerialPropertyArea
最后将 /dev/__properties__/properties_serial 映射到内存,作为存储 context为 u:object_r:properties_serial:s0 的属性的文件
/// @bionic/libc/system_properties/contexts_serialized.cpp
bool ContextsSerialized::MapSerialPropertyArea(bool access_rw, bool* fsetxattr_failed)
char filename[PROP_FILENAME_MAX];
int len = async_safe_format_buffer(filename, sizeof(filename), "%s/properties_serial", filename_);
以上是关于Android 12 init 属性服务的主要内容,如果未能解决你的问题,请参考以下文章