1.简介
Android系统中,为统一管理系统的属性,设计了一个统一的属性系统,包括两部分:文件保存的持久属性和每次开机导入的cache属性,前者主要保存在.prop文件中,需要注意的是android属性的名称是有一定的格式要求。每个属性都有一个名称和值,他们都是字符串格式。属性被大量使用在Android系统中,用来记录系统设置或进程之间的信息交换。 系统属性根据不同的应用类型,分为不可变型,持久型,网络型,启动和停止服务等。
- 属性名称以“ro.”开头,那么这个属性被视为只读属性。一旦设置,属性值不能改变。
- 属性名称以“persist.”开头,当设置这个属性时,其值也将写入/data/property/persistent_properties。(相对旧一点的Android版本是/data/property)
- 属性名称以“net.”开头,当设置这个属性时,“net.change”属性将会自动设置,以加入到最后修改的属性名。(这是很巧妙的。 netresolve模块的使用这个属性来追踪在net.*属性上的任何变化。)
- 属性“ ctrl.start ”和“ ctrl.stop ”是用来启动和停止服务。每一项服务必须在/init.rc中定义.系统启动时,与init守护进程将解析init.rc和启动属性服务。一旦收到设置“ ctrl.start ”属性的请求,属性服务将使用该属性值作为服务名找到该服务,启动该服务。这项服务的启动结果将会放入“ init.svc.<服务名>“属性中 。客户端应用程序可以轮询那个属性值,以确定结果。
2.API使用
Java API:
import android.os.SystemProperties; public static String get(String key, String def) {} public static void set(String key, String val) {} //示例 //Systemproperties类在android.os下,但这个类是隐藏的,上层程序开发无法直接使用 //需要系统属性设置的程序也必须有system或root权限,或者用Java的反射机制 需要注意的是android属性的名称是有一定的格式要求的,前缀必须用system\core\init\property_service.c中定义的前缀 Systemproperties.set(name, value);//设置 Systemproperties.get(name);//读取
Native API:
#include <cutils/properties.h> int property_get(const char *key, char *value, const char *default_value); int property_set(const char *key, const char *value);
3.系统属性服务启动流程
在init的启动第二阶段,启动属性服务线程,提供相关属性服务,给其他进程提供设置属性的支持,并通知init去处理属性事件。
/// system/core/init/init.cpp int SecondStageMain(int argc, char** argv) { ... PropertyInit(); // 属性环境相关初始化,及固有属性加载 ... StartPropertyService(&property_fd); // 启动属性服务 ... }
属性系统服务端源码:system/core/init/property_service.cpp
(1)初始化(PropertyInit):
/// @system/core/init/property_service.cpp void PropertyInit() { selinux_callback cb; cb.func_audit = PropertyAuditCallback; selinux_set_callback(SELINUX_CB_AUDIT, cb); mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH); // 创建/dev/__properties__目录,权限drwx--x--x CreateSerializedPropertyInfo(); // 创建property se contexts if (__system_property_area_init()) { // 将 /dev/__properties__/properties_serial 映射到内存, 创建 ContextNodes,映射节点 LOG(FATAL) << "Failed to initialize property area"; } if (!property_info_area.LoadDefaultPath()) { // 加载/dev/__properties__/property_info 映射进内存 LOG(FATAL) << "Failed to load serialized property info file"; } // If arguments are passed both on the command line and in DT, // properties set in DT always have priority over the command-line ones. ProcessKernelDt(); // 解析 /proc/device-tree/firmware/android/ ProcessKernelCmdline(); // 解析 /proc/cmdline , 将其中键值对满足key为androidboot.* 的,将key替换为ro.boot.*,然后添加到属性 ProcessBootconfig(); // 解析 /proc/bootconfig ,同上 // Propagate the kernel variables to internal variables // used by init as well as the current required properties. ExportKernelBootProps(); // 给一些kernel属性初始赋值,如果没有设置的话 // 加载默认的属性文件的属性 // 如 /system/build.prop /vendor/default.prop /vendor/build.prop // 还会解析其他分区 如 odm、product、system_ext PropertyLoadBootDefaults(); }
从相关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; }
/// @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; }
/// @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; }
/// @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函数:
/// @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()流程:
- 创建/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。
property_info_area.LoadDefaultPath 加载 /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); } // /<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配置属性 }
更新usb相关的配置属性:
// 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); } }
(2)启动服务(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 , 表示可以处理请求消息 // 创建接收属性请求的 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,监听其相关事件,当收到设置属性请求时,会回调 handle_property_set_fd 函数 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)(); } } } }
/// @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; }
WritePersistentProperty实现是在 system/core/init/persistent_properties.cpp:
/// @system/core/init/persistent_properties.cpp // Persistent properties are not written often, so we rather not keep any data in memory and read // then rewrite the persistent property file for each update. void WritePersistentProperty(const std::string& name, const std::string& value) { auto persistent_properties = LoadPersistentPropertyFile(); if (!persistent_properties.ok()) { LOG(ERROR) << "Recovering persistent properties from memory: " << persistent_properties.error(); persistent_properties = LoadPersistentPropertiesFromMemory(); } auto it = std::find_if(persistent_properties->mutable_properties()->begin(), persistent_properties->mutable_properties()->end(), [&name](const auto& record) { return record.name() == name; }); if (it != persistent_properties->mutable_properties()->end()) { it->set_name(name); it->set_value(value); } else { AddPersistentProperty(name, value, &persistent_properties.value()); } if (auto result = WritePersistentPropertyFile(*persistent_properties); !result.ok()) { //persistent属性要接入文件永久存储 LOG(ERROR) << "Could not store persistent property: " << result.error(); } }
/// @system/core/init/persistent_properties.cpp Result<void> WritePersistentPropertyFile(const PersistentProperties& persistent_properties) { const std::string temp_filename = persistent_property_filename + ".tmp"; unique_fd fd(TEMP_FAILURE_RETRY( open(temp_filename.c_str(), O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC | O_CLOEXEC, 0600))); if (fd == -1) { return ErrnoError() << "Could not open temporary properties file"; } std::string serialized_string; if (!persistent_properties.SerializeToString(&serialized_string)) { return Error() << "Unable to serialize properties"; } if (!WriteStringToFd(serialized_string, fd)) { //属性写入临时文件 return ErrnoError() << "Unable to write file contents"; } fsync(fd); fd.reset(); if (rename(temp_filename.c_str(), persistent_property_filename.c_str())) { //临时文件重命名为/data/property/persistent_properties int saved_errno = errno; unlink(temp_filename.c_str()); return Error(saved_errno) << "Unable to rename persistent property file"; }
//注:以上rename库函数并不会立即将文件写入磁盘,它只是将文件在文件系统中的目录项(包括文件名和对应的索引节点)进行修改,以实现文件的重命名操作。
//而Android 10及之前版本没有以下目录sync的动作,所以有些不带电池的设备,在设置persist属性后如果突然断电,重启后设置的属性值可能会丢失!
// rename() is atomic with regards to the kernel's filesystem buffers, but the parent // directories must be fsync()'ed otherwise, the rename is not necessarily written to storage. // Note in this case, that the source and destination directories are the same, so only one // fsync() is required. auto dir = Dirname(persistent_property_filename); auto dir_fd = unique_fd{open(dir.c_str(), O_DIRECTORY | O_RDONLY | O_CLOEXEC)}; if (dir_fd < 0) { return ErrnoError() << "Unable to open persistent properties directory for fsync()"; } fsync(dir_fd); return {}; }
属性变化的处理逻辑:
/// @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(); } }
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来执行。
/// @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()); } } }
4.属性的读写
前面提到了persist属性的设置,接下来具体分析下属性的读取和设置流程。
(1)读取属性,以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 方法
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; }
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); // 回调获取得到值 }
#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函数,其中就走了 __system_properties_init ---> SystemProperties::Init的流程:
/// 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 ... }
(2)设置属性,以 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
/// @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; } } }
在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(); } }
通过socket写属性:
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写数据来设置属性值。
- 客户端通过打开节点文件并以只读映射到内存,然后读取对应的属性值。
标签:__,return,system,value,详解,Android,property,properties,属性 From: https://www.cnblogs.com/blogs-of-lxl/p/17774765.html