文章托管在gitee上 Android Notes , 同步csdn
本文基于Android12 分析
在 init 的启动第二阶段,启动属性服务线程,提供相关属性服务,给其他进程提供设置属性的支持,并通知init去处理属性事件。
/// system/core/init/init.cpp
int SecondStageMain(int argc, char** argv) {
...
PropertyInit(); // 属性环境相关初始化,及固有属性加载
...
StartPropertyService(&property_fd); // 启动属性服务
...
}
/// @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();
}
从相关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
将 /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;
}
创建 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;
}
第一个参数 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;
}
/// @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;
}
根据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函数
从上面可以知道,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;
}
最后将 /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_);
if (len < 0 || len >= PROP_FILENAME_MAX) {
serial_prop_area_ = nullptr;
return false;
}
if (access_rw) {
serial_prop_area_ = // 映射到内存,并设置context为 u:object_r:properties_serial:s0
prop_area::map_prop_area_rw(filename, "u:object_r:properties_serial:s0", fsetxattr_failed);
} else {
serial_prop_area_ = prop_area::map_prop_area(filename);
}
return serial_prop_area_;
}
总结一下上面__system_property_area_init()流程:
到此 __system_property_area_init() 完成,创建了所有context对应的节点并映射到内存以及映射properties_serial,可以查看/dev/__properties__
# ls /dev/__properties__/ -lZ
total 1136
-r--r--r-- 1 root root u:object_r:properties_serial:s0 131072 2022-11-30 14:24 properties_serial
-r--r--r-- 1 root root u:object_r:property_info:s0 64956 2022-11-30 14:24 property_info
-r--r--r-- 1 root root u:object_r:aac_drc_prop:s0 131072 2022-11-30 14:24 u:object_r:aac_drc_prop:s0
-r--r--r-- 1 root root u:object_r:aaudio_config_prop:s0 131072 2022-11-30 14:24 u:object_r:aaudio_config_prop:s0
-r--r--r-- 1 root root u:object_r:ab_update_gki_prop:s0 131072 2022-11-30 14:24 u:object_r:ab_update_gki_prop:s0
-r--r--r-- 1 root root u:object_r:adbd_config_prop:s0 131072 2022-11-30 14:24 u:object_r:adbd_config_prop:s0
-r--r--r-- 1 root root u:object_r:adbd_prop:s0 131072 2022-11-30 14:24 u:object_r:adbd_prop:s0
-r--r--r-- 1 root root u:object_r:apexd_config_prop:s0 131072 2022-11-30 14:24 u:object_r:apexd_config_prop:s0
-r--r--r-- 1 root root u:object_r:apexd_prop:s0 131072 2022-11-30 14:24 u:object_r:apexd_prop:s0
...
加载 /dev/__properties__/property_info 并映射到内存,逻辑同之前 property_info_area_file_ 加载
bool PropertyInfoAreaFile::LoadDefaultPath() {
return LoadPath("/dev/__properties__/property_info");
}
/// @system/core/init/property_service.cpp
constexpr auto ANDROIDBOOT_PREFIX = "androidboot."sv;
static void ProcessKernelCmdline() {
// 加载 /proc/cmdline
ImportKernelCmdline([&](const std::string& key, const std::string& value) {
if (StartsWith(key, ANDROIDBOOT_PREFIX)) { // key 起始是 androidboot
// 将key起始位置替换为ro.boot, 然后设置新属性
InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
}
});
}
static void ProcessBootconfig() {
// 加载 /proc/bootconfig
ImportBootconfig([&](const std::string& key, const std::string& value) {
if (StartsWith(key, ANDROIDBOOT_PREFIX)) { // key 起始是 androidboot
// 将key起始位置替换为ro.boot, 然后设置新属性
InitPropertySet("ro.boot." + key.substr(ANDROIDBOOT_PREFIX.size()), value);
}
});
}
// 读取/proc/cmdline , 匹配回调 fn
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]);
}
}
}
// 读取/proc/bootconfig , 匹配回调 fn
void ImportBootconfig(const std::function<void(const std::string&, const std::string&)>& fn) {
std::string bootconfig;
android::base::ReadFileToString("/proc/bootconfig", &bootconfig);
for (const auto& entry : android::base::Split(bootconfig, "\n")) {
std::vector<std::string> pieces = android::base::Split(entry, "=");
if (pieces.size() == 2) {
// get rid of the extra space between a list of values and remove the quotes.
std::string value = android::base::StringReplace(pieces[1], "\", \"", ",", true);
value.erase(std::remove(value.begin(), value.end(), '"'), value.end());
fn(android::base::Trim(pieces[0]), android::base::Trim(value));
}
}
}
加载开机默认属性
/// @system/core/init/property_service.cpp
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()) {
load_properties_from_file("/prop.default", nullptr, &properties);
}
// //etc/build.prop is the canonical location of the build-time properties since S.
// Falling back to //defalt.prop and //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) { // 加载某个分区的lambda表达式
auto path = "/" + partition + "/etc/build.prop";
if (load_properties_from_file(path.c_str(), nullptr, &properties)) {
return;
}
// To read ro..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);
load_properties_from_file("/system/build.prop", nullptr, &properties); // 加载某个文件
load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30); // 加载某个分区的
// 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;
load_properties_from_file(kDebugRamdiskProp, nullptr, &properties);
}
for (const auto& [name, value] : properties) { // 将加载的属性键值对执行设置操作,保持到映射区
std::string error;
if (PropertySet(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();
update_sys_usb_config(); // 更新usb配置属性
}
update_sys_usb_config
// persist.sys.usb.config values can't be combined on build-time when property
// files are split into each partition.
// So we need to apply the same rule of build/make/tools/post_process_props.py
// on runtime.
static void update_sys_usb_config() {
bool is_debuggable = android::base::GetBoolProperty("ro.debuggable", false);
std::string config = android::base::GetProperty("persist.sys.usb.config", "");
// b/150130503, add (config == "none") condition here to prevent appending
// ",adb" if "none" is explicitly defined in default prop.
if (config.empty() || config == "none") {
InitPropertySet("persist.sys.usb.config", is_debuggable ? "adb" : "none");
} else if (is_debuggable && config.find("adb") == std::string::npos &&
config.length() + 4 < PROP_VALUE_MAX) {
config.append(",adb");
InitPropertySet("persist.sys.usb.config", config);
}
}
启动属性服务
/// @system/core/init/property_service.cpp
void StartPropertyService(int* epoll_socket) {
InitPropertySet("ro.property_service.version", "2");
int sockets[2];
// 创建 socket 对,用于init和属性服务间通信
if (socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sockets) != 0) {
PLOG(FATAL) << "Failed to socketpair() between property_service and init";
}
*epoll_socket = from_init_socket = sockets[0]; // 回传给init端
init_socket = sockets[1]; // 持有此fd端
StartSendingMessages(); // 设置 accept_messages = true , 表示可以处理请求消息
// 创建接收属性请求的 socket
if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
false, 0666, 0, 0, {});
result.ok()) {
property_set_fd = *result; // 记录此fd
} else {
LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
}
listen(property_set_fd, 8); // 监听
auto new_thread = std::thread{PropertyServiceThread}; // 创建新线程,调用PropertyServiceThread用于处理请求,
property_service_thread.swap(new_thread);
}
/// @system/core/init/property_service.cpp
static void PropertyServiceThread() {
Epoll epoll;
if (auto result = epoll.Open(); !result.ok()) { // 创建 epoll
LOG(FATAL) << result.error();
}
// 将属性设置fd注册到epoll,监听其相关事件
if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd);
!result.ok()) {
LOG(FATAL) << result.error();
}
// 注册init_socket到epoll,监听来自init相关事件
if (auto result = epoll.RegisterHandler(init_socket, HandleInitSocket); !result.ok()) {
LOG(FATAL) << result.error();
}
while (true) { // 循环处理事件
auto pending_functions = epoll.Wait(std::nullopt); // 等待事件发生
if (!pending_functions.ok()) {
LOG(ERROR) << pending_functions.error();
} else {
for (const auto& function : *pending_functions) {
(*function)();
}
}
}
}
当收到设置属性请求时,会回调 handle_property_set_fd 函数
/// @system/core/init/property_service.cpp
static void handle_property_set_fd() {
static constexpr uint32_t kDefaultSocketTimeout = 2000; /* ms */
int s = accept4(property_set_fd, nullptr, nullptr, SOCK_CLOEXEC);
if (s == -1) {
return;
}
ucred cr;
socklen_t cr_size = sizeof(cr);
if (getsockopt(s, SOL_SOCKET, SO_PEERCRED, &cr, &cr_size) < 0) {
close(s);
PLOG(ERROR) << "sys_prop: unable to get SO_PEERCRED";
return;
}
SocketConnection socket(s, cr);
uint32_t timeout_ms = kDefaultSocketTimeout;
uint32_t cmd = 0;
if (!socket.RecvUint32(&cmd, &timeout_ms)) {
PLOG(ERROR) << "sys_prop: error while reading command from the socket";
socket.SendUint32(PROP_ERROR_READ_CMD);
return;
}
switch (cmd) {
case PROP_MSG_SETPROP: {
char prop_name[PROP_NAME_MAX];
char prop_value[PROP_VALUE_MAX];
if (!socket.RecvChars(prop_name, PROP_NAME_MAX, &timeout_ms) ||
!socket.RecvChars(prop_value, PROP_VALUE_MAX, &timeout_ms)) {
PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP): error while reading name/value from the socket";
return;
}
prop_name[PROP_NAME_MAX-1] = 0;
prop_value[PROP_VALUE_MAX-1] = 0;
std::string source_context;
if (!socket.GetSourceContext(&source_context)) {
PLOG(ERROR) << "Unable to set property '" << prop_name << "': getpeercon() failed";
return;
}
const auto& cr = socket.cred();
std::string error;
uint32_t result =
HandlePropertySet(prop_name, prop_value, source_context, cr, nullptr, &error);
if (result != PROP_SUCCESS) {
LOG(ERROR) << "Unable to set property '" << prop_name << "' from uid:" << cr.uid
<< " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
}
break;
}
case PROP_MSG_SETPROP2: {
std::string name;
std::string value;
if (!socket.RecvString(&name, &timeout_ms) ||
!socket.RecvString(&value, &timeout_ms)) {
PLOG(ERROR) << "sys_prop(PROP_MSG_SETPROP2): error while reading name/value from the socket";
socket.SendUint32(PROP_ERROR_READ_DATA);
return;
}
std::string source_context;
if (!socket.GetSourceContext(&source_context)) {
PLOG(ERROR) << "Unable to set property '" << name << "': getpeercon() failed";
socket.SendUint32(PROP_ERROR_PERMISSION_DENIED);
return;
}
const auto& cr = socket.cred();
std::string error; // 调用 HandlePropertySet 设置属性
uint32_t result = HandlePropertySet(name, value, source_context, cr, &socket, &error);
if (result != PROP_SUCCESS) {
LOG(ERROR) << "Unable to set property '" << name << "' from uid:" << cr.uid
<< " gid:" << cr.gid << " pid:" << cr.pid << ": " << error;
}
socket.SendUint32(result);
break;
}
default:
LOG(ERROR) << "sys_prop: invalid command " << cmd;
socket.SendUint32(PROP_ERROR_INVALID_CMD);
break;
}
}
处理设置属性
/// @system/core/init/property_service.cpp
// This returns one of the enum of PROP_SUCCESS or PROP_ERROR*.
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr,
SocketConnection* socket, std::string* error) {
// 权限检查
if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) {
return ret;
}
if (StartsWith(name, "ctl.")) { // 处理 ctl 属性
return SendControlMessage(name.c_str() + 4, value, cr.pid, socket, error);
}
// sys.powerctl is a special property that is used to make the device reboot. We want to log
// any process that sets this property to be able to accurately blame the cause of a shutdown.
if (name == "sys.powerctl") { // 处理关机/重启请求
std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid); // 读取请求进程信息
std::string process_cmdline;
std::string process_log_string;
if (ReadFileToString(cmdline_path, &process_cmdline)) {
// Since cmdline is null deliminated, .c_str() conveniently gives us just the process
// path.
process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
}
LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
<< process_log_string;
if (!value.empty()) {
DebugRebootLogging();
}
if (value == "reboot,userspace" && !is_userspace_reboot_supported().value_or(false)) {
*error = "Userspace reboot is not supported by this device";
return PROP_ERROR_INVALID_VALUE;
}
}
// If a process other than init is writing a non-empty value, it means that process is
// requesting that init performs a restorecon operation on the path specified by 'value'.
// We use a thread to do this restorecon operation to prevent holding up init, as it may take
// a long time to complete.
if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) {
static AsyncRestorecon async_restorecon;
async_restorecon.TriggerRestorecon(value);
return PROP_SUCCESS;
}
return PropertySet(name, value, error); // 真正设置更新
}
/// @system/core/init/property_service.cpp
static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
size_t valuelen = value.size();
if (!IsLegalPropertyName(name)) { // 值合法性检查
*error = "Illegal property name";
return PROP_ERROR_INVALID_NAME;
}
if (auto result = IsLegalPropertyValue(name, value); !result.ok()) { // 值合法性检查
*error = result.error().message();
return PROP_ERROR_INVALID_VALUE;
}
prop_info* pi = (prop_info*) __system_property_find(name.c_str()); // 查找属性信息是否存在
if (pi != nullptr) {
// ro.* properties are actually "write-once".
if (StartsWith(name, "ro.")) { // 只读属性不能设置
*error = "Read-only property was already set";
return PROP_ERROR_READ_ONLY_PROPERTY;
}
__system_property_update(pi, value.c_str(), valuelen); // 更新
} else {// 添加属性
int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
if (rc < 0) {
*error = "__system_property_add failed";
return PROP_ERROR_SET_FAILED;
}
}
// Don't write properties to disk until after we have read all default
// properties to prevent them from being overwritten by default values.
if (persistent_properties_loaded && StartsWith(name, "persist.")) {
WritePersistentProperty(name, value); // 写入 persist 属性
}
// If init hasn't started its main loop, then it won't be handling property changed messages
// anyway, so there's no need to try to send them.
auto lock = std::lock_guard{accept_messages_lock};
if (accept_messages) {
PropertyChanged(name, value); //通知属性变化
}
return PROP_SUCCESS;
}
属性变化处理逻辑
/// @system/core/init/init.cpp
void PropertyChanged(const std::string& name, const std::string& value) {
// If the property is sys.powerctl, we bypass the event queue and immediately handle it.
// This is to ensure that init will always and immediately shutdown/reboot, regardless of
// if there are other pending events to process or if init is waiting on an exec service or
// waiting on a property.
// In non-thermal-shutdown case, 'shutdown' trigger will be fired to let device specific
// commands to be executed.
if (name == "sys.powerctl") { // 处理关机请求
trigger_shutdown(value);
}
if (property_triggers_enabled) {// 已经使能属性触发器
ActionManager::GetInstance().QueuePropertyChange(name, value); //添加到事件队列
WakeMainInitThread(); // 唤醒init主线程处理
}
prop_waiter_state.CheckAndResetWait(name, value); // 检查是否有设置等待此属性值
}
/// @system/core/init/action_manager.cpp
void ActionManager::QueuePropertyChange(const std::string& name, const std::string& value) {
auto lock = std::lock_guard{event_queue_lock_};
event_queue_.emplace(std::make_pair(name, value)); // 插入一个属性变化的事件
}
/// @system/core/init/init.cpp
static void WakeMainInitThread() {
uint64_t counter = 1; // 写入一个数据,唤醒对端
TEMP_FAILURE_RETRY(write(wake_main_thread_fd, &counter, sizeof(counter)));
}
// Init epolls various FDs to wait for various inputs. It previously waited on property changes
// with a blocking socket that contained the information related to the change, however, it was easy
// to fill that socket and deadlock the system. Now we use locks to handle the property changes
// directly in the property thread, however we still must wake the epoll to inform init that there
// is a change to process, so we use this FD. It is non-blocking, since we do not care how many
// times WakeMainInitThread() is called, only that the epoll will wake.
static int wake_main_thread_fd = -1;
static void InstallInitNotifier(Epoll* epoll) { // 在启动属性服务之前,调用来设置醒监听
wake_main_thread_fd = eventfd(0, EFD_CLOEXEC); // 初始化一个event fd
if (wake_main_thread_fd == -1) {
PLOG(FATAL) << "Failed to create eventfd for waking init";
}
auto clear_eventfd = [] { // epoll收到相关事件会回调此lambda
uint64_t counter; // 仅仅只是读出来,消费掉事件。主要目的是唤醒epoll
TEMP_FAILURE_RETRY(read(wake_main_thread_fd, &counter, sizeof(counter)));
};
// 注册到epoll 监听写事件
if (auto result = epoll->RegisterHandler(wake_main_thread_fd, clear_eventfd); !result.ok()) {
LOG(FATAL) << result.error();
}
}
int SecondStageMain(int argc, char** argv) {
...
// Restore prio before main loop
setpriority(PRIO_PROCESS, 0, 0);
while (true) {
// By default, sleep until something happens.
auto epoll_timeout = std::optional<std::chrono::milliseconds>{};
auto shutdown_command = shutdown_state.CheckShutdown();
if (shutdown_command) { // 处理关机请求
LOG(INFO) << "Got shutdown_command '" << *shutdown_command
<< "' Calling HandlePowerctlMessage()";
HandlePowerctlMessage(*shutdown_command);
shutdown_state.set_do_shutdown(false);
}
// 当没有要等待的属性或执行的服务 则从事件队列中取出一个执行
if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
}
if (!IsShuttingDown()) { // 不是正在关机, 如有需要重启的服务,需要据此重新计算超时时间
auto next_process_action_time = HandleProcessActions();
// If there's a process that needs restarting, wake up in time for that.
if (next_process_action_time) {
epoll_timeout = std::chrono::ceil<std::chrono::milliseconds>(
*next_process_action_time - boot_clock::now());
if (*epoll_timeout < 0ms) epoll_timeout = 0ms;
}
}
if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
// If there's more work to do, wake up again immediately.
if (am.HasMoreCommands()) epoll_timeout = 0ms; // 还有事件需要处理,超时为0,即里面处理下个事件
}
auto pending_functions = epoll.Wait(epoll_timeout); // 等待到新消息到来或超时
if (!pending_functions.ok()) {
LOG(ERROR) << pending_functions.error();
} else if (!pending_functions->empty()) { // 有待执行命令,比如唤醒要执行的回调 clear_eventfd
// We always reap children before responding to the other pending functions. This is to
// prevent a race where other daemons see that a service has exited and ask init to
// start it again via ctl.start before init has reaped it.
ReapAnyOutstandingChildren(); // 首先回收已退出的进程,回收僵尸进程
for (const auto& function : *pending_functions) { // 执行相关回调
(*function)();
}
}
if (!IsShuttingDown()) { // 不是正在关机,
HandleControlMessages(); // 处理 ctl 属性消息
SetUsbController();
}
}
return 0;
}
如上,当属性服务写数据时唤醒epoll,epoll从Wait函数返回,然后处理此事件,回调clear_eventfd来消耗掉此事件。由此主线程达到了唤醒的目的,然后下次循环,通过 am.ExecuteOneCommand 来取出属性事件对应的action来执行。
/// @system/core/init/action_manager.cpp
void ActionManager::ExecuteOneCommand() {
{
auto lock = std::lock_guard{event_queue_lock_};
// Loop through the event queue until we have an action to execute
while (current_executing_actions_.empty() && !event_queue_.empty()) { // 当前没有执行的action,但是有事件
for (const auto& action : actions_) { // 找到此事件对应要处理的action
if (std::visit([&action](const auto& event) { return action->CheckEvent(event); },
event_queue_.front())) {
current_executing_actions_.emplace(action.get()); // 一个事件可以对应多个 action
}
}
event_queue_.pop();
}
}
if (current_executing_actions_.empty()) {
return;
}
// 返回 queue 中第一个元素的引用,而不是删除
auto action = current_executing_actions_.front();
if (current_command_ == 0) { // 处理此action的第一条命令时打印
std::string trigger_name = action->BuildTriggersString();
LOG(INFO) << "processing action (" << trigger_name << ") from (" << action->filename()
<< ":" << action->line() << ")";
}
action->ExecuteOneCommand(current_command_); // 回调每个action匹配的命令
// If this was the last command in the current action, then remove
// the action from the executing list.
// If this action was oneshot, then also remove it from actions_.
++current_command_; // 每执行一次数量加1
if (current_command_ == action->NumCommands()) { // 如果此 action的命令全部执行完毕
current_executing_actions_.pop(); // 从正执行列表删除
current_command_ = 0;
if (action->oneshot()) { // 如果是一次性的 还有从action列表移除。 通常Builtin Action是一次性的
auto eraser = [&action](std::unique_ptr<Action>& a) { return a.get() == action; };
actions_.erase(std::remove_if(actions_.begin(), actions_.end(), eraser),
actions_.end());
}
}
}
接下来的流程和 init(2) rc脚本解析和事件执行流程 类似,就不在赘述了。
当设置属性 persist.traced.enable 为 1, 以下动作将会触发,会依次执行 mkdir命令和 start 命令。
/// perfetto.rc
on property:persist.traced.enable=1
# Trace files need to be:
# - Written by either uid:shell or uid:statsd.
# - Read by shell and incidentd.
mkdir /data/misc/perfetto-traces 0773 root shell
# Traces in this directory are only accessed by dumpstate (read+unlink) and
# by the bug reporting UI (ls+getattr).
mkdir /data/misc/perfetto-traces/bugreport 0773 root shell
# This directory allows shell to save configs file in a place where the
# perfetto cmdline client can read then. /data/local/tmp/ isn't safe because
# too many other domains can write into that. See b/170404111.
mkdir /data/misc/perfetto-configs 0775 root shell
start traced
start traced_probes
接下来分析如何获取属性,以java层的SystemProperties的get获取方式来分析这个过程
frameworks/base/core/java/android/os/SystemProperties.java
/**
* Get the String value for the given {@code key}.
*
* @param key the key to lookup
* @return an empty string if the {@code key} isn't found
* @hide
*/
@NonNull
@SystemApi
public static String get(@NonNull String key) {
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get(key);
}
// The one-argument version of native_get used to be a regular native function.
@UnsupportedAppUsage
private static String native_get(String key) {
return native_get(key, "");
}
@FastNative
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private static native String native_get(String key, String def); // 最后调用 native 方法
native_get方法 对应的jni函数是SystemProperties_getSS
frameworks/base/core/jni/android_os_SystemProperties.cpp
const JNINativeMethod method_table[] = {
{ "native_get",
"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
(void*) SystemProperties_getSS },
...
}
/// @frameworks/base/core/jni/android_os_SystemProperties.cpp
jstring SystemProperties_getSS(JNIEnv* env, jclass clazz, jstring keyJ, jstring defJ)
{
jstring ret = defJ;
ReadProperty(env, keyJ, [&](const char* value) { // 读取属性,此处lambda是Functor回调
if (value[0]) {
ret = env->NewStringUTF(value);
}
});
if (ret == nullptr && !env->ExceptionCheck()) {
ret = env->NewStringUTF(""); // Legacy behavior
}
return ret;
}
ReadProperty
template<typename Functor>
void ReadProperty(JNIEnv* env, jstring keyJ, Functor&& functor)
{
ScopedUtfChars key(env, keyJ);
if (!key.c_str()) {
return;
}
#if defined(__BIONIC__) // 使用了bionic
const prop_info* prop = __system_property_find(key.c_str());//获取key对应的prop info
if (!prop) {
return;
}
ReadProperty(prop, std::forward<Functor>(functor)); // 读取value
#else
std::forward<Functor>(functor)(
android::base::GetProperty(key.c_str(), "").c_str());
#endif
}
// 调用另一个 ReadProperty 函数
template<typename Functor>
void ReadProperty(const prop_info* prop, Functor&& functor)
{
#if defined(__BIONIC__)
auto thunk = [](void* cookie,
const char* /*name*/,
const char* value,
uint32_t /*serial*/) {
std::forward<Functor>(*static_cast<Functor*>(cookie))(value); // 回调 Functor
};
__system_property_read_callback(prop, thunk, &functor); // 读取value并回调
#else
LOG(FATAL) << "fast property access supported only on device";
#endif
}
/// @bionic/libc/bionic/system_property_api.cpp
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
const prop_info* __system_property_find(const char* name) {
return system_properties.Find(name); // 寻找name对应context的映射区域内的prop信息
}
/// @bionic/libc/system_properties/system_properties.cpp
const prop_info* SystemProperties::Find(const char* name) {
if (!initialized_) {// 未初始化则返回
return nullptr;
}
prop_area* pa = contexts_->GetPropAreaForName(name); // 找到name对应的映射区
if (!pa) {
async_safe_format_log(ANDROID_LOG_WARN, "libc", "Access denied finding property \"%s\"", name);
return nullptr;
}
return pa->find(name); // 从这个区域寻找对应的属性信息
}
prop_area* ContextsSerialized::GetPropAreaForName(const char* name) {
uint32_t index;
property_info_area_file_->GetPropertyInfoIndexes(name, &index, nullptr);// 根据name获取context在集合中的index
if (index == ~0u || index >= num_context_nodes_) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Could not find context for property \"%s\"",
name);
return nullptr;
}
auto* context_node = &context_nodes_[index]; // 获取name对应的 context_node
if (!context_node->pa()) { // 如果还没映射该节点
// We explicitly do not check no_access_ in this case because unlike the
// case of foreach(), we want to generate an selinux audit for each
// non-permitted property access in this function.
context_node->Open(false, nullptr); // 需要映射此节点,注意第一个参数为false,只读
}
return context_node->pa(); // 返回映射区域
}
如之前,在ContextNode::Open中传入参数为false,会调用prop_area::map_prop_area映射到内存
prop_area* prop_area::map_prop_area(const char* filename) {
int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY); //只读方式打开
if (fd == -1) return nullptr;
prop_area* map_result = map_fd_ro(fd); // 只读映射到内存
close(fd);
return map_result;
}
此处上面的callback是上面传过来的thunk,cookie 是传的Functor
/// @bionic/libc/bionic/system_property_api.cpp
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
void __system_property_read_callback(const prop_info* pi,
void (*callback)(void* cookie, const char* name,
const char* value, uint32_t serial),
void* cookie) {
return system_properties.ReadCallback(pi, callback, cookie);
}
/// @bionic/libc/system_properties/system_properties.cpp
void SystemProperties::ReadCallback(const prop_info* pi,
void (*callback)(void* cookie, const char* name,
const char* value, uint32_t serial),
void* cookie) {
// Read only properties don't need to copy the value to a temporary buffer, since it can never
// change. We use relaxed memory order on the serial load for the same reason.
if (is_read_only(pi->name)) { // ro. 只读属性,value不会改变
uint32_t serial = load_const_atomic(&pi->serial, memory_order_relaxed);
if (pi->is_long()) {
callback(cookie, pi->name, pi->long_value(), serial);
} else {
callback(cookie, pi->name, pi->value, serial);
}
return;
}
char value_buf[PROP_VALUE_MAX];
uint32_t serial = ReadMutablePropertyValue(pi, value_buf); // 读取可变的属性值
callback(cookie, pi->name, value_buf, serial); // 回调获取得到值
}
SystemProperties::ReadMutablePropertyValue
#define __predict_true(exp) __builtin_expect((exp) != 0, 1)
#define __predict_false(exp) __builtin_expect((exp) != 0, 0)
uint32_t SystemProperties::ReadMutablePropertyValue(const prop_info* pi, char* value) {
// We assume the memcpy below gets serialized by the acquire fence.
uint32_t new_serial = load_const_atomic(&pi->serial, memory_order_acquire);
uint32_t serial;
unsigned int len;
for (;;) {
serial = new_serial;
len = SERIAL_VALUE_LEN(serial);
if (__predict_false(SERIAL_DIRTY(serial))) { // 检测到数据有更新
// See the comment in the prop_area constructor.
prop_area* pa = contexts_->GetPropAreaForName(pi->name);
memcpy(value, pa->dirty_backup_area(), len + 1);
} else {
memcpy(value, pi->value, len + 1); // 拷贝value数据
}
atomic_thread_fence(memory_order_acquire);
new_serial = load_const_atomic(&pi->serial, memory_order_relaxed);
if (__predict_true(serial == new_serial)) { // 没有数据改变,跳出
break;
}
// We need another fence here because we want to ensure that the memcpy in the
// next iteration of the loop occurs after the load of new_serial above. We could
// get this guarantee by making the load_const_atomic of new_serial
// memory_order_acquire instead of memory_order_relaxed, but then we'd pay the
// penalty of the memory_order_acquire even in the overwhelmingly common case
// that the serial number didn't change.
atomic_thread_fence(memory_order_acquire);
}
return serial;
}
总结一下ReadProperty获取属性的流程
在上面SystemProperties::Find中,如果initialized_为false则返回,那么什么时候进行初始化呢?对于Android中的应用或native程序而言,它在启动时会首先加载linker模块做一些初始化,其中就包括属性初始化,主要看linker_main函数
/// bionic/linker/linker_main.cpp
static ElfW(Addr) linker_main(KernelArgumentBlock& args, const char* exe_to_load) {
...
// Initialize system properties
// 此处做初始化
__system_properties_init(); // may use 'environ'
// Initialize platform properties.
platform_properties_init();
// Register the debuggerd signal handler.
linker_debuggerd_init(); // 初始化 signal handler
...
}
/// @bionic/libc/bionic/system_property_api.cpp
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int __system_properties_init() {// PROP_FILENAME 是 /dev/__properties__
return system_properties.Init(PROP_FILENAME) ? 0 : -1;
}
/// @bionic/libc/system_properties/system_properties.cpp
bool SystemProperties::Init(const char* filename) {
// This is called from __libc_init_common, and should leave errno at 0 (http://b/37248982).
ErrnoRestorer errno_restorer;
if (initialized_) {
contexts_->ResetAccess();
return true;
}
if (strlen(filename) >= PROP_FILENAME_MAX) {
return false;
}
strcpy(property_filename_, filename);
if (is_dir(property_filename_)) { // 判断是目录,已在init中创建
// property_info 可读,创建 ContextsSerialized 并初始化
if (access("/dev/__properties__/property_info", R_OK) == 0) {
contexts_ = new (contexts_data_) ContextsSerialized();
if (!contexts_->Initialize(false, property_filename_, nullptr)) {
return false;
}
} else { // 否则创建 ContextsSplit 并初始化
contexts_ = new (contexts_data_) ContextsSplit 并初始化();
if (!contexts_->Initialize(false, property_filename_, nullptr)) {
return false;
}
}
} else { // 否则创建 ContextsPreSplit 并初始化
contexts_ = new (contexts_data_) ContextsPreSplit();
if (!contexts_->Initialize(false, property_filename_, nullptr)) {
return false;
}
}
initialized_ = true; // 标记已初始化
return true;
}
以ContextsSerialized的初始化为例
/// @bionic/libc/system_properties/contexts_serialized.cpp
bool ContextsSerialized::Initialize(bool writable, const char* filename, bool* fsetxattr_failed) {
filename_ = filename;
if (!InitializeProperties()) {
return false;
}
// 从上面传递的writable值为false
if (writable) { // 在init中走这个逻辑
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 { // 走这个逻辑,将 properties_serial 映射到内存
if (!MapSerialPropertyArea(false, nullptr)) {
FreeAndUnmap();
return false;
}
}
return true;
}
从上面分析看,只是将properties_serial映射到了内存,而并没有映射其他ContextNode对应的文件节点。
接下来,以 SystemProperties#set 设置属性来分析该流程。
/// @frameworks/base/core/java/android/os/SystemProperties.java
/**
* Set the value for the given {@code key} to {@code val}.
*
* @throws IllegalArgumentException if the {@code val} exceeds 91 characters
* @throws RuntimeException if the property cannot be set, for example, if it was blocked by
* SELinux. libc will log the underlying reason.
* @hide
*/
@UnsupportedAppUsage
public static void set(@NonNull String key, @Nullable String val) {
if (val != null && !val.startsWith("ro.") && val.length() > PROP_VALUE_MAX) {
throw new IllegalArgumentException("value of system property '" + key
+ "' is longer than " + PROP_VALUE_MAX + " characters: " + val);
}
if (TRACK_KEY_ACCESS) onKeyAccess(key);
native_set(key, val); // 对应native方法
}
// _NOT_ FastNative: native_set performs IPC and can block
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P)
private static native void native_set(String key, String def); // 跨进程可能阻塞,不能是FastNative
native_set 对应的jni函数是 SystemProperties_set
/// @frameworks/base/core/jni/android_os_SystemProperties.cpp
{ "native_set", "(Ljava/lang/String;Ljava/lang/String;)V",
(void*) SystemProperties_set },
void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ,
jstring valJ)
{
ScopedUtfChars key(env, keyJ); // 封装,将jstring转char*
if (!key.c_str()) {
return;
}
std::optional<ScopedUtfChars> value;
if (valJ != nullptr) {
value.emplace(env, valJ);
if (!value->c_str()) {
return;
}
}
bool success;
#if defined(__BIONIC__) // android 定义了 __BIONIC__
success = !__system_property_set(key.c_str(), value ? value->c_str() : "");
#else
success = android::base::SetProperty(key.c_str(), value ? value->c_str() : "");
#endif
if (!success) {
jniThrowException(env, "java/lang/RuntimeException",
"failed to set system property (check logcat for reason)");
}
}
/// @bionic/libc/bionic/system_property_set.cpp
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int __system_property_set(const char* key, const char* value) {
if (key == nullptr) return -1;
if (value == nullptr) value = "";
if (g_propservice_protocol_version == 0) { // 默认为0,需要获取协议版本
detect_protocol_version();
}
if (g_propservice_protocol_version == kProtocolVersion1) { // 老协议,使用 PROP_MSG_SETPROP
// Old protocol does not support long names or values
if (strlen(key) >= PROP_NAME_MAX) return -1;
if (strlen(value) >= PROP_VALUE_MAX) return -1;
prop_msg msg;
memset(&msg, 0, sizeof msg);
msg.cmd = PROP_MSG_SETPROP;
strlcpy(msg.name, key, sizeof msg.name);
strlcpy(msg.value, value, sizeof msg.value);
return send_prop_msg(&msg);
} else { // 新协议,使用 PROP_MSG_SETPROP2
// New protocol only allows long values for ro. properties only.
if (strlen(value) >= PROP_VALUE_MAX && strncmp(key, "ro.", 3) != 0) return -1;
// Use proper protocol
PropertyServiceConnection connection; // 连接属性服务的socket服务端
if (!connection.IsValid()) {
errno = connection.GetLastError();
async_safe_format_log(
ANDROID_LOG_WARN, "libc",
"Unable to set property \"%s\" to \"%s\": connection failed; errno=%d (%s)", key, value,
errno, strerror(errno));
return -1;
}
SocketWriter writer(&connection); // 传给SocketWriter,用它来进行读写
// 向 init 属性服务 server socket 写入属性键值
if (!writer.WriteUint32(PROP_MSG_SETPROP2).WriteString(key).WriteString(value).Send()) {
errno = connection.GetLastError();
async_safe_format_log(ANDROID_LOG_WARN, "libc",
"Unable to set property \"%s\" to \"%s\": write failed; errno=%d (%s)",
key, value, errno, strerror(errno));
return -1;
}
int result = -1;
// 读取服务端的回复
if (!connection.RecvInt32(&result)) {
errno = connection.GetLastError();
async_safe_format_log(ANDROID_LOG_WARN, "libc",
"Unable to set property \"%s\" to \"%s\": recv failed; errno=%d (%s)",
key, value, errno, strerror(errno));
return -1;
}
if (result != PROP_SUCCESS) { // 不成功,打印失败log
async_safe_format_log(ANDROID_LOG_WARN, "libc",
"Unable to set property \"%s\" to \"%s\": error code: 0x%x", key, value,
result);
return -1;
}
return 0;
}
}
#define PROP_SERVICE_NAME "property_service"
#define PROP_FILENAME "/dev/__properties__"
/// @bionic/libc/bionic/system_property_set.cpp
static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
static const char* kServiceVersionPropertyName = "ro.property_service.version";
static constexpr uint32_t kProtocolVersion1 = 1;
static constexpr uint32_t kProtocolVersion2 = 2; // current
static atomic_uint_least32_t g_propservice_protocol_version = 0;
static void detect_protocol_version() {
char value[PROP_VALUE_MAX];
// 获取属性服务版本
if (__system_property_get(kServiceVersionPropertyName, value) == 0) {
g_propservice_protocol_version = kProtocolVersion1; // 为0,默认使用协议1
async_safe_format_log(ANDROID_LOG_WARN, "libc",
"Using old property service protocol (\"%s\" is not set)",
kServiceVersionPropertyName);
} else { // 否则使用获取的版本
uint32_t version = static_cast<uint32_t>(atoll(value));
if (version >= kProtocolVersion2) { // 协议2
g_propservice_protocol_version = kProtocolVersion2;
} else { // 协议1
async_safe_format_log(ANDROID_LOG_WARN, "libc",
"Using old property service protocol (\"%s\"=\"%s\")",
kServiceVersionPropertyName, value);
g_propservice_protocol_version = kProtocolVersion1;
}
}
}
从机器获取的属性值是2,也就是当前使用的是协议2
$ getprop ro.property_service.version
2
在PropertyServiceConnection的构造函数中,建立与属性服务端socket的连接。
PropertyServiceConnection() : last_error_(0) {
socket_.reset(::socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0));// 创建client socket
if (socket_.get() == -1) {
last_error_ = errno;
return;
}
const size_t namelen = strlen(property_service_socket);
sockaddr_un addr;
memset(&addr, 0, sizeof(addr));
strlcpy(addr.sun_path, property_service_socket, sizeof(addr.sun_path));
addr.sun_family = AF_LOCAL;
socklen_t alen = namelen + offsetof(sockaddr_un, sun_path) + 1;
// 连接 server socket
if (TEMP_FAILURE_RETRY(connect(socket_.get(),
reinterpret_cast<sockaddr*>(&addr), alen)) == -1) {
last_error_ = errno;
socket_.reset();
}
}
class SocketWriter {
public:
explicit SocketWriter(PropertyServiceConnection* connection)
: connection_(connection), iov_index_(0), uint_buf_index_(0) {
}
// 写 uint32_t 数据
SocketWriter& WriteUint32(uint32_t value) {
CHECK(uint_buf_index_ < kUintBufSize);
CHECK(iov_index_ < kIovSize);
uint32_t* ptr = uint_buf_ + uint_buf_index_;
uint_buf_[uint_buf_index_++] = value;
iov_[iov_index_].iov_base = ptr;
iov_[iov_index_].iov_len = sizeof(*ptr);
++iov_index_;
return *this;
}
// 写 字符串 数据
SocketWriter& WriteString(const char* value) {
uint32_t valuelen = strlen(value);
WriteUint32(valuelen);
if (valuelen == 0) {
return *this;
}
CHECK(iov_index_ < kIovSize);
iov_[iov_index_].iov_base = const_cast<char*>(value);
iov_[iov_index_].iov_len = valuelen;
++iov_index_;
return *this;
}
// 发送数据给server端
bool Send() {
if (!connection_->IsValid()) {
return false;
}
if (writev(connection_->socket(), iov_, iov_index_) == -1) {
connection_->last_error_ = errno;
return false;
}
iov_index_ = uint_buf_index_ = 0;
return true;
}
private:
...
BIONIC_DISALLOW_IMPLICIT_CONSTRUCTORS(SocketWriter);
};
class PropertyServiceConnection {
public:
...
// 读取 int32_t 数据
bool RecvInt32(int32_t* value) {
int result = TEMP_FAILURE_RETRY(recv(socket_.get(), value, sizeof(*value), MSG_WAITALL));
return CheckSendRecvResult(result, sizeof(*value));
}
private:
bool CheckSendRecvResult(int result, int expected_len) { // 检查结果是否错误
if (result == -1) {
last_error_ = errno;
} else if (result != expected_len) {
last_error_ = -1;
} else {
last_error_ = 0;
}
return last_error_ == 0;
}
}
当init属性服务收到客户端收到的设置属性请求后,会回调handle_property_set_fd函数来处理请求,可以参照之前的分析。