前言

SystemProperties 是Android系统中的一个关键机制,用于存储和管理系统级别的配置信息和参数。这些参数影响系统行为、硬件配置、调试选项、设备特性等。
它类似于“全局变量”或“配置项”,可以由多个系统组件读写,且具备以下特点

  • 只读/只写访问限制:某些属性只能由系统权限较高的组件修改。
  • 层级存储:存储在内存中,重启后可能保持或清除(依赖属性的定义和管理方式)。
  • 属性名称常以类似“路径”的格式组织,例如: ro.build.version.release ro.product.model
  • 属性以字符串键值对存储

以下是一些常用属性
在这里插入图片描述

一、属性服务property_service的启动流程

安卓版本 :10
平台:MTK

1.1 init–>SecondStageMain(argc, argv)

property_serviceinit进程 来初始化和启动的

system\core\init\main.cpp
int main(int argc, char** argv) {
#if __has_feature(address_sanitizer)
    __asan_set_error_report_callback(AsanReportCallback);
#endif

    if (!strcmp(basename(argv[0]), "ueventd")) {
        return ueventd_main(argc, argv);
    }
    
    if (argc > 1) {
        if (!strcmp(argv[1], "subcontext")) {
            android::base::InitLogging(argv, &android::base::KernelLogger);
            const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap();

            return SubcontextMain(argc, argv, &function_map);
        }

        if (!strcmp(argv[1], "selinux_setup")) {
            return SetupSelinux(argv);
        }

        if (!strcmp(argv[1], "second_stage")) {
            return SecondStageMain(argc, argv);
        }
    }

    return FirstStageMain(argc, argv);
}

init进程的main方法中,通过命令行参数判断程序启动时执行的动作,分别执行
FirstStageMain(argc, argv),
SubcontextMain(argc, argv, &function_map),
SetupSelinux(argv),
SecondStageMain(argc, argv)方法,在SecondStageMain()方法中执行propertyInit()方法初始化系统属性,启动属性服务。

system\core\init\init.cpp
int SecondStageMain(int argc, char** argv) {
    ...
    property_init();//属性初始化
    ...
    property_load_boot_defaults(load_debug_prop);//加载开机默认属性配置
    StartPropertyService(&epoll);//启动属性服务
    ...
}

属性初始化、加载开机默认属性配置、启动属性服务

1.1.1 property_init // 将xxx_property_contexts加载到共享内存

system\core\init\property_service.cpp
void property_init() {
    mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
    CreateSerializedPropertyInfo();
    if (__system_property_area_init()) {
        LOG(FATAL) << "Failed to initialize property area";
    }
    if (!property_info_area.LoadDefaultPath()) {
        LOG(FATAL) << "Failed to load serialized property info file";
    }
}
.....
void CreateSerializedPropertyInfo() {
    auto property_infos = std::vector<PropertyInfoEntry>();
    if (access("/system/etc/selinux/plat_property_contexts", R_OK) != -1) {
        if (!LoadPropertyInfoFromFile("/system/etc/selinux/plat_property_contexts",
                                      &property_infos)) {
            return;
        }
        // Don't check for failure here, 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 (!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;
        }
        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);
}

property_init 做了以下操作:
一、创建目录 /dev/properties
二、调用 CreateSerializedPropertyInfo()

  • 2.1 创建一个空向量property_infos,用于存储所有属性条目。std::vector< PropertyInfoEntry >();
  • 2.2 分路径加载属性上下文文件 LoadPropertyInfoFromFile(“/xxx_contexts”, &property_infos),加载到向量property_infos
  • 2.3 将 property_infos 转换为 trie 结构(高效的字符串查找树),用于快速查找属性上下文,输出到serialized_contexts
  • 2.4 将序列化的 trie 数据serialized_contexts写入 /dev/__ properties __/property_info,权限为 0444(只读)。
  • 2.5 selinux_android_restorecon:恢复文件的 SELinux 上下文,确保 /dev/__ properties __/property_info 的安全标签正确。

三、__system_property_area_init()      初始化属性共享内存区域
四、property_info_area.LoadDefaultPath()     将序列化的 trie 数据property_info加载到内存,用于运行时检查系统属性的 SELinux 上下文

