second_stage_property模块
- 概述
- 源码解析
- 1. property_service
- 1.1 PropertyInit-对property进行初始化
- 1.2 CreateSerializedPropertyInfo-创建并初始化/dev/properties/property_info文件
- 1.3 LoadPropertyInfoFromFile-解析property_context文件
- 1.4 ProcessKernelDt-处理dts中的属性
- 1.5 InitPropertySet-设置属性
- 1.6 HandlePropertySet-设置属性
- 1.7 PropertySet-设置属性
- 1.8 ProcessKernelCmdline-处理内核cmdline中的属性,androidboot.开头的设置为ro.boot.属性
- 1.9 ExportKernelBootProps-内核启动相关的一些属性
- 1.10 PropertyLoadBootDefaults-加载文件中的属性
- 1.11 property_initialize_ro_product_props
- 1.12 property_derive_build_fingerprint-如果没设置fingerprint的话,就自动生成
- 1.13 update_sys_usb_config-adb相关的,debug固件默认打开adb
- 1.14 load_properties_from_file-从文件中加载属性
- 1.15 LoadProperties
- 1.16 StartPropertyService-启动propertyservice线程
- 1.17 PropertyServiceThread-property线程
- 2. libpropertyinfoserializer模块
- 3. Trie模块-树型结构
- 3.1 BuildTrie-将property_info格式化成一个字符串,然后写入到/dev/properties/property_info文件中
- 3.2 TrieBuilder构造函数
- 3.3 StringPointerFromContainer-将string放到set中,并返回一个指向它的iterator指针
- 3.4 AddToTrie-将propertyinfo按照顺序添加到TrieBuilder中
- 3.5 SerializeTrie-将TrieBuilder组成一个字符串
- 3.6 AllocateObject+为T对象在string的data中分配空间
- 3.7 AllocateData-string的data中分配大小
- 3.8 SerializeStrings-写string到arena_的data中
- 3.9 allocate相关的函数
- 3.10 WriteTrieNode-写Node
- 3.11 WritePropertyEntry-写每个PropertyEntry
- 3.12 FindContextIndex-找data_中context string的index
- 3.13 LoadDefaultPath-每个进程都会打开/dev/properties/property_info文件
- 3.14 GetPropertyInfoIndexes-返回context的indexes
- 3.15 CheckPrefixMatch
- 4. system_property_area模块-SystemProperties的接口函数
- 5. SystemProperties类
- 6. ContextsSerialized类
- 6.1 Initialize-
/dev/__properties
__下创建文件映射 - 6.2 InitializeProperties-mmap这个/dev/properties/property_info文件以及匿名映射
- 6.3 InitializeContextNodes-初始化[anon:System property context nodes]-创建ContextNode
- 6.4 MapSerialPropertyArea-创建
/dev/__properties__/properties_serial
文件及其映射 - 6.5 GetPropAreaForName-返回u:object_r:exported2_default_prop:s0文件映射
- 6.1 Initialize-
- 7. ContextNode类
- 8. prop_area类
- 1. property_service
- 问题
- 补充
- 参考
概述
1. 按context来进行分类
属性是以context为基础的
(1)读/system/etc/selinux/plat_property_contexts文件,按照Trie形式,组织成树形形式
三种情况
(1)ro.boot. u:object_r:exported2_default_prop:s0
TrieBuilder -> builder_root_ -> ro -> boot
(2)ro.boot.vendor.overlay.theme u:object_r:exported_overlay_prop:s0 exact string 将theme放到node中的exact_matches_中
TrieBuilder -> builder_root_ -> ro -> boot -> vendor -> overlay
(3)ro.boot.serialno u:object_r:serialno_prop:s0 将serialno放到node中的prefixes_
树的节点数据为PropertyEntryBuilder类型,PropertyEntryBuilder存放name,context,type;
context和types都指向triebuild中的同一个context和type,以节省内存
/dev/__properties__/property_info文件的组织形式:
24字节的PropertyInfoAreaHeader + contexts大小 + context_string_array(将context中strings在data中的位置保存到array中) + contexts数据 + types大小 + types_string_array + types数据 + trieNode数据
trieNode数据为:
这是一个Node:(TrieNodeInternal对象 + PropertyEntry + prefix的数组 + prefix的PropertyEntry + exact_matches的数组 + exact_matches的PropertyEntry + children的数组 + children的Node)以此循环
PropertyEntry的数据为:
name的offset + context的offset + type的offset,context和type都是指向前面的contexts数据和types数据
(2)将Trie树形结构保存到/dev/__properties__/property_info
文件中
(3)将/dev/__properties__/property_info
文件mmap shared的形式到内存中,用PropertyInfoArea类来管理
(4)将context数据映射到[anon:System property context nodes]匿名映射中,用ContextNode来管理
(5)用4中创建的ContextNode来创建/dev/__properties__/u:object_r:boottime_prop:s0
文件映射,用memory_area来进行管理,用来存储属性的值
(6)创建/dev/__properties__/properties_serial
文件映射,用prop_area来进行管理,这个文件用来表示属性的值有更新
2. 启动PropertyServiceThread
用socket设置属性,可以走一下流程
源码解析
1. property_service
android/system/core/init/property_service.cpp
1.1 PropertyInit-对property进行初始化
void PropertyInit() {
// selinux相关的
selinux_callback cb;
cb.func_audit = PropertyAuditCallback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
// 读plat_property_contexts文件,初始化到propertyInfo数据结构中,然后创建trie数据结构,最后将trie数据结构写入到/dev/__properties__/property_info文件
CreateSerializedPropertyInfo();
// 创建/dev/__properties__/下的文件映射
if (__system_property_area_init()) {
LOG(FATAL) << "Failed to initialize property area";
}// 又创建了一次/dev/__properties__/property_info文件映射,cat /proc/1/maps可以看到有两个/dev/__properties__/property_info文件映射
if (!property_info_area.LoadDefaultPath()) {
LOG(FATAL) << "Failed to load serialized property info file";
}
// If arguments are passed both on the command line and in DT,
// properties set in DT always have priority over the command-line ones.
ProcessKernelDt();
ProcessKernelCmdline();
// Propagate the kernel variables to internal variables
// used by init as well as the current required properties.
ExportKernelBootProps();
PropertyLoadBootDefaults();
}
1.2 CreateSerializedPropertyInfo-创建并初始化/dev/properties/property_info文件
void CreateSerializedPropertyInfo() {
auto property_infos = std::vector<PropertyInfoEntry>();
if (access("/system/etc/selinux/plat_property_contexts", R_OK) != -1) {
// 加载property_contexts到property_infos结构体中
if (!LoadPropertyInfoFromFile("/system/etc/selinux/plat_property_contexts",
&property_infos)) {
return;
}
// Don't check for failure here, so we always have a sane list of properties.
// 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)) {
PLOG(ERROR) << "Unable to write serialized property infos to file";
}
selinux_android_restorecon(kPropertyInfosPath, 0);
}
1.3 LoadPropertyInfoFromFile-解析property_context文件
bool LoadPropertyInfoFromFile(const std::string& filename,
std::vector<PropertyInfoEntry>* property_infos) {
auto file_contents = std::string();
if (!ReadFileToString(filename, &file_contents)) {
PLOG(ERROR) << "Could not read properties from '" << filename << "'";
return false;
}
auto errors = std::vector<std::string>{};
// Android R为true
bool require_prefix_or_exact = SelinuxGetVendorAndroidVersion() >= __ANDROID_API_R__;
ParsePropertyInfoFile(file_contents, require_prefix_or_exact, property_infos, &errors);
// Individual parsing errors are reported but do not cause a failed boot, which is what
// returning false would do here.
for (const auto& error : errors) {
LOG(ERROR) << "Could not read line from '" << filename << "': " << error;
}
return true;
}
1.4 ProcessKernelDt-处理dts中的属性
static void ProcessKernelDt() {
// 读/proc/device-tree/firmware/android/compatible,看是否为android,firmware
if (!is_android_dt_value_expected("compatible", "android,firmware")) {
return;
}
// 打开/proc/device-tree/firmware/android/文件夹
std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
if (!dir) return;
std::string dt_file;
struct dirent* dp;
while ((dp = readdir(dir.get())) != NULL) {
if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") ||
!strcmp(dp->d_name, "name")) {
continue;
}
std::string file_name = get_android_dt_dir() + dp->d_name;
android::base::ReadFileToString(file_name, &dt_file);
std::replace(dt_file.begin(), dt_file.end(), ',', '.');
// 所以这里只是设置了[ro.boot.boot_devices]: [soc@2900000/4020000.sdmmc.soc@2900000/4022000.sdmmc.soc@2900000]
// 因为配置了ro.boot.的context,所以可以设置任何ro.boot.xxx的属性
InitPropertySet("ro.boot."s + dp->d_name, dt_file);
}
}
1.5 InitPropertySet-设置属性
uint32_t InitPropertySet(const std::string& name, const std::string& value) {
uint32_t result = 0;
ucred cr = {.pid = 1, .uid = 0, .gid = 0};
std::string error;
result = HandlePropertySet(name, value, kInitContext, cr, nullptr, &error);
if (result != PROP_SUCCESS) {
LOG(ERROR) << "Init cannot set '" << name << "' to '" << value << "': " << error;
}
return result;
}
1.6 HandlePropertySet-设置属性
// ro.boot.boot_devices,soc@2900000/4020000.sdmmc.soc@2900000/4022000.sdmmc.soc@2900000,u:r:init:s0,{.pid = 1, .uid = 0, .gid = 0},socket为null
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.")) {
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 == "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.
// selinux.restorecon_recursive属性
if (name == kRestoreconProperty && cr.pid != 1 && !value.empty()) {
static AsyncRestorecon async_restorecon;
async_restorecon.TriggerRestorecon(value);
return PROP_SUCCESS;
}
return PropertySet(name, value, error);
}
1.7 PropertySet-设置属性
// ro.boot.boot_devices,soc@2900000/4020000.sdmmc.soc@2900000/4022000.sdmmc.soc@2900000
static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
size_t valuelen = value.size();
// 小于1,以.开头,最后一个为.,以及包含..都是不行的
if (!IsLegalPropertyName(name)) {
*error = "Illegal property name";
return PROP_ERROR_INVALID_NAME;
}
// ro.开头的就可以无限制了?mbstowcs判断是否有无效的多字节字符
if (auto result = IsLegalPropertyValue(name, value); !result.ok()) {
*error = result.error().message();
return PROP_ERROR_INVALID_VALUE;
}
// 找,ro.boot.boot_devices为null
prop_info* pi = (prop_info*) __system_property_find(name.c_str());
if (pi != nullptr) {
// ro.* properties are actually "write-once".
// 如果已经存在了这样的属性,并且以ro.开头的不能设置
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 {
// 如果不存在的话,ro.开头的就可以进行添加了
// 增
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.
// persistent_properties_loaded为true之前,不能改persist.的值
if (persistent_properties_loaded && StartsWith(name, "persist.")) {
WritePersistentProperty(name, value);
}
// 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;
}
1.8 ProcessKernelCmdline-处理内核cmdline中的属性,androidboot.开头的设置为ro.boot.属性
static void ProcessKernelCmdline() {
bool for_emulator = false;
ImportKernelCmdline([&](const std::string& key, const std::string& value) {
if (key == "qemu") {
for_emulator = true;
} else if (StartsWith(key, "androidboot.")) {
InitPropertySet("ro.boot." + key.substr(12), value);
}
});
if (for_emulator) {
ImportKernelCmdline([&](const std::string& key, const std::string& value) {
// In the emulator, export any kernel option with the "ro.kernel." prefix.
InitPropertySet("ro.kernel." + key, value);
});
}
}
1.9 ExportKernelBootProps-内核启动相关的一些属性
static void ExportKernelBootProps() {
constexpr const char* UNSET = "";
struct {
const char* src_prop;
const char* dst_prop;
const char* default_value;
} prop_map[] = {
// clang-format off
{ "ro.boot.serialno", "ro.serialno", UNSET, },
{ "ro.boot.mode", "ro.bootmode", "unknown", },
{ "ro.boot.baseband", "ro.baseband", "unknown", },
{ "ro.boot.bootloader", "ro.bootloader", "unknown", },
{ "ro.boot.hardware", "ro.hardware", "unknown", },
{ "ro.boot.revision", "ro.revision", "0", },
// clang-format on
};
for (const auto& prop : prop_map) {
std::string value = GetProperty(prop.src_prop, prop.default_value);
if (value != UNSET) InitPropertySet(prop.dst_prop, value);
}
}
1.10 PropertyLoadBootDefaults-加载文件中的属性
void PropertyLoadBootDefaults() {
// TODO(b/117892318): merge prop.default and build.prop files into one
// 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 (!load_properties_from_file("/system/etc/prop.default", nullptr, &properties)) {
// Try recovery path
if (!load_properties_from_file("/prop.default", nullptr, &properties)) {
// Try legacy path
load_properties_from_file("/default.prop", nullptr, &properties);
}
}
load_properties_from_file("/system/build.prop", nullptr, &properties);
load_properties_from_file("/system_ext/build.prop", nullptr, &properties);
load_properties_from_file("/vendor/default.prop", nullptr, &properties);
load_properties_from_file("/vendor/build.prop", nullptr, &properties);
if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
load_properties_from_file("/odm/etc/build.prop", nullptr, &properties);
} else {
load_properties_from_file("/odm/default.prop", nullptr, &properties);
load_properties_from_file("/odm/build.prop", nullptr, &properties);
}
load_properties_from_file("/product/build.prop", nullptr, &properties);
load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
if (access(kDebugRamdiskProp, R_OK) == 0) {
// adb root相关的
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_derive_build_fingerprint();
update_sys_usb_config();
}
1.11 property_initialize_ro_product_props
// If the ro.product.[brand|device|manufacturer|model|name] properties have not been explicitly
// set, derive them from ro.product.${partition}.* properties
// 好像这些默认都配置有
static void property_initialize_ro_product_props() {
const char* RO_PRODUCT_PROPS_PREFIX = "ro.product.";
const char* RO_PRODUCT_PROPS[] = {
"brand", "device", "manufacturer", "model", "name",
};
const char* RO_PRODUCT_PROPS_ALLOWED_SOURCES[] = {
"odm", "product", "system_ext", "system", "vendor",
};
const char* RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER = "product,odm,vendor,system_ext,system";
const std::string EMPTY = "";
std::string ro_product_props_source_order =
GetProperty("ro.product.property_source_order", EMPTY);
if (!ro_product_props_source_order.empty()) {
// Verify that all specified sources are valid
for (const auto& source : Split(ro_product_props_source_order, ",")) {
// Verify that the specified source is valid
bool is_allowed_source = false;
for (const auto& allowed_source : RO_PRODUCT_PROPS_ALLOWED_SOURCES) {
if (source == allowed_source) {
is_allowed_source = true;
break;
}
}
if (!is_allowed_source) {
LOG(ERROR) << "Found unexpected source in ro.product.property_source_order; "
"using the default property source order";
ro_product_props_source_order = RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER;
break;
}
}
} else {
// 为空,用的是默认的
ro_product_props_source_order = RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER;
}
for (const auto& ro_product_prop : RO_PRODUCT_PROPS) {
std::string base_prop(RO_PRODUCT_PROPS_PREFIX);
base_prop += ro_product_prop;
std::string base_prop_val = GetProperty(base_prop, EMPTY);
if (!base_prop_val.empty()) {
continue;
}
for (const auto& source : Split(ro_product_props_source_order, ",")) {
std::string target_prop(RO_PRODUCT_PROPS_PREFIX);
target_prop += source;
target_prop += '.';
target_prop += ro_product_prop;
std::string target_prop_val = GetProperty(target_prop, EMPTY);
if (!target_prop_val.empty()) {
LOG(INFO) << "Setting product property " << base_prop << " to '" << target_prop_val
<< "' (from " << target_prop << ")";
std::string error;
uint32_t res = PropertySet(base_prop, target_prop_val, &error);
if (res != PROP_SUCCESS) {
LOG(ERROR) << "Error setting product property " << base_prop << ": err=" << res
<< " (" << error << ")";
}
break;
}
}
}
}
1.12 property_derive_build_fingerprint-如果没设置fingerprint的话,就自动生成
// If the ro.build.fingerprint property has not been set, derive it from constituent pieces
static void property_derive_build_fingerprint() {
std::string build_fingerprint = GetProperty("ro.build.fingerprint", "");
if (!build_fingerprint.empty()) {
return;
}
const std::string UNKNOWN = "unknown";
build_fingerprint = GetProperty("ro.product.brand", UNKNOWN);
build_fingerprint += '/';
build_fingerprint += GetProperty("ro.product.name", UNKNOWN);
build_fingerprint += '/';
build_fingerprint += GetProperty("ro.product.device", UNKNOWN);
build_fingerprint += ':';
build_fingerprint += GetProperty("ro.build.version.release", UNKNOWN);
build_fingerprint += '/';
build_fingerprint += GetProperty("ro.build.id", UNKNOWN);
build_fingerprint += '/';
build_fingerprint += GetProperty("ro.build.version.incremental", UNKNOWN);
build_fingerprint += ':';
build_fingerprint += GetProperty("ro.build.type", UNKNOWN);
build_fingerprint += '/';
build_fingerprint += GetProperty("ro.build.tags", UNKNOWN);
LOG(INFO) << "Setting property 'ro.build.fingerprint' to '" << build_fingerprint << "'";
std::string error;
uint32_t res = PropertySet("ro.build.fingerprint", build_fingerprint, &error);
if (res != PROP_SUCCESS) {
LOG(ERROR) << "Error setting property 'ro.build.fingerprint': err=" << res << " (" << error
<< ")";
}
}
1.13 update_sys_usb_config-adb相关的,debug固件默认打开adb
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);
}
}
1.14 load_properties_from_file-从文件中加载属性
static bool load_properties_from_file(const char* filename, const char* filter,
std::map<std::string, std::string>* properties) {
Timer t;
auto file_contents = ReadFile(filename);
if (!file_contents.ok()) {
PLOG(WARNING) << "Couldn't load property file '" << filename
<< "': " << file_contents.error();
return false;
}
file_contents->push_back('\n');
// filter为null
LoadProperties(file_contents->data(), filter, filename, properties);
LOG(VERBOSE) << "(Loading properties from " << filename << " took " << t << ".)";
return true;
}
1.15 LoadProperties
static void LoadProperties(char* data, const char* filter, const char* filename,
std::map<std::string, std::string>* properties) {
char *key, *value, *eol, *sol, *tmp, *fn;
size_t flen = 0;
static constexpr const char* const kVendorPathPrefixes[2] = {
"/vendor",
"/odm",
};
// 从Android P开始,vendor和odm分区的属性都是u:r:vendor_init:s0类型的
// 可能有些属性vendor_init不能去设置
const char* context = kInitContext;
if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_P__) {
for (const auto& vendor_path_prefix : kVendorPathPrefixes) {
if (StartsWith(filename, vendor_path_prefix)) {
context = kVendorContext;
}
}
}
if (filter) {
flen = strlen(filter);
}
sol = data;
while ((eol = strchr(sol, '\n'))) {
key = sol;
*eol++ = 0;
sol = eol;
while (isspace(*key)) key++;
if (*key == '#') continue;
tmp = eol - 2;
while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;
if (!strncmp(key, "import ", 7) && flen == 0) {
fn = key + 7;
while (isspace(*fn)) fn++;
key = strchr(fn, ' ');
if (key) {
*key++ = 0;
while (isspace(*key)) key++;
}
std::string raw_filename(fn);
auto expanded_filename = ExpandProps(raw_filename);
if (!expanded_filename.ok()) {
LOG(ERROR) << "Could not expand filename ': " << expanded_filename.error();
continue;
}
load_properties_from_file(expanded_filename->c_str(), key, properties);
} else {
value = strchr(key, '=');
if (!value) continue;
*value++ = 0;
tmp = value - 2;
while ((tmp > key) && isspace(*tmp)) *tmp-- = 0;
while (isspace(*value)) value++;
if (flen > 0) {
if (filter[flen - 1] == '*') {
if (strncmp(key, filter, flen - 1) != 0) continue;
} else {
if (strcmp(key, filter) != 0) continue;
}
}
if (StartsWith(key, "ctl.") || key == "sys.powerctl"s ||
std::string{key} == kRestoreconProperty) {
LOG(ERROR) << "Ignoring disallowed property '" << key
<< "' with special meaning in prop file '" << filename << "'";
continue;
}
ucred cr = {.pid = 1, .uid = 0, .gid = 0};
std::string error;
if (CheckPermissions(key, value, context, cr, &error) == PROP_SUCCESS) {
auto it = properties->find(key);
if (it == properties->end()) {
// 建立map
(*properties)[key] = value;
} else if (it->second != value) {
LOG(WARNING) << "Overriding previous 'ro.' property '" << key << "':'"
<< it->second << "' with new value '" << value << "'";
it->second = value;
}
} else {
LOG(ERROR) << "Do not have permissions to set '" << key << "' to '" << value
<< "' in property file '" << filename << "': " << error;
}
}
}
}
1.16 StartPropertyService-启动propertyservice线程
void StartPropertyService(int* epoll_socket) {
// 设置属性
InitPropertySet("ro.property_service.version", "2");
int sockets[2];
// int socketpair(int domain, int type, int protocol, int sv[2]);
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_socket = sockets[1];
// 设置accept_messages为true
StartSendingMessages();
if (auto result = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
false, 0666, 0, 0, {});
result.ok()) {
property_set_fd = *result;
} else {
LOG(FATAL) << "start_property_service socket creation failed: " << result.error();
}
// socket四部曲:socket创建,socket bind,socket listen,socket accept
listen(property_set_fd, 8);
auto new_thread = std::thread{PropertyServiceThread};
// swap是相当于赋值操作
property_service_thread.swap(new_thread);
}
void StartSendingMessages() {
auto lock = std::lock_guard{accept_messages_lock};
accept_messages = true;
}
1.17 PropertyServiceThread-property线程
static void PropertyServiceThread() {
Epoll epoll;
if (auto result = epoll.Open(); !result.ok()) {
LOG(FATAL) << result.error();
}
// 处理设置属性
if (auto result = epoll.RegisterHandler(property_set_fd, handle_property_set_fd);
!result.ok()) {
LOG(FATAL) << result.error();
}
// 处理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)();
}
}
}
}
2. libpropertyinfoserializer模块
android/system/core/property_service/libpropertyinfoserializer
2.1 ParsePropertyInfoFile-解析property_context文件
// file_contents文件,true,property数据结构
void ParsePropertyInfoFile(const std::string& file_contents, bool require_prefix_or_exact,
std::vector<PropertyInfoEntry>* property_infos,
std::vector<std::string>* errors) {
// Do not clear property_infos to allow this function to be called on multiple files, with
// their results concatenated.
errors->clear();
for (const auto& line : Split(file_contents, "\n")) {
auto trimmed_line = Trim(line);
// 去掉#开头的
if (trimmed_line.empty() || StartsWith(trimmed_line, "#")) {
continue;
}
auto property_info_entry = PropertyInfoEntry{};
auto parse_error = std::string{};
if (!ParsePropertyInfoLine(trimmed_line, require_prefix_or_exact, &property_info_entry,
&parse_error)) {
errors->emplace_back(parse_error);
continue;
}
property_infos->emplace_back(property_info_entry);
}
}
2.2 ParsePropertyInfoLine-解析property_context文件
// ro.boot.btmacaddr u:object_r:bluetooth_prop:s0
// ro.boot.serialno u:object_r:serialno_prop:s0
// dalvik.vm.extra-opts u:object_r:exported_dalvik_prop:s0 exact string
// name context exact_match type
bool ParsePropertyInfoLine(const std::string& line, bool require_prefix_or_exact,
PropertyInfoEntry* out, std::string* error) {
auto tokenizer = SpaceTokenizer(line);
auto property = tokenizer.GetNext();
if (property.empty()) {
*error = "Did not find a property entry in '" + line + "'";
return false;
}
auto context = tokenizer.GetNext();
if (context.empty()) {
*error = "Did not find a context entry in '" + line + "'";
return false;
}
// It is not an error to not find exact_match or a type, as older files will not contain them.
auto match_operation = tokenizer.GetNext();
// We reformat type to be space deliminated regardless of the input whitespace for easier storage
// and subsequent parsing.
auto type_strings = std::vector<std::string>{};
auto type = tokenizer.GetNext();
// 说明有很多种type
while (!type.empty()) {
type_strings.emplace_back(type);
type = tokenizer.GetNext();
}
bool exact_match = false;
if (match_operation == "exact") {
exact_match = true;
} else if (match_operation != "prefix" && match_operation != "" && require_prefix_or_exact) {
*error = "Match operation '" + match_operation +
"' is not valid: must be either 'prefix' or 'exact'";
return false;
}
// "string", "bool", "int", "uint", "double", "size"};等于这些其中的一种;enum类型的另说
if (!type_strings.empty() && !IsTypeValid(type_strings)) {
*error = "Type '" + Join(type_strings, " ") + "' is not valid";
return false;
}
// 把type_string vector合成一个字符串的类型
*out = {property, context, Join(type_strings, " "), exact_match};
return true;
}
bool IsTypeValid(const std::vector<std::string>& type_strings) {
if (type_strings.empty()) {
return false;
}
// There must be at least one string following 'enum'
if (type_strings[0] == "enum") {
return type_strings.size() > 1;
}
// There should not be any string following any other types.
if (type_strings.size() != 1) {
return false;
}
// Check that the type matches one of remaining valid types.
static const char* const no_parameter_types[] = {"string", "bool", "int",
"uint", "double", "size"};
for (const auto& type : no_parameter_types) {
if (type_strings[0] == type) {
return true;
}
}
return false;
}
2.3 SpaceTokenizer类-获取空格分割的下一个字符串
SpaceTokenizer(const std::string& string)
: string_(string), it_(string_.begin()), end_(string_.end()) {}
std::string GetNext() {
auto next = std::string();
// space为间隔
while (it_ != end_ && !isspace(*it_)) {
next.push_back(*it_++);
}
while (it_ != end_ && isspace(*it_)) {
it_++;
}
return next;
}
2.4 GetProperty
std::string GetProperty(const std::string& key, const std::string& default_value) {
std::string property_value;
#if defined(__BIONIC__)
const prop_info* pi = __system_property_find(key.c_str());
if (pi == nullptr) return default_value;
__system_property_read_callback(pi,
[](void* cookie, const char*, const char* value, unsigned) {
auto property_value = reinterpret_cast<std::string*>(cookie);
*property_value = value;
},
&property_value);
#else
auto it = g_properties.find(key);
if (it == g_properties.end()) return default_value;
property_value = it->second;
#endif
// If the property exists but is empty, also return the default value.
// Since we can't remove system properties, "empty" is traditionally
// the same as "missing" (this was true for cutils' property_get).
return property_value.empty() ? default_value : property_value;
}
3. Trie模块-树型结构
android/system/core/property_service/libpropertyinfoserializer文件夹中
TrieBuilder(builder_root_根节点) -> TrieBuilderNode(PropertyEntryBuilder property_entry 存数据, children指向孩子节点)
PropertyEntryBuilder存放name,context,type;context和types都指向triebuild中的同一个context和type,以节省内存
TrieBuilder树形结构:
三种情况
(1)ro.boot. u:object_r:exported2_default_prop:s0
TrieBuilder -> builder_root_ -> ro -> boot boot新建一个node
(2)ro.boot.vendor.overlay.theme u:object_r:exported_overlay_prop:s0 exact string 将theme放到node中的exact_matches_中
TrieBuilder -> builder_root_ -> ro -> boot -> vendor -> overlay
(3)ro.boot.serialno u:object_r:serialno_prop:s0 将serialno放到node中的prefixes_
文件:
/dev/__properties__/property_info文件结构
24字节的PropertyInfoAreaHeader + contexts大小 + context_string_array(将context中strings在data中的位置保存到array中) + contexts数据 + types大小 + types_string_array + types数据 +
struct PropertyInfoAreaHeader {
// The current version of this data as created by property service.
uint32_t current_version; // 恒为1
// The lowest version of libc that can properly parse this data.
uint32_t minimum_supported_version; // 恒为1
uint32_t size; // 整个文件的大小
uint32_t contexts_offset; // contexts的offset
uint32_t types_offset;
uint32_t root_offset; // root node的offset
};
3.1 BuildTrie-将property_info格式化成一个字符串,然后写入到/dev/properties/property_info文件中
// "property数据","u:object_r:default_prop:s0", "string",要返回的一个字符串
bool BuildTrie(const std::vector<PropertyInfoEntry>& property_info,
const std::string& default_context, const std::string& default_type,
std::string* serialized_trie, std::string* error) {
// Check that names are legal first
auto trie_builder = TrieBuilder(default_context, default_type);
for (const auto& [name, context, type, is_exact] : property_info) {
if (!trie_builder.AddToTrie(name, context, type, is_exact, error)) {
return false;
}
}
auto trie_serializer = TrieSerializer();
// 返回字符串
*serialized_trie = trie_serializer.SerializeTrie(trie_builder);
return true;
}
3.2 TrieBuilder构造函数
TrieBuilder::TrieBuilder(const std::string& default_context, const std::string& default_type)
: builder_root_("root") {
auto* context_pointer = StringPointerFromContainer(default_context, &contexts_);
builder_root_.set_context(context_pointer);
auto* type_pointer = StringPointerFromContainer(default_type, &types_);
builder_root_.set_type(type_pointer);
}
std::set<std::string> contexts_; // 所有的树节点的context都指向这里
std::set<std::string> types_; // 所有树节点的types都指向这里
3.3 StringPointerFromContainer-将string放到set中,并返回一个指向它的iterator指针
const std::string* TrieBuilder::StringPointerFromContainer(const std::string& string,
std::set<std::string>* container) {
// Get a pointer to the string in a given set, such that we only ever serialize each string once.
// std::set::emplace函数会返回一个iterator(指向string)和true
auto [iterator, _] = container->emplace(string);
return &(*iterator);
}
3.4 AddToTrie-将propertyinfo按照顺序添加到TrieBuilder中
bool TrieBuilder::AddToTrie(const std::string& name, const std::string& context,
const std::string& type, bool exact, std::string* error) {
// 这里还是那个TrieBuilder::contexts_,所以所有的contexts都放在contexts_中
// 所有的type都放在types_中,而且他们都是set类型的,可以节省内存
auto* context_pointer = StringPointerFromContainer(context, &contexts_);
auto* type_pointer = StringPointerFromContainer(type, &types_);
return AddToTrie(name, context_pointer, type_pointer, exact, error);
}
bool TrieBuilder::AddToTrie(const std::string& name, const std::string* context,
const std::string* type, bool exact, std::string* error) {
TrieBuilderNode* current_node = &builder_root_;
auto name_pieces = Split(name, ".");
bool ends_with_dot = false;
// ro.boot. 所以分出来就是 ro boot 空
// ro.virtual_ab.enabled 所以分出来就是ro virtual_ab enabled
if (name_pieces.back().empty()) {
ends_with_dot = true;
name_pieces.pop_back();
}
// Move us to the final node that we care about, adding incremental nodes if necessary.
while (name_pieces.size() > 1) {
// 看是否有name一样的
auto child = current_node->FindChild(name_pieces.front());
if (child == nullptr) {
// 没有则添加到children_中
child = current_node->AddChild(name_pieces.front());
}
if (child == nullptr) {
*error = "Unable to allocate Trie node";
return false;
}
current_node = child;
name_pieces.erase(name_pieces.begin());
}
// 三种情况
// (1)ro.boot. u:object_r:exported2_default_prop:s0
// TrieBuilder -> builder_root_ -> ro -> boot
// (2)ro.boot.vendor.overlay.theme u:object_r:exported_overlay_prop:s0 exact string 将theme放到node中的exact_matches_中
// TrieBuilder -> builder_root_ -> ro -> boot -> vendor -> overlay
// (3)ro.boot.serialno u:object_r:serialno_prop:s0 将serialno放到node中的prefixes_
// Store our context based on what type of match it is.
if (exact) {
if (!current_node->AddExactMatchContext(name_pieces.front(), context, type)) {
*error = "Duplicate exact match detected for '" + name + "'";
return false;
}
} else if (!ends_with_dot) {
if (!current_node->AddPrefixContext(name_pieces.front(), context, type)) {
*error = "Duplicate prefix match detected for '" + name + "'";
return false;
}
} else {
auto child = current_node->FindChild(name_pieces.front());
if (child == nullptr) {
child = current_node->AddChild(name_pieces.front());
}
if (child == nullptr) {
*error = "Unable to allocate Trie node";
return false;
}
if (child->context() != nullptr || child->type() != nullptr) {
*error = "Duplicate prefix match detected for '" + name + "'";
return false;
}
child->set_context(context);
child->set_type(type);
}
return true;
}
TrieBuilderNode* AddChild(const std::string& name) { return &children_.emplace_back(name); }
// FindChild-看children_中是否有name一样的
TrieBuilderNode* FindChild(const std::string& name) {
for (auto& child : children_) {
if (child.name() == name) return &child;
}
return nullptr;
}
3.5 SerializeTrie-将TrieBuilder组成一个字符串
// 24字节的PropertyInfoAreaHeader + contexts大小 + context_string_array(将context中strings在data中的位置保存到array中) + contexts数据 + types大小 + types_string_array + types数据 + trie数据
std::string TrieSerializer::SerializeTrie(const TrieBuilder& trie_builder) {
// 新建一个TrieNodeArena对象,TrieNodeArena构造函数将current_data_pointer_初始化为0
arena_.reset(new TrieNodeArena());
// 在arena_中分配data_为48,PropertyInfoAreaHeader大小为24
auto header = arena_->AllocateObject<PropertyInfoAreaHeader>(nullptr);
header->current_version = 1;
header->minimum_supported_version = 1;
// Store where we're about to write the contexts.
// 返回的是current_data_pointer_,为24
header->contexts_offset = arena_->size();
// propertyinfo中的所有contexts都放在这里
SerializeStrings(trie_builder.contexts());
// Store where we're about to write the types.
header->types_offset = arena_->size();
SerializeStrings(trie_builder.types());
// We need to store size() up to this point now for Find*Offset() to work.
header->size = arena_->size();
// 写Node
uint32_t root_trie_offset = WriteTrieNode(trie_builder.builder_root());
header->root_offset = root_trie_offset;
// Record the real size now that we've written everything
header->size = arena_->size();
// resize一下data_,然后返回字符串
return arena_->truncated_data();
}
3.6 AllocateObject+为T对象在string的data中分配空间
// We can't return pointers to objects since data_ may move when reallocated, thus invalidating
// any pointers. Therefore we return an ArenaObjectPointer, which always accesses elements via
// data_ + offset.
template <typename T>
ArenaObjectPointer<T> AllocateObject(uint32_t* return_offset) {
uint32_t offset;
AllocateData(sizeof(T), &offset);
if (return_offset) *return_offset = offset;
return ArenaObjectPointer<T>(data_, offset);
}
3.7 AllocateData-string的data中分配大小
void* AllocateData(size_t size, uint32_t* offset) {
// size和uint32_t的大小对齐
size_t aligned_size = size + (sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1);
// 第一次:0 + 24 > 0; new_size = 48
// 如果data_的空间太小了,就分配一个两倍大小的空间
if (current_data_pointer_ + aligned_size > data_.size()) {
auto new_size = (current_data_pointer_ + aligned_size + data_.size()) * 2;
data_.resize(new_size, '\0');
}
if (offset) *offset = current_data_pointer_;
uint32_t return_offset = current_data_pointer_;
// 变为24了
current_data_pointer_ += aligned_size;
return &data_[0] + return_offset;
}
3.8 SerializeStrings-写string到arena_的data中
void TrieSerializer::SerializeStrings(const std::set<std::string>& strings) {
// 先存放string的大小
arena_->AllocateAndWriteUint32(strings.size());
// Allocate space for the array.
// 创建string.size * sizeof(uint32_t)那么大的空间,这个strings是set类型的
uint32_t offset_array_offset = arena_->AllocateUint32Array(strings.size());
// Write offset pointers and strings; these are already alphabetically sorted by virtue of being
// in an std::set.
auto it = strings.begin();
// 然后分配空间,然后往里写strings
for (unsigned int i = 0; i < strings.size(); ++i, ++it) {
uint32_t string_offset = arena_->AllocateAndWriteString(*it);
arena_->uint32_array(offset_array_offset)[i] = string_offset;
}
}
3.9 allocate相关的函数
void AllocateAndWriteUint32(uint32_t value) {
auto location = static_cast<uint32_t*>(AllocateData(sizeof(uint32_t), nullptr));
*location = value;
}
uint32_t AllocateUint32Array(int length) {
uint32_t offset;
AllocateData(sizeof(uint32_t) * length, &offset);
return offset;
}
void AllocateAndWriteUint32(uint32_t value) {
auto location = static_cast<uint32_t*>(AllocateData(sizeof(uint32_t), nullptr));
*location = value;
}
3.10 WriteTrieNode-写Node
node的格式:
struct TrieNodeInternal {
// This points to a property entry struct, which includes the name for this node
uint32_t property_entry;
// Children are a sorted list of child nodes_; binary search them.
uint32_t num_child_nodes;
uint32_t child_nodes;
// Prefixes are terminating prefix matches at this node, sorted longest to smallest
// Take the first match sequentially found with StartsWith().
uint32_t num_prefixes;
uint32_t prefix_entries;
// Exact matches are a sorted list of exact matches at this node_; binary search them.
uint32_t num_exact_matches;
uint32_t exact_match_entries;
};
格式就是:
这是一个Node:(TrieNodeInternal对象 + PropertyEntry + prefix的数组 + prefix的PropertyEntry + exact_matches的数组 + exact_matches的PropertyEntry + children的数组 + children的Node)以此循环
uint32_t TrieSerializer::WriteTrieNode(const TrieBuilderNode& builder_node) {
uint32_t trie_offset;
auto trie = arena_->AllocateObject<TrieNodeInternal>(&trie_offset);
trie->property_entry = WritePropertyEntry(builder_node.property_entry());
// Write prefix matches
auto sorted_prefix_matches = builder_node.prefixes();
// Prefixes are sorted by descending length
// 先进行排序
std::sort(sorted_prefix_matches.begin(), sorted_prefix_matches.end(),
[](const auto& lhs, const auto& rhs) { return lhs.name.size() > rhs.name.size(); });
trie->num_prefixes = sorted_prefix_matches.size();
uint32_t prefix_entries_array_offset = arena_->AllocateUint32Array(sorted_prefix_matches.size());
trie->prefix_entries = prefix_entries_array_offset;
for (unsigned int i = 0; i < sorted_prefix_matches.size(); ++i) {
uint32_t property_entry_offset = WritePropertyEntry(sorted_prefix_matches[i]);
arena_->uint32_array(prefix_entries_array_offset)[i] = property_entry_offset;
}
// Write exact matches
auto sorted_exact_matches = builder_node.exact_matches();
// Exact matches are sorted alphabetically
std::sort(sorted_exact_matches.begin(), sorted_exact_matches.end(),
[](const auto& lhs, const auto& rhs) { return lhs.name < rhs.name; });
trie->num_exact_matches = sorted_exact_matches.size();
uint32_t exact_match_entries_array_offset =
arena_->AllocateUint32Array(sorted_exact_matches.size());
trie->exact_match_entries = exact_match_entries_array_offset;
for (unsigned int i = 0; i < sorted_exact_matches.size(); ++i) {
uint32_t property_entry_offset = WritePropertyEntry(sorted_exact_matches[i]);
arena_->uint32_array(exact_match_entries_array_offset)[i] = property_entry_offset;
}
// Write children
auto sorted_children = builder_node.children();
std::sort(sorted_children.begin(), sorted_children.end(),
[](const auto& lhs, const auto& rhs) { return lhs.name() < rhs.name(); });
trie->num_child_nodes = sorted_children.size();
uint32_t children_offset_array_offset = arena_->AllocateUint32Array(sorted_children.size());
trie->child_nodes = children_offset_array_offset;
for (unsigned int i = 0; i < sorted_children.size(); ++i) {
arena_->uint32_array(children_offset_array_offset)[i] = WriteTrieNode(sorted_children[i]);
}
return trie_offset;
}
3.11 WritePropertyEntry-写每个PropertyEntry
uint32_t TrieSerializer::WritePropertyEntry(const PropertyEntryBuilder& property_entry) {
uint32_t context_index = property_entry.context != nullptr && !property_entry.context->empty()
? serialized_info()->FindContextIndex(property_entry.context->c_str())
: ~0u;
uint32_t type_index = property_entry.type != nullptr && !property_entry.type->empty()
? serialized_info()->FindTypeIndex(property_entry.type->c_str())
: ~0u;
uint32_t offset;
auto serialized_property_entry = arena_->AllocateObject<PropertyEntry>(&offset);
// 在data_中的offset
serialized_property_entry->name_offset = arena_->AllocateAndWriteString(property_entry.name);
serialized_property_entry->namelen = property_entry.name.size();
// 写在data_中的index
serialized_property_entry->context_index = context_index;
serialized_property_entry->type_index = type_index;
return offset;
}
const PropertyInfoArea* serialized_info() const {
// arena_->data()为string类型的,data()为char*类型 ,强制转换为PropertyInfoArea类型
return reinterpret_cast<const PropertyInfoArea*>(arena_->data().data());
}
3.12 FindContextIndex-找data_中context string的index
android/system/core/property_service/libpropertyinfoparser文件夹中
template <typename F>
int Find(uint32_t array_length, F&& f) {
// 采用二分法的方式进行查找
int bottom = 0;
int top = array_length - 1;
while (top >= bottom) {
int search = (top + bottom) / 2;
auto cmp = f(search);
if (cmp == 0) return search;
if (cmp < 0) bottom = search + 1;
if (cmp > 0) top = search - 1;
}
return -1;
}
} // namespace
// Binary search the list of contexts to find the index of a given context string.
// Only should be used for TrieSerializer to construct the Trie.
int PropertyInfoArea::FindContextIndex(const char* context) const {
// num_contexts返回contexts的条数,第一个写进去的就是context_string_size
return Find(num_contexts(), [this, context](auto array_offset) {
// data_中的contexts array的offset
auto string_offset = uint32_array(contexts_array_offset())[array_offset];
// 然后比较
return strcmp(c_string(string_offset), context);
});
}
uint32_t* uint32_array(uint32_t offset) {
return reinterpret_cast<uint32_t*>(data_.data() + offset);
}
3.13 LoadDefaultPath-每个进程都会打开/dev/properties/property_info文件
cat /proc/426/maps就可以看到了
bool PropertyInfoAreaFile::LoadDefaultPath() {
return LoadPath("/dev/__properties__/property_info");
}
bool PropertyInfoAreaFile::LoadPath(const char* filename) {
int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
struct stat fd_stat;
if (fstat(fd, &fd_stat) < 0) {
close(fd);
return false;
}
if ((fd_stat.st_uid != 0) || (fd_stat.st_gid != 0) ||
((fd_stat.st_mode & (S_IWGRP | S_IWOTH)) != 0) ||
(fd_stat.st_size < static_cast<off_t>(sizeof(PropertyInfoArea)))) {
close(fd);
return false;
}
auto mmap_size = fd_stat.st_size;
// null表示由内核指定一个地址来使用;size表示大小;READ表示只读;
// MAP_SHARED表示共享此映射。 映射的更新对映射此文件的其他进程可见,并传递到底层文件。 在调用 msync(2) 或 munmap() 之前,该文件实际上可能不会更新。
// fd;offset为0
void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd, 0);
if (map_result == MAP_FAILED) {
close(fd);
return false;
}
// 然后强制转换为PropertyInfoArea进行管理
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;
}
3.14 GetPropertyInfoIndexes-返回context的indexes
// ro.boot.boot_devices
void PropertyInfoArea::GetPropertyInfoIndexes(const char* name, uint32_t* context_index,
uint32_t* type_index) const {
uint32_t return_context_index = ~0u;
uint32_t return_type_index = ~0u;
const char* remaining_name = name;
// 这个是TrieNode类型,是/dev/__properties__/property_info文件中TrieNode数据的root节点
auto trie_node = root_node();
while (true) {
// 返回第一次遇到.的位置的指针
const char* sep = strchr(remaining_name, '.');
// Apply prefix match for prefix deliminated with '.'
// child_node的context_index为~0u的
if (trie_node.context_index() != ~0u) {
return_context_index = trie_node.context_index();
}
if (trie_node.type_index() != ~0u) {
return_type_index = trie_node.type_index();
}
// Check prefixes at this node. This comes after the node check since these prefixes are by
// definition longer than the node itself.
// 第一次是ro.boot.boot_devices是找不到的
// 最后一次是boot_devices也是找不到的
CheckPrefixMatch(remaining_name, trie_node, &return_context_index, &return_type_index);
if (sep == nullptr) {
break;
}
const uint32_t substr_size = sep - remaining_name;
TrieNode child_node;
// 第一次ro,找到了childnode
if (!trie_node.FindChildForString(remaining_name, substr_size, &child_node)) {
break;
}
// 第一次找到了child_node:ro
trie_node = child_node;
remaining_name = sep + 1;
}
// We've made it to a leaf node, so check contents and return appropriately.
// Check exact matches
// 这里是检查
for (uint32_t i = 0; i < trie_node.num_exact_matches(); ++i) {
if (!strcmp(c_string(trie_node.exact_match(i)->name_offset), remaining_name)) {
if (context_index != nullptr) {
if (trie_node.exact_match(i)->context_index != ~0u) {
*context_index = trie_node.exact_match(i)->context_index;
} else {
*context_index = return_context_index;
}
}
if (type_index != nullptr) {
if (trie_node.exact_match(i)->type_index != ~0u) {
*type_index = trie_node.exact_match(i)->type_index;
} else {
*type_index = return_type_index;
}
}
return;
}
}
// Check prefix matches for prefixes not deliminated with '.'
CheckPrefixMatch(remaining_name, trie_node, &return_context_index, &return_type_index);
// Return previously found prefix match.
if (context_index != nullptr) *context_index = return_context_index;
if (type_index != nullptr) *type_index = return_type_index;
return;
}
3.15 CheckPrefixMatch
// 检查prefix的
void PropertyInfoArea::CheckPrefixMatch(const char* remaining_name, const TrieNode& trie_node,
uint32_t* context_index, uint32_t* type_index) const {
const uint32_t remaining_name_size = strlen(remaining_name);
for (uint32_t i = 0; i < trie_node.num_prefixes(); ++i) {
auto prefix_len = trie_node.prefix(i)->namelen;
if (prefix_len > remaining_name_size) continue;
if (!strncmp(c_string(trie_node.prefix(i)->name_offset), remaining_name, prefix_len)) {
if (trie_node.prefix(i)->context_index != ~0u) {
*context_index = trie_node.prefix(i)->context_index;
}
if (trie_node.prefix(i)->type_index != ~0u) {
*type_index = trie_node.prefix(i)->type_index;
}
return;
}
}
}
bool TrieNode::FindChildForString(const char* name, uint32_t namelen, TrieNode* child) const {
auto node_index = Find(trie_node_base_->num_child_nodes, [this, name, namelen](auto array_offset) {
const char* child_name = child_node(array_offset).name();
// 第一次:比较ro,肯定可以找到的
int cmp = strncmp(child_name, name, namelen);
if (cmp == 0 && child_name[namelen] != '\0') {
// We use strncmp() since name isn't null terminated, but we don't want to match only a
// prefix of a child node's name, so we check here if we did only match a prefix and
// return 1, to indicate to the binary search to search earlier in the array for the real
// match.
return 1;
}
return cmp;
});
if (node_index == -1) {
return false;
}
// 使用child_node
*child = child_node(node_index);
return true;
}
4. system_property_area模块-SystemProperties的接口函数
android/bionic/libc/bionic文件夹中
4.1 __system_property_area_init-初始化操作
static SystemProperties system_properties;
#define PROP_FILENAME "/dev/__properties__"
int __system_property_area_init() {
bool fsetxattr_failed = false;
// /dev/__properties__下创建文件映射
return system_properties.AreaInit(PROP_FILENAME, &fsetxattr_failed) && !fsetxattr_failed ? 0 : -1;
}
4.2 __system_property_find-找属性
const prop_info* __system_property_find(const char* name) {
return system_properties.Find(name);
}
4.3 __system_property_add-添加属性
int __system_property_add(const char* name, unsigned int namelen, const char* value,
unsigned int valuelen) {
return system_properties.Add(name, namelen, value, valuelen);
}
5. SystemProperties类
android/bionic/libc/system_properties文件夹中
SystemProperties
contexts_
property_info_area_file_ /dev/__properties__/property_info
文件(首先在这里找context)
serial_prop_area_ /dev/__properties__/properties_serial
文件
pa_ 文件映射
context_nodes_ 每一个context_nodes代表一个/dev/__properties__/u:object_r:boottime_prop:s0
文件映射
pa_ 为对应的文件映射(找到context之后从这里找属性对应的值)
5.1 AreaInit-/dev/__properties
__下创建文件映射
bool SystemProperties::AreaInit(const char* filename, bool* fsetxattr_failed) {
if (strlen(filename) >= PROP_FILENAME_MAX) {
return false;
}
// "/dev/__properties__"
strcpy(property_filename_, filename);
// 构造ContextsSerialized对象,并将它放到contexts_data_内存中
contexts_ = new (contexts_data_) ContextsSerialized();
// /dev/__properties__下创建文件映射
if (!contexts_->Initialize(true, property_filename_, fsetxattr_failed)) {
return false;
}
initialized_ = true;
return true;
}
5.2 Find-找属性-先找context-再在文件中找name
const prop_info* SystemProperties::Find(const char* name) {
if (!initialized_) {
return nullptr;
}
// ro.boot.boot_devices,返回u:object_r:exported2_default_prop:s0文件映射
prop_area* pa = contexts_->GetPropAreaForName(name);
if (!pa) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Access denied finding property \"%s\"", name);
return nullptr;
}
// 在这里找,ro.boot.boot_devices,这里应该是null
return pa->find(name);
}
5.3 Add-添加属性
// ro.boot.boot_devices,soc@2900000/4020000.sdmmc.soc@2900000/4022000.sdmmc.soc@2900000
int SystemProperties::Add(const char* name, unsigned int namelen, const char* value,
unsigned int valuelen) {
if (valuelen >= PROP_VALUE_MAX && !is_read_only(name)) {
return -1;
}
if (namelen < 1) {
return -1;
}
if (!initialized_) {
return -1;
}
prop_area* serial_pa = contexts_->GetSerialPropArea();
if (serial_pa == nullptr) {
return -1;
}
// 先找context,再找pa
prop_area* pa = contexts_->GetPropAreaForName(name);
if (!pa) {
async_safe_format_log(ANDROID_LOG_ERROR, "libc", "Access denied adding property \"%s\"", name);
return -1;
}
// 添加到pa中
bool ret = pa->add(name, namelen, value, valuelen);
if (!ret) {
return -1;
}
// There is only a single mutator, but we want to make sure that
// updates are visible to a reader waiting for the update.
// add了之后,就会更新serial的,serial的值变为1了;可以用cache,来查询serial是否更新了
atomic_store_explicit(serial_pa->serial(),
// 初始值为0
atomic_load_explicit(serial_pa->serial(), memory_order_relaxed) + 1,
memory_order_release);
__futex_wake(serial_pa->serial(), INT32_MAX);
return 0;
}
6. ContextsSerialized类
6.1 Initialize-/dev/__properties
__下创建文件映射
bool ContextsSerialized::Initialize(bool writable, const char* filename, bool* fsetxattr_failed) {
filename_ = filename;
// mmap这个/dev/__properties__/property_info文件以及匿名映射
if (!InitializeProperties()) {
return false;
}
// 第一次初始化writable为true
if (writable) {
// 创建/dev/__properties__/文件夹
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) {
// 在这里Open,创建/dev/__properties__/u:object_r:boottime_prop:s0文件映射
if (!context_nodes_[i].Open(true, fsetxattr_failed)) {
open_failed = true;
}
}// 创建/dev/__properties__/properties_serial文件及其映射
if (open_failed || !MapSerialPropertyArea(true, fsetxattr_failed)) {
FreeAndUnmap();
return false;
}
} else {
if (!MapSerialPropertyArea(false, nullptr)) {
FreeAndUnmap();
return false;
}
}
return true;
}
6.2 InitializeProperties-mmap这个/dev/properties/property_info文件以及匿名映射
bool ContextsSerialized::InitializeProperties() {
// 调用到上面的system/core/property_service的模块中,mmap这个/dev/__properties__/property_info文件
if (!property_info_area_file_.LoadDefaultPath()) {
return false;
}
// 初始化[anon:System property context nodes]
if (!InitializeContextNodes()) {
FreeAndUnmap();
return false;
}
return true;
}
6.3 InitializeContextNodes-初始化[anon:System property context nodes]-创建ContextNode
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).
// anonymous map,匿名映射
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;
}
// cat /proc/pid/maps可以看到[anon:System property context nodes]
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;
for (size_t i = 0; i < num_context_nodes; ++i) {
// 构造ContextNode对象,并把它放在context_nodes_[i]内存中
// 就context名字 + filename
new (&context_nodes_[i]) ContextNode(property_info_area_file_->context(i), filename_);
}
return true;
}
6.4 MapSerialPropertyArea-创建/dev/__properties__/properties_serial
文件及其映射
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_ =
// 创建/dev/__properties__/properties_serial映射
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_;
}
6.5 GetPropAreaForName-返回u:object_r:exported2_default_prop:s0文件映射
prop_area* ContextsSerialized::GetPropAreaForName(const char* name) {
uint32_t index;
// ro.boot.boot_devices
property_info_area_file_->GetPropertyInfoIndexes(name, &index, nullptr);
// 有ro.boot.,所以index应该是不为~0u的,所以context是找得到的
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];
// 如果为null,则创建
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);
}
// 返回u:object_r:exported2_default_prop:s0文件
return context_node->pa();
}
7. ContextNode类
7.1 Open-创建/dev/properties/u:object_r:boottime_prop:s0文件映射
bool ContextNode::Open(bool access_rw, bool* fsetxattr_failed) {
lock_.lock();
if (pa_) {
lock_.unlock();
return true;
}
char filename[PROP_FILENAME_MAX];
// filename = /dev/__properties__/u:object_r:boottime_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) {
// 创建/dev/__properties__/u:object_r:boottime_prop:s0文件映射
pa_ = prop_area::map_prop_area_rw(filename, context_, fsetxattr_failed);
} else {
pa_ = prop_area::map_prop_area(filename);
}
lock_.unlock();
return pa_;
}
8. prop_area类
组织形式:
// +-----+ children +----+ children +--------+
// | |-------------->| ro |-------------->| secure |
// +-----+ +----+ +--------+
// / \ / |
// left / \ right left / | prop +===========+
// v v v +-------->| ro.secure |
// +-----+ +-----+ +-----+ +-----------+
// | net | | sys | | com | | 1 |
// +-----+ +-----+ +-----+ +===========+
左边的name的长度比较小,右边的name的长度比较大
8.1 map_prop_area_rw-创建/dev/properties/u:object_r:boottime_prop:s0文件映射
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...
*/
// 创建/dev/__properties__/u:object_r:boottime_prop:s0文件
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) {
// 设置/dev/__properties__/u:object_r:boottime_prop:s0文件的selinux属性为u:object_r:boottime_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;
}
}
}
// 设置文件大小为128K
if (ftruncate(fd, PA_SIZE) < 0) {
close(fd);
return nullptr;
}
pa_size_ = PA_SIZE;
pa_data_size_ = pa_size_ - sizeof(prop_area);
// 创建/dev/__properties__/u:object_r:boottime_prop:s0文件映射
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对象并保存到该文件映射中
prop_area* pa = new (memory_area) prop_area(PROP_AREA_MAGIC, PROP_AREA_VERSION);
close(fd);
return pa;
}
8.2 find
const prop_info* prop_area::find(const char* name) {
return find_property(root_node(), name, strlen(name), nullptr, 0, false);
}
8.3 find_property
// ro.boot.boot_devices,soc@2900000/4020000.sdmmc.soc@2900000/4022000.sdmmc.soc@2900000,true
const prop_info* prop_area::find_property(prop_bt* const trie, const char* name, uint32_t namelen,
const char* value, uint32_t valuelen,
bool alloc_if_needed) {
if (!trie) return nullptr;
const char* remaining_name = name;
prop_bt* current = trie;
while (true) {
// ro -> boot -> boot_devices
const char* sep = strchr(remaining_name, '.');
const bool want_subtree = (sep != nullptr);
const uint32_t substr_size = (want_subtree) ? sep - remaining_name : strlen(remaining_name);
if (!substr_size) {
return nullptr;
}
prop_bt* root = nullptr;
uint_least32_t children_offset = atomic_load_explicit(¤t->children, memory_order_relaxed);
if (children_offset != 0) {
root = to_prop_bt(¤t->children);
// 这里创建child
} else if (alloc_if_needed) {
uint_least32_t new_offset;
// 第一步:创建ro
// 第二步:创建boot
// 第三步:创建boot_devices
root = new_prop_bt(remaining_name, substr_size, &new_offset);
if (root) {
atomic_store_explicit(¤t->children, new_offset, memory_order_release);
}
}
if (!root) {
return nullptr;
}
// 用来找child的
current = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed);
if (!current) {
return nullptr;
}
if (!want_subtree) break;
remaining_name = sep + 1;
}
uint_least32_t prop_offset = atomic_load_explicit(¤t->prop, memory_order_relaxed);
if (prop_offset != 0) {
return to_prop_info(¤t->prop);
} else if (alloc_if_needed) {
uint_least32_t new_offset;
// 这里创建prop_info保存属性的值
prop_info* new_info = new_prop_info(name, namelen, value, valuelen, &new_offset);
if (new_info) {
atomic_store_explicit(¤t->prop, new_offset, memory_order_release);
}
return new_info;
} else {
return nullptr;
}
}
8.4 add-添加属性
bool prop_area::add(const char* name, unsigned int namelen, const char* value,
unsigned int valuelen) {
return find_property(root_node(), name, namelen, value, valuelen, true);
}
8.5 new_prop_bt
prop_bt* prop_area::new_prop_bt(const char* name, uint32_t namelen, uint_least32_t* const off) {
uint_least32_t new_offset;
// 分配空间,data + offset 以及offset对齐
void* const p = allocate_obj(sizeof(prop_bt) + namelen + 1, &new_offset);
if (p != nullptr) {
// 新建prop_bt
prop_bt* bt = new (p) prop_bt(name, namelen);
*off = new_offset;
return bt;
}
return nullptr;
}
8.6 allocate_obj
void* prop_area::allocate_obj(const size_t size, uint_least32_t* const off) {
const size_t aligned = __BIONIC_ALIGN(size, sizeof(uint_least32_t));
if (bytes_used_ + aligned > pa_data_size_) {
return nullptr;
}
*off = bytes_used_;
bytes_used_ += aligned;
return data_ + *off;
}
8.7 find_prop_bt-找child的prop_bt
// bt为刚新建的ro的bt,name为ro.boot.boot_devices,namelen为2,alloc为true
prop_bt* prop_area::find_prop_bt(prop_bt* const bt, const char* name, uint32_t namelen,
bool alloc_if_needed) {
prop_bt* current = bt;
while (true) {
if (!current) {
return nullptr;
}
// 所以第一步,ro的时候,ret==0直接返回
const int ret = cmp_prop_name(name, namelen, current->name, current->namelen);
if (ret == 0) {
return current;
}
if (ret < 0) {
uint_least32_t left_offset = atomic_load_explicit(¤t->left, memory_order_relaxed);
if (left_offset != 0) {
current = to_prop_bt(¤t->left);
} else {
if (!alloc_if_needed) {
return nullptr;
}
uint_least32_t new_offset;
prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset);
if (new_bt) {
atomic_store_explicit(¤t->left, new_offset, memory_order_release);
}
return new_bt;
}
} else {
uint_least32_t right_offset = atomic_load_explicit(¤t->right, memory_order_relaxed);
if (right_offset != 0) {
current = to_prop_bt(¤t->right);
} else {
if (!alloc_if_needed) {
return nullptr;
}
uint_least32_t new_offset;
prop_bt* new_bt = new_prop_bt(name, namelen, &new_offset);
if (new_bt) {
atomic_store_explicit(¤t->right, new_offset, memory_order_release);
}
return new_bt;
}
}
}
}
static int cmp_prop_name(const char* one, uint32_t one_len, const char* two, uint32_t two_len) {
if (one_len < two_len)
return -1;
else if (one_len > two_len)
return 1;
else
return strncmp(one, two, one_len);
}
8.8 new_prop_info-新建保存属性的值
prop_info* prop_area::new_prop_info(const char* name, uint32_t namelen, const char* value,
uint32_t valuelen, uint_least32_t* const off) {
uint_least32_t new_offset;
void* const p = allocate_obj(sizeof(prop_info) + namelen + 1, &new_offset);
if (p == nullptr) return nullptr;
prop_info* info;
if (valuelen >= PROP_VALUE_MAX) {
uint32_t long_value_offset = 0;
char* long_location = reinterpret_cast<char*>(allocate_obj(valuelen + 1, &long_value_offset));
if (!long_location) return nullptr;
memcpy(long_location, value, valuelen);
long_location[valuelen] = '\0';
// Both new_offset and long_value_offset are offsets based off of data_, however prop_info
// does not know what data_ is, so we change this offset to be an offset from the prop_info
// pointer that contains it.
long_value_offset -= new_offset;
info = new (p) prop_info(name, namelen, long_value_offset);
} else {
info = new (p) prop_info(name, namelen, value, valuelen);
}
*off = new_offset;
return info;
}
问题
1. 烧了GSI之后,lmkd设置的属性不生效
ro.lmk.use_new_strategy u:object_r:exported3_default_prop:s0 exact bool
在vendor目录设置的属性的context,被放在了system/system_ext目录下了,烧了GSI之后就没有了
没有context,就不会去设置属性了,就解析不出来了
补充
1. c++
1.1 vector
1.1.1 back函数-返回最后一个函数的值
1.1.2 end函数-返回一个iterator
一般用法是在循环中:
for (std::vector<int>::iterator it = myvector.begin() ; it != myvector.end(); ++it)
std::cout << ' ' << *it;
1.2 const成员函数
// 在C++中,只有被声明为const的成员函数才能被一个const类对象调用。
class Screen {
public:
char get(int x,int y);
char get(int x,int y) const; // 被const类对象调用,const Screen blankScreen;
};
1.3 reinterpret_cast-指针转换
对数据进行重新解释:
struct到基本类型之间的转换,如int类型的:
struct S1 { int a; } s1;
int* p1 = reinterpret_cast<int*>(&s1);
如const char*类型的:
class SerializedData {
private:
const char data_base_[0];
};
class PropertyInfoArea : private SerializedData {}
reinterpret_cast<const PropertyInfoArea*>(arena_->data().data());
参考
1.C++的const类成员函数
https://blog.csdn.net/lihao21/article/details/8634876