文章托管在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--xCreateSerializedPropertyInfo(); // 创建property se contextsif (__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_extPropertyLoadBootDefaults();
}

CreateSerializedPropertyInfo

从相关property_contexts文件读取到context信息,并将内容序列化,然后写入 /dev/properties/property_info

void CreateSerializedPropertyInfo() {auto property_infos = std::vector<PropertyInfoEntry>();// 从 selinux 的相关 property context 解析属性上下文信息,并存入 property_infosif (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_infoPLOG(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();// 初始化 ContextsSerializedif (!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_rwopen_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_inforeturn 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) { // 获取fstatclose(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:s0int 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_rwpa_ = 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:s0if (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_);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:s0prop_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()流程:

  • 创建/dev/__properties__目录,权限drwx–x–x
  • 解析property context文件并将信息存储到 /dev/__properties__/property_info
  • 映射property_info,读取property context并根据每个context创建对应的 ContextNode
  • ContextNode::Open 创建节点文件并以读写映射到内存
  • 将/dev/__properties__/properties_serial 映射到内存

到此 __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
...

property_info_area.LoadDefaultPath

加载 /dev/__properties__/property_info 并映射到内存,逻辑同之前 property_info_area_file_ 加载

bool PropertyInfoAreaFile::LoadDefaultPath() {return LoadPath("/dev/__properties__/property_info");
}

ProcessKernelCmdline / ProcessBootconfig

/// @system/core/init/property_service.cpp
constexpr auto ANDROIDBOOT_PREFIX = "androidboot."sv;static void ProcessKernelCmdline() {// 加载 /proc/cmdlineImportKernelCmdline([&](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/bootconfigImportBootconfig([&](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));}}
}

PropertyLoadBootDefaults

加载开机默认属性

/// @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);}// /<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) {  // 加载某个分区的lambda表达式auto path = "/" + partition + "/etc/build.prop";if (load_properties_from_file(path.c_str(), nullptr, &properties)) {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);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);}
}

StartPropertyService

启动属性服务

/// @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 , 表示可以处理请求消息// 创建接收属性请求的 socketif (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);
}

PropertyServiceThread

/// @system/core/init/property_service.cpp
static void PropertyServiceThread() {Epoll epoll;if (auto result = epoll.Open(); !result.ok()) { // 创建 epollLOG(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 函数

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;}
}

HandlePropertySet

处理设置属性

/// @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); // 真正设置更新
}

PropertySet

/// @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;
}

PropertyChanged

属性变化处理逻辑

/// @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); // 检查是否有设置等待此属性值
}

ActionManager::QueuePropertyChange

/// @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)); // 插入一个属性变化的事件
}

WakeMainInitThread

/// @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 fdif (wake_main_thread_fd == -1) {PLOG(FATAL) << "Failed to create eventfd for waking init";}auto clear_eventfd = [] { // epoll收到相关事件会回调此lambdauint64_t counter; // 仅仅只是读出来,消费掉事件。主要目的是唤醒epollTEMP_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();}
}

SecondStageMain 处理属性事件

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来执行。

ActionManager::ExecuteOneCommand

/// @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 executewhile (current_executing_actions_.empty() && !event_queue_.empty()) { // 当前没有执行的action,但是有事件for (const auto& action : actions_) { // 找到此事件对应要处理的actionif (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_;  // 每执行一次数量加1if (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

当设置属性 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 shellstart tracedstart traced_probes

获取属性流程

接下来分析如何获取属性,以java层的SystemProperties的get获取方式来分析这个过程

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 },...
}

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__)  // 使用了bionicconst prop_info* prop = __system_property_find(key.c_str());//获取key对应的prop infoif (!prop) {return;}ReadProperty(prop, std::forward<Functor>(functor)); // 读取value
#elsestd::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并回调
#elseLOG(FATAL) << "fast property access supported only on device";
#endif
}

__system_property_find

/// @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); // 从这个区域寻找对应的属性信息
}

ContextsSerialized::GetPropAreaForName

prop_area* ContextsSerialized::GetPropAreaForName(const char* name) {uint32_t index;property_info_area_file_->GetPropertyInfoIndexes(name, &index, nullptr);// 根据name获取context在集合中的indexif (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_nodeif (!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;
}

__system_property_read_callback

此处上面的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);
}

SystemProperties::ReadCallback

/// @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获取属性的流程

  • __system_property_find 寻找prop_info

    • 获取key对应的ContextNode的内存映射区域prop_area
    • 如果节点没有,则会打开并以只读映射到内存
    • 从prop_area中读取prop_info
  • __system_property_read_callback 从prop info获取value,并回传value值
    • 从prop_info中读取value
    • callback回调传回value值

在上面SystemProperties::Find中,如果initialized_为false则返回,那么什么时候进行初始化呢?对于Android中的应用或native程序而言,它在启动时会首先加载linker模块做一些初始化,其中就包括属性初始化,主要看linker_main函数

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...
}

__system_properties_init

/// @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;
}

SystemProperties::Init

/// @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值为falseif (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 设置属性来分析该流程。

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

SystemProperties_set

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() : "");#elsesuccess = android::base::SetProperty(key.c_str(), value ? value->c_str() : "");#endifif (!success) {jniThrowException(env, "java/lang/RuntimeException","failed to set system property (check logcat for reason)");}}

__system_property_set

/// @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 valuesif (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 protocolPropertyServiceConnection 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) { // 不成功,打印失败logasync_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;}
}

detect_protocol_version

