second_stage_property模块

目录

概述

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(&current->children, memory_order_relaxed);
    if (children_offset != 0) {
      root = to_prop_bt(&current->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(&current->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(&current->prop, memory_order_relaxed);
  if (prop_offset != 0) {
    return to_prop_info(&current->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(&current->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(&current->left, memory_order_relaxed);
      if (left_offset != 0) {
        current = to_prop_bt(&current->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(&current->left, new_offset, memory_order_release);
        }
        return new_bt;
      }
    } else {
      uint_least32_t right_offset = atomic_load_explicit(&current->right, memory_order_relaxed);
      if (right_offset != 0) {
        current = to_prop_bt(&current->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(&current->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
posted @ 2021-06-18 20:13  pyjetson  阅读(762)  评论(0编辑  收藏  举报