1.1.1.1 __system_property_area_init()
bionic/libc/bionic/system_property_api.cpp
int __system_property_area_init() {
  bool fsetxattr_failed = false;
  return system_properties.AreaInit(PROP_FILENAME, &fsetxattr_failed) && !fsetxattr_failed ? 0 : -1;
}
bionic/libc/system_properties/system_properties.cpp
bool SystemProperties::AreaInit(const char* filename, bool* fsetxattr_failed) {
  if (strlen(filename) >= PROP_FILENAME_MAX) {
    return false;
  }
  strcpy(property_filename_, filename);

  contexts_ = new (contexts_data_) ContextsSerialized();
  if (!contexts_->Initialize(true, property_filename_, fsetxattr_failed)) {
    return false;
  }
  initialized_ = true;
  return true;
}
bionic/libc/system_properties/contexts_serialized.cpp
bool ContextsSerialized::InitializeProperties() {
  if (!property_info_area_file_.LoadDefaultPath()) {
    return false;
  }

  if (!InitializeContextNodes()) {
    FreeAndUnmap();
    return false;
  }

  return true;
}

bool ContextsSerialized::Initialize(bool writable, const char* filename, bool* fsetxattr_failed) {
  filename_ = filename;
  if (!InitializeProperties()) {
    return false;
  }
  ......
}
1.1.1.2 property_info_area.LoadDefaultPath()
system/core/property_service/libpropertyinfoparser/property_info_parser.cpp
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;

  void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd, 0);
  if (map_result == MAP_FAILED) {
    close(fd);
    return false;
  }

  auto property_info_area = reinterpret_cast<PropertyInfoArea*>(map_result);
  if (property_info_area->minimum_supported_version() > 1 ||
      property_info_area->size() != mmap_size) {
    munmap(map_result, mmap_size);
    close(fd);
    return false;
  }

  close(fd);
  mmap_base_ = map_result;
  mmap_size_ = mmap_size;
  return true;
}

老实说 system_property_area_init() 和property_info_area.LoadDefaultPath() 看的不太懂,感觉它俩重复调用了mmap(), 将/dev/ property __/property_info加载到共享内存。 如果有大神对这块很熟悉,麻烦在评论区讲解下

1.1.2 property_load_boot_defaults(load_debug_prop);加载开机默认属性配置

system/core/init/property_service.cpp
void property_load_boot_defaults(bool load_debug_prop) {
    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("/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("/product_services/build.prop", nullptr, &properties);
    load_properties_from_file("/factory/factory.prop", "ro.*", &properties);

    if (load_debug_prop) {
        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();//初始化 ro_product前缀的只读属性
    property_derive_build_fingerprint();//初始化编译相关属性

    update_sys_usb_config();//设置persist.sys.usb.config属性,用于控制USB调试和文件传输功能
}

将多个属性文件.prop中系统属性加载至properties变量,再通过PropertySet()将properties添加到系统中,并初始化只读、编译、usb相关属性值。

1.1.2.1 property_initialize_ro_product_props
system/core/init/property_service.cpp
// 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", "product_services", "system", "vendor",
    };
    const char* RO_PRODUCT_PROPS_DEFAULT_SOURCE_ORDER =
            "product,product_services,odm,vendor,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;
            }
        }
    }
}
  • 功能:初始化 ro.product.[brand|device|manufacturer|model|name] 属性,如果未设置,则从分区特定属性(如 ro.product.system.brand)派生值。
  • 流程
    1. 获取 ro.product.property_source_order,验证其合法性,否则使用默认顺序。
    2. 遍历每个 ro.product.* 属性,检查是否已设置。
    3. 如果未设置,按分区顺序查找对应的 ro.product.<source>.* 属性,设置第一个非空值。
  • 错误处理
    • 非法分区顺序:回退到默认顺序。
    • 属性设置失败:记录错误日志,继续处理其他属性。

1.1.2.2 property_derive_build_fingerprint
system/core/init/property_service.cpp
// 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";
#ifndef RECOVERY
    build_fingerprint = GetProperty("ro.product.brand", UNKNOWN);
#else
    build_fingerprint = "alps";
#endif
    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
                   << ")";
    }
}
  • 功能:检查 ro.build.fingerprint 是否已设置。如果未设置,从以下属性派生:
    ro.product.brand, ro.product.name, ro.product.device
    ro.build.version.release, ro.build.id, ro.build.version.incremental
    ro.build.type, ro.build.tags
    在 recovery 模式下,brand 固定为 “alps”。

  • 格式:brand/name/device:version.release/id/version.incremental:type/tags

  • 特殊处理:recovery 模式下使用 “alps” 作为品牌。

1.1.2.3 update_sys_usb_config
// persist.sys.usb.config values can't be combined on build-time when property
// files are split into each partition.
// So we need to apply the same rule of build/make/tools/post_process_props.py
// on runtime.
static void update_sys_usb_config() {
    bool is_debuggable = android::base::GetBoolProperty("ro.debuggable", false);
    std::string config = android::base::GetProperty("persist.sys.usb.config", "");
    if (config.empty()) {
        property_set("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");
        property_set("persist.sys.usb.config", config);
    }
}
  • 功能:动态更新 persist.sys.usb.config 属性,确保 USB 配置符合设备调试状态。
    如果属性为空,根据 ro.debuggable 设置为 "adb"(debuggable)"none"(非 debuggable)
    如果设备是 debuggable 构建且属性不含 “adb”,追加 ",adb"
  • 目的
    解决构建时属性文件分割(system.prop、vendor.prop 等)导致的 persist.sys.usb.config 配置不一致问题,模仿 build/make/tools/post_process_props.py 的逻辑。

1.1.3 StartPropertyService(&epoll);//启动属性服务

system/core/init/property_service.cpp
void StartPropertyService(Epoll* epoll) {
    selinux_callback cb;
    cb.func_audit = SelinuxAuditCallback;
    selinux_set_callback(SELINUX_CB_AUDIT, cb);
    //这里设置了属性ro.property_service.version
    property_set("ro.property_service.version", "2");
    property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
                                   false, 0666, 0, 0, nullptr);
    if (property_set_fd == -1) {
        PLOG(FATAL) << "start_property_service socket creation failed";
    }
    listen(property_set_fd, 8);//设置Socket连接数为8
    //注册epoll,监听property_set_fd改变时调用handle_property_set_fd
    if (auto result = epoll->RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
        PLOG(FATAL) << result.error();
    }
}
static void handle_property_set_fd() {
    ...
    switch (cmd) {
    case PROP_MSG_SETPROP: {//设置属性
        ...
        uint32_t result =
            HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
        ...
    }
    case PROP_MSG_SETPROP2: {
        ...
        uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error);
        ...
    }
    ...
}
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
                           const std::string& source_context, const ucred& cr, std::string* error) {
    //检查prop 权限
    if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) {
        return ret;
    }
    if (StartsWith(name, "ctl.")) {//ctl属性:ctl.start启动服务,ctl.stop关闭服务
        HandleControlMessage(name.c_str() + 4, value, cr.pid);
        return PROP_SUCCESS;
    }
    // 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") {//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 (name == "selinux.restorecon_recursive") {
        return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
    }
    return PropertySet(name, value, error);//设置属性
}

//检查prop 权限
uint32_t CheckPermissions(const std::string& name, const std::string& value,
                          const std::string& source_context, const ucred& cr, std::string* error) {
    //检查属性名称是否正确:1、属性名长度需不小于1;2、开头与结尾不能为.;3、字符:0~9+a~z+A~Z+'.'+'@'+'-'+'_'+':'
    if (!IsLegalPropertyName(name)) {
        *error = "Illegal property name";
        return PROP_ERROR_INVALID_NAME;
    }
    if (StartsWith(name, "ctl.")) {
    	//检查ctl控制属性selinux权限
        if (!CheckControlPropertyPerms(name, value, source_context, cr)) {
            *error = StringPrintf("Invalid permissions to perform '%s' on '%s'", name.c_str() + 4,
                                  value.c_str());
            return PROP_ERROR_HANDLE_CONTROL_MESSAGE;
        }
        return PROP_SUCCESS;
    }
    ...
    if (!CheckMacPerms(name, target_context, source_context.c_str(), cr)) {
        *error = "SELinux permission check failed";
        return PROP_ERROR_PERMISSION_DENIED;
    }
	...
    return PROP_SUCCESS;
}

static bool CheckMacPerms(const std::string& name, const char* target_context,
                          const char* source_context, const ucred& cr) {
	...
	//检查selinux权限,property_service对该属性是否有set权限
    bool has_access = (selinux_check_access(source_context, target_context, "property_service",
                                            "set", &audit_data) == 0);
    ...
}
...
static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
    size_t valuelen = value.size();
    if (!IsLegalPropertyName(name)) {//检测属性合法性
        *error = "Illegal property name";
        return PROP_ERROR_INVALID_NAME;
    }
    if (valuelen >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {
        *error = "Property value too long";
        return PROP_ERROR_INVALID_VALUE;
    }
    if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
        *error = "Value is not a UTF8 encoded string";
        return PROP_ERROR_INVALID_VALUE;
    }
    //检测属性是否已存在
    prop_info* pi = (prop_info*) __system_property_find(name.c_str());
    if (pi != nullptr) {
        // ro.* properties are actually "write-once".
        if (StartsWith(name, "ro.")) {
            *error = "Read-only property was already set";
            return PROP_ERROR_READ_ONLY_PROPERTY;
        }
        //属性已存在,并且非ro只读属性,更新属性值
        __system_property_update(pi, value.c_str(), valuelen);
    } else {
        //属性不存在,添加属性值
        int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
        if (rc < 0) {
            *error = "__system_property_add failed";
            return PROP_ERROR_SET_FAILED;
        }
    }
    // Don't write properties to disk until after we have read all default
    // properties to prevent them from being overwritten by default values.
    //避免在load所有属性之前将属性写入disk,防止属性值被覆盖。
    if (persistent_properties_loaded && StartsWith(name, "persist.")) {
        WritePersistentProperty(name, value);//将persist属性持久化disk和/data/property/persistent_properties
    }
    property_changed(name, value);//特殊属性值(如sys.powerctl)改变后系统需要立即处理。
    return PROP_SUCCESS;
}

  • 功能:StartPropertyService 初始化 Android 属性服务,创建并监听 Unix 域 socket,注册到 epoll 实例以处理属性设置请求。

  • 流程
    设置 SELinux 审计回调,记录权限相关日志。
    设置 ro.property_service.version 为 “2”。
    创建非阻塞流式 socket(/dev/socket/property_service)。
    启动监听,允许最多 8 个待处理连接。
    将 socket 注册到 epoll,绑定事件处理函数 handle_property_set_fd

二、prop的get和set源码流程

涉及的代码路径汇总如下:

frameworks\base\core\java\android\os\SystemProperties.java
frameworks\base\core\jni\android_os_SystemProperties.cpp
system\core\base\properties.cpp
system\core\init\main.cpp
system\core\init\init.cpp
system\core\init\property_service.cpp
system\core\property_service\libpropertyinfoparser\property_info_parser.cpp
bionic\libc\include\sys\_system_properties.h
bionic\libc\include\sys\system_properties.h
bionic\libc\bionic\system_property_set.cpp
bionic\libc\bionic\system_property_api.cpp
bionic\libc\system_properties\contexts_serialized.cpp
bionic\libc\system_properties\system_properties.cpp
bionic\libc\system_properties\prop_area.cpp

系统属性架构设计如下:

在这里插入图片描述

SystemProperties提供了setprop和多种返回数据类型的getprop,采用键值对(key-value)的数据格式进行操作,具体如下:

frameworks/base/core/java/android/os/SystemProperties.java
public class SystemProperties { 
...
   @UnsupportedAppUsage
   public static final int PROP_NAME_MAX = Integer.MAX_VALUE;//自android 8开始,取消对属性名长度限制
   public static final int PROP_VALUE_MAX = 91;
   ...
   public static String get(@NonNull String key)
   public static String get(@NonNull String key, @Nullable String def)
   public static int getInt(@NonNull String key, int def)
   public static long getLong(@NonNull String key, long def)
   public static boolean getBoolean(@NonNull String key, boolean def)
   public static void set(@NonNull String key, @Nullable String val)
   public static void addChangeCallback(@NonNull Runnable callback) 
...
  //获取属性key的值,如果没有该属性则返回默认值def
   public static String get(@NonNull String key, @Nullable String def) {
       if (TRACK_KEY_ACCESS) onKeyAccess(key);
       return native_get(key, def);
   }
...
   //设置属性key的值为val,其不可为空、不能是"ro."开头的只读属性
   //长度不能超过PROP_VALUE_MAX(91)
   public static void set(@NonNull String key, @Nullable String val) {
       if (val != null && !val.startsWith("ro.") && val.length() > PROP_VALUE_MAX) {
           throw new IllegalArgumentException("value of system property '" + key
                   + "' is longer than " + PROP_VALUE_MAX + " characters: " + val);
       }
       if (TRACK_KEY_ACCESS) onKeyAccess(key);
       native_set(key, val);
   }
}

通过JNI上述 native_set()native_get()走到

frameworks/base/core/jni/android_os_SystemProperties.cpp
//获取属性的方法最终调用GetProperty()
jstring SystemProperties_getSS(JNIEnv *env, jclass clazz, jstring keyJ,
                               jstring defJ)
{
    auto handler = [&](const std::string& key, jstring defJ) {
        std::string prop_val = android::base::GetProperty(key, "");
        ...
    };
    return ConvertKeyAndForward(env, keyJ, defJ, handler);
}
jstring SystemProperties_getS(JNIEnv *env, jclass clazz, jstring keyJ)
{
    return SystemProperties_getSS(env, clazz, keyJ, nullptr);
}

//设置属性的接口最终调用SetProperty()
void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ,
                          jstring valJ)
{
    auto handler = [&](const std::string& key, bool) {
        ...
        return android::base::SetProperty(key, val);
    };
    ...
}
...

跟着调用关系,接着进入:

system/core/base/properties.cpp
//调用__system_property_find()查找属性值
std::string GetProperty(const std::string& key, const std::string& default_value) {
  ...
  const prop_info* pi = __system_property_find(key.c_str());
  ...
}
//调用__system_property_set()设置属性值
bool SetProperty(const std::string& key, const std::string& value) {
  return (__system_property_set(key.c_str(), value.c_str()) == 0);
}

2.1 set prop流程

_system_properties.h 头文件定义 PROP_SERVICE_NAME

bionic/libc/include/sys/_system_properties.h
#define PROP_SERVICE_NAME "property_service"
bionic/libc/bionic/system_property_set.cpp
static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
static const char* kServiceVersionPropertyName = "ro.property_service.version";
...
int __system_property_set(const char* key, const char* value) {
  if (g_propservice_protocol_version == 0) {
    detect_protocol_version();//获取属性服务协议版本
  }
  //旧协议版本限定prop name最大长度为PROP_NAME_MAX,prop val最大长度为PROP_VALUE_MAX
  if (g_propservice_protocol_version == kProtocolVersion1) {
    //在bionic\libc\include\sys\system_properties.h中定义
    //#define PROP_NAME_MAX   32
    //#define PROP_VALUE_MAX  92
    if (strlen(key) >= PROP_NAME_MAX) return -1;
    if (strlen(value) >= PROP_VALUE_MAX) return -1;
    ...
    return send_prop_msg(&msg);//send_prop_msg()也通过Socket与property_service进行通信
  } else {//进入新版本协议,仅对prop val长度有要求,不超过92,且属性不为只读属性
    // New protocol only allows long values for ro. properties only.
    if (strlen(value) >= PROP_VALUE_MAX && strncmp(key, "ro.", 3) != 0) return -1;
    ...
    SocketWriter writer(&connection);//通过Socket与property_service进行通信
    ...
  }
...
static const char* kServiceVersionPropertyName = "ro.property_service.version";
static constexpr uint32_t kProtocolVersion1 = 1;
static constexpr uint32_t kProtocolVersion2 = 2;  // current
static atomic_uint_least32_t g_propservice_protocol_version = 0;
static void detect_protocol_version() {
  //从ro.property_service.version中获取协议版本,可在平台终端getprop ro.property_service.version
  //在后续2.2.5小节中可找到设置该属性的位置
  if (__system_property_get(kServiceVersionPropertyName, value) == 0) {
    g_propservice_protocol_version = kProtocolVersion1;
  } else {
    uint32_t version = static_cast<uint32_t>(atoll(value));
    if (version >= kProtocolVersion2) {
      g_propservice_protocol_version = kProtocolVersion2;
    } else {
      g_propservice_protocol_version = kProtocolVersion1;
    }
  ...
}

回顾 1.1.3小节 StartPropertyService(&epoll);//启动属性服务

socket 通信方式进行了值的set操作

2.1.1 更新和添加属性

bionic/libc/bionic/system_property_api.cpp
int __system_property_update(prop_info* pi, const char* value, unsigned int len) {
  return system_properties.Update(pi, value, len);//更新属性值
}
int __system_property_add(const char* name, unsigned int namelen, const char* value,
                          unsigned int valuelen) {
  return system_properties.Add(name, namelen, value, valuelen);//添加属性值
}
bionic/libc/system_properties/system_properties.cpp
int SystemProperties::Update(prop_info* pi, const char* value, unsigned int len) {
  ...
  prop_area* pa = contexts_->GetSerialPropArea();
  uint32_t serial = atomic_load_explicit(&pi->serial, memory_order_relaxed);
  serial |= 1;
  atomic_store_explicit(&pi->serial, serial, memory_order_relaxed);
  atomic_thread_fence(memory_order_release);
  strlcpy(pi->value, value, len + 1);//属性值更新
  ...
  return 0;
}
int SystemProperties::Add(const char* name, unsigned int namelen, const char* value,
                          unsigned int valuelen) {
  ...
  prop_area* serial_pa = contexts_->GetSerialPropArea();
  prop_area* pa = contexts_->GetPropAreaForName(name);
  bool ret = pa->add(name, namelen, value, valuelen);//向共享内存添加新属性
  ...
  return 0;
}
bionic/libc/system_properties/prop_area.cpp
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);
}
...
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) {
    ...
    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);
    } else if (alloc_if_needed) {//未找到时新建节点
      uint_least32_t new_offset;
      root = new_prop_bt(remaining_name, substr_size, &new_offset);
      if (root) {
        atomic_store_explicit(&current->children, new_offset, memory_order_release);
      }
    }
    ...
    current = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed);
    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);//返回已存在的prop_info
  } else if (alloc_if_needed) {
    uint_least32_t new_offset;
    //添加新属性
    prop_info* new_info = new_prop_info(name, namelen, value, valuelen, &new_offset);
    ...
  }
}
prop_info* prop_area::new_prop_info(const char* name, uint32_t namelen, const char* value,
                                    uint32_t valuelen, uint_least32_t* const off) {
  ...
  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';
    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;
}

从上述代码可分析出设置属性流程中,根据所设置的属性值是否存在分别走update()和add()流程,而add 最后调用查找属性方法,如果不存在则新建共享内存节点,将prop_info存入。自此,set prop流程结束。

2.2 get prop流程

承接

system/core/base/properties.cpp

中的 __system_property_find(key.c_str())

bionic/libc/bionic/system_property_api.cpp
const prop_info* __system_property_find(const char* name) {
  return system_properties.Find(name);
}
bionic/libc/system_properties/system_properties.cpp
const prop_info* SystemProperties::Find(const char* name) {
  ...
  prop_area* pa = contexts_->GetPropAreaForName(name);
  ...
  return pa->find(name);
}
bionic/libc/system_properties/prop_area.cpp
const prop_info* prop_area::find(const char* name) {
  return find_property(root_node(), name, strlen(name), nullptr, 0, false);
}
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) {
    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);
    } else if (alloc_if_needed) {//未找到时新建节点
      uint_least32_t new_offset;
      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;
    }

    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);//返回已存在的prop_info
  } else if (alloc_if_needed) {//添加新属性
    uint_least32_t new_offset;
    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;
  }
}

三、代码中使用属性

3.1 java中使用系统属性

frameworks/base/core/java/android/os/SystemProperties.java
...
public class SystemProperties {
...
    //获取属性key的值,如果没有该属性则返回默认值def
    @SystemApi
    public static String get(@NonNull String key, @Nullable String def) {
        if (TRACK_KEY_ACCESS) onKeyAccess(key);
        return native_get(key, def);
    }
...
    //设置属性key的值为val
    @SystemApi
    public static void set(@NonNull String key, @Nullable String val) {
        if (val != null && !val.startsWith("ro.") && val.length() > PROP_VALUE_MAX) {
            throw new IllegalArgumentException("value of system property '" + key
                    + "' is longer than " + PROP_VALUE_MAX + " characters: " + val);
        }
        if (TRACK_KEY_ACCESS) onKeyAccess(key);
        native_set(key, val);
    }
....
}

3.1.1 普通应用调用

由于SystemProperties.java的API为系统API,普通应用无法直接使用,可以通过反射来get和set prop。

package com.giada.reflectiondemo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "ro.vendor.build.version.incremental:"+getString("ro.vendor.build.version.incremental", "fuckyou"));
        Log.d(TAG, "sys.isolated_storage_snapshot:"+getBoolean("sys.isolated_storage_snapshot", false));
    }
    public static boolean getBoolean(String key, boolean def) throws IllegalArgumentException {
        try {
            Class SystemPropertiesClass = Class.forName("android.os.SystemProperties");
            Method getBooleanMethod =
                    SystemPropertiesClass.getDeclaredMethod(
                            "getBoolean", String.class, boolean.class);
            getBooleanMethod.setAccessible(true);
            return (boolean) getBooleanMethod.invoke(SystemPropertiesClass, key, def);
        } catch (InvocationTargetException
                | IllegalAccessException
                | NoSuchMethodException
                | ClassNotFoundException e) {
            Log.e(TAG, "Failed to invoke SystemProperties.getBoolean()", e);
        }
        return def;

3.1.2 系统源码或应用

导入android.os.SystemProperties;直接调用即可

import android.os.SystemProperties;
......
final void finishBooting() {
  ...
  //设置开机完成标志属性sys.boot_completed
  SystemProperties.set("sys.boot_completed", "1");
}
...
private static void maybePruneOldTraces(File tracesDir) {
  ...
  //获取tombstoned.max_anr_count属性值
  final int max = SystemProperties.getInt("tombstoned.max_anr_count", 64);
}

3.2 C++代码中使用系统属性

在C++代码中使用prop需要:

  • include <cutils/properties.h>
  • Android.mk或Android.bp或Makefile中需要链接libcutils库

示例:
frameworks/av/media/libstagefright/Android.bp

shared_libs: [
        ...
        "libcutils",
        ...
frameworks/av/media/libstagefright/MPEG4Writer.cpp
#include <cutils/properties.h>
    ...
void MPEG4Writer::addDeviceMeta() {
    ...
    if (property_get("ro.build.version.release", val, NULL)
    ...
        if (property_get("ro.product.model", val, NULL)
    ...
}

properties.h源码:

system/core/libcutils/include/cutils/properties.h
int property_get(const char* key, char* value, const char* default_value);
int property_set(const char *key, const char *value);
int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie);

四、特殊属性

有些属性比较特殊,总结如下:

4.1 ro只读属性

ro即read only这类属性通常是系统默认属性,在系统编译或初始化时设置的。

$ getprop ro.vendor.build.version.release
10
$ setprop ro.vendor.build.version.release 9
setprop: failed to set property 'ro.vendor.build.version.release' to '9'

4.2 persist持久属性

设置persist开头的属性,断电后仍能保存,值写入data/property/persistent_properties。

$ getprop persist.prop.test  //属性为空
$ setprop persist.prop.test abc //设置属性persist.prop.test值为abc
$ getprop persist.prop.test abc //属性get正常
abc
$reboot //重启设备
$ getprop persist.prop.test //属性为abc
abc

4.3 ctl 控制属性

setprop ctl.start xxx //启动某服务
setprop ctl.stop xxx  //关闭某服务
setprop ctl.restart xxx  //重启某服务

4.4 sys.powerctl属性

sys.powerctl属性可控制设备重启关机

setprop sys.powerctl shutdown //设备关机
setprop sys.powerctl reboot //设备重启

4.5 普通属性

设置其他格式开头的属性,断电后不能保存

$ getprop prop.test  //属性为空
$ setprop prop.test 123//设置属性persist.prop.test值为abc
$ getprop prop.test 123//属性get正常
123
$reboot //重启设备
$ getprop prop.test //属性为空

注意:属性命名需符合 1.1.3 小节中属性命名规范,否则报错,例如

setprop good.good.study. day.day.up
Property names must not start or end with a '.'

4.6 添加系统默认属性

从前面的介绍中我们知道系统开机时会 load *.prop 属性配置文件中的属性,因此开机后就有了默认属性。这里我们可以在device/xxx/xxx/system.prop 中添加

# 添加自己的系统默认属性
persist.test.prop=test

注意:这里添加的属性前缀必须是在system/sepolicy/private/property_contexts中被定义过的,否则无效;定制化前缀属性在后面定制prop属性配置中会介绍。
系统编译后在out/target/product/xxx/system/build.propout/target/product/xxx/vendor/build.prop可找到添加的属性persist.test.prop,则说明基本添加成功,烧录img验证即可。


五、打包、定制prop

项目中有许多 *.prop 配置文件,那么问题来了

  • 这些文件是如何最终打包至 out/tartget/product/…/build.prop 的呢?
  • 为了便于客制化属性管控,如何添加自己的prop配置文件呢?

5.1 prop打包流程

build.prop 是在代码编译时,build/core/Makefile里完成打包的

//指定编译信息及设备基本信息脚本
BUILDINFO_SH := build/make/tools/buildinfo.sh
BUILDINFO_COMMON_SH := build/make/tools/buildinfo_common.sh

//指定build.prop生成路径
INSTALLED_BUILD_PROP_TARGET := $(TARGET_OUT)/build.prop
INSTALLED_PRODUCT_BUILD_PROP_TARGET := $(TARGET_OUT_PRODUCT)/build.prop
INSTALLED_VENDOR_BUILD_PROP_TARGET := $(TARGET_OUT_VENDOR)/build.prop
...
//生成build.prop
$(intermediate_system_build_prop): $(BUILDINFO_SH) $(BUILDINFO_COMMON_SH) $(INTERNAL_BUILD_ID_MAKEFILE) $(BUILD_SYSTEM)/version_defaults.mk $(system_prop_file) $(INSTALLED_ANDROID_INFO_TXT_TARGET) $(API_FINGERPRINT)
   @echo Target buildinfo: $@
   @mkdir -p $(dir $@)
   $(hide) echo > $@
ifneq ($(PRODUCT_OEM_PROPERTIES),)
   $(hide) echo "#" >> $@; \
           echo "# PRODUCT_OEM_PROPERTIES" >> $@; \
           echo "#" >> $@;
   $(hide) $(foreach prop,$(PRODUCT_OEM_PROPERTIES), \
       echo "import /oem/oem.prop $(prop)" >> $@;)
endif
   $(hide) PRODUCT_BRAND="$(PRODUCT_SYSTEM_BRAND)" \
           ...
           TARGET_CPU_ABI2="$(TARGET_CPU_ABI2)" \
           bash $(BUILDINFO_SH) >> $@//将许多属性追加至out/.../build.prop

build/make/tools/buildinfo.sh中配置了系统编译常见信息,具体如下

...
echo "ro.build.version.incremental=$BUILD_NUMBER"//软件增量版本
echo "ro.build.version.sdk=$PLATFORM_SDK_VERSION"//软件使用的sdk版本
echo "ro.build.version.release=$PLATFORM_VERSION"//系统版本号
echo "ro.build.date=`$DATE`"//软件编译日期
...

经过Makefile,将系统中各种prop配置文件合并生成在out指定路径下。
也是在Makefile中将各路径下 build.prop 随系统分区一同打包进img,out\target\product\xxx\system\build.prop打包进system.img
out\target\product\xxx\vendor\build.prop打包进vendor.img


5.2 添加定制属性 test.prop

涉及的代码路径汇总如下:

device/qcom/qssi/test.prop
device/qcom/qssi/qssi.mk
device/qcom/sepolicy/generic/private/property_contexts
system/core/rootdir/init.rc
system/core/init/property_service.cpp

为了方便统一管理定制化属性,有时会将定制化属性都写在定制的.prop文件中,下面以添加test.prop为例说明添加过程。

5.2.1 device下添加test.prop

device/qcom/qssi/test.prop
#
# system.prop for qssi
#
ro.test.year=2022    //添加ro属性
persist.test.month=07    //添加persist属性
test.day=25    //添加普通属性

ro.product.model=test //定制系统已有ro.product.model属性

5.2.2 配置预置路径

修改device下的device.mk来指定 test.prop 的预置路径

device/qcom/qssi/qssi.mk
#将test.prop预置到system/test.prop
PRODUCT_COPY_FILES += \
    device/qcom/qssi/test.prop:system/test.prop

5.2.3 SELinux权限配置

test.开头的属性是新添加的配置,需要在配置对应的SELinux规则,否则无效,配置方法如下:

device/qcom/sepolicy/generic/private/property_contexts
test.                             u:object_r:system_prop:s0

5.2.4 配置test.prop权限

此步骤可省略,若未配置读写权限,默认system/prop为644
这里配置与system/build.prop相同的600权限

system/core/rootdir/init.rc
on fs
   chmod 0600 /system/test.prop

5.2.5 load test.prop

上面仅仅将 test.prop 预置到 system/test.prop 还不够,系统启动时需要 load test.prop 才能使其生效

system/core/init/property_service.cpp
    load_properties_from_file("/system/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("/product_services/build.prop", nullptr, &properties);
   load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
   //load 预置的test.prop ,最后load保证其配置属性优先级更高
   load_properties_from_file("/system/test.prop", nullptr, &properties);

5.2.6 验证test.prop

将打包好的img烧录到设备中进行确认

//test.prop预置成功
$ ls -al system/
-rw-r--r--  1 root root    117 2009-01-01 08:00 test.prop

//新增属性设置成功
$ getprop | grep test
[test.day]: [25]
[persist.test.month]: [07]
[ro.test.year]: [2022]

//ro.product.model覆盖了默认值
$ getprop ro.product.model
test

六、参考链接

安卓property service系统分析
Android 系统属性(SystemProperties)介绍
安卓系统属性persist类型prop深入剖析

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