#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,默认使用协议1async_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) { // 协议2g_propservice_protocol_version = kProtocolVersion2;} else {  // 协议1async_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

建立socket连接

在PropertyServiceConnection的构造函数中,建立与属性服务端socket的连接。

PropertyServiceConnection() : last_error_(0) {socket_.reset(::socket(AF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0));// 创建client socketif (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 socketif (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函数来处理请求,可以参照之前的分析。

总结

  • init初始化属性内存区域,根据每个property context创建对应的ContextNode,建立节点文件并以读写映射到内存
  • init从各个build.prop文件中加载属性值
  • init启动属性服务,创建服务端socket,等待客户端连接来设置属性
  • 客户端通过socket连接属性服务端socket,通过socket写数据来设置属性值
  • 客户端通过打开节点文件并以只读映射到内存,然后读取对应的属性值

Android 12 init(3) 属性服务相关推荐

  1. 《深入理解Android 卷1》读书笔记 (一)—— Android Init之属性服务 (property_service)

    本文的大体流程还是按照书本上来,分三段. (一)从Main开始到service start (二)zygote restart (三)属性服务 (property_service) 由于本文内容较长, ...

  2. Android 12 应用兼容性适配指导

    一.兼容性调试工具 Android 11开始引入了新的工具,可针对Android新平台中的行为变更进行测试和调试.这些工具是兼容性框架的一部分,该框架使得开发者可通过开发者选项或adb命令单独打开和关 ...

  3. Android 12的行为变更和版本兼容思路

    一年一度的产品线兼容活动又开始了.Android系统每更新一次系统,对开发者而言都是持续而漫长的挑战. 自15年6.0的兼容以来,每年给公司产品线app做版本兼容,成了我每年的保留节目-- 结束完产品 ...

  4. Android 12上焕然一新的小组件

    /   今日科技快讯   / 近日,电动汽车制造商特斯拉公布了2021年第三季度财报.财报显示,特斯拉第三季度营收137.6亿美元,同比增长57%:净利润16.2亿美元,同比增长389%:每股收益1. ...

  5. Android 12上焕然一新的小组件:美观、便捷和实用

    Google IO 2021上重磅介绍的Android 12,号称历代设计变化最大的版本.其全新的Material You设计语言.流畅的动画特效再到焕然一新的小组件,都令人印象深刻.本文将聚焦小组件 ...

  6. 更进一步 | Android 12 Beta 2 发布

    作者 / Dave Burke, VP of Engineering 几周前在 Google I/O 上,我们发布了 Android 12 的第一个 Beta 版,带来了能展现您的个性.契合您的需求的 ...

  7. [日更-2019.4.8、4.9、4.12、4.13] cm-14.1 Android系统启动过程分析(一)-init进程的启动、rc脚本解析、zygote启动、属性服务...

    2019独角兽企业重金招聘Python工程师标准>>> 声明 前阶段在项目中涉及到了Android系统定制任务,Android系统定制前提要知道Android系统是如何启动的. 本文 ...

  8. cm-14.1 Android系统启动过程分析(4)-init进程的启动、rc脚本解析、zygote启动、属性服务

    声明 前阶段在项目中涉及到了Android系统定制任务,Android系统定制前提要知道Android系统是如何启动的. 本文参考了一些书籍的若干章节,比如<Android进阶解密-第2章-An ...

  9. 谷歌正式发布Android 12,UI更好看,应用更快,打造独属于自己的定制化属性

    焕然一新的Android 12 今年 5 月的 Google I/O 大会上,谷歌推出了 Android 12 系统,这是原生安卓系统史上最大的设计变化,从此旧貌换新颜. 不只是外观,Android ...

最新文章

  1. Shark0.9.1安装
  2. android 颜色填充工具栏,Android工具栏颜色未由colorPrimary设置
  3. 720 智能硬件与 LeanCloud 云端的默契协作
  4. java thread 线程销毁_手把手带你了解Java线程的实现方式及生命周期原理
  5. NYOJ-最少步数(dfs)
  6. 每日三道前端面试题--vue 第一弹
  7. 转: hibernate配置文件hibernate.cfg.xml和.hbm.xml的详细解释
  8. MFC(6)点击按钮弹出新的对话框源码
  9. 实验七 6 音乐DJ和回响贝斯
  10. 微信小程序快递查询插件
  11. 【华为云技术分享】从软件开发到 AI 领域工程师:模型训练篇
  12. 机载激光雷达原理与应用科普(一)
  13. air202c语言编程,Air202学习 五
  14. 我的世界服务器怎么设置自动拾取,自动拾取Auto Pickup Mod
  15. 1.Python下载与安装教程 For Windows
  16. python编程入门书-最适合Python初学者的6本书籍推荐「必须收藏」
  17. Vue-Watcher观察者源码详解
  18. AMS1117接线图
  19. 白竹机器人_白术防风黄芪功效作用
  20. 人工智能是否会取代人类的工作?为什么或为什么不?

热门文章

  1. pythonpptx库_python-pptx安装
  2. S7-300/400 PLC 模拟量输入/输出的量程转换
  3. Arduion实验九 轻触开关实验
  4. table css个人简历的实现 美观 简洁
  5. ADN: Artifact Disentanglement Network for Unsupervised Metal Artifact Reduction
  6. flash插件替换方案-把uploadfy替换为uploadfive
  7. 实验二 XML之数学公式和化学分子式
  8. KMP - Oulipo - HDU - 1686
  9. requests使用socks代理
  10. 利用iptables的SNAT功能实现局域网共享上网