当前位置: 首页 > news >正文

Android 属性系统

1. 总览

在这里插入图片描述

2. 属性基础

2.1 属性格式

安卓系统的属性名称格式:xx.xx.xx.xx

2.2 属性特点

  • 全局性
  • 跨语言(java、c/c++)
  • 进程运行需要的条件

2.3 属性简单区分

  • 如何查看系统中有哪些属性:
getprop
  • 如何设置属性:
setprop vendor.my.work.test
  • 简单区分:
    • init.svc.xxx:由 init 启动的一些服务进程的属性
    • ro.xxx.xx:均是只读属性,不能 set
      • ro.kernel.xxx:这类属性来自 uboot 以及内核参数传递至安卓系统中
      • ro.build.xxx:与编译相关
    • persist.xx.xx.xx:表示永久化,启动后不丢失,但是可以写。
      • 为什么需要 persist 这种属性:属性是共享内存,会掉电丢失,除非是编译自动生成的 xxx.prop 文件中的属性(默认属性)。而 persist 开头的属性会保存在 /data/property/persistent_properties 文件中,因为永久化的存储就是文件存储。
    • sys.xx.xx 开头的
  • ctl.start、ctl.stop、ctl.restart:用于控制启动与关闭服务,整个服务可以理解为后台的一些守护进程,但是控制时需要有 selinux 权限,检查权限时会去查看是否有以下权限:
-r--r--r--  1 root root 131072 2024-12-16 04:19 u:object_r:ctl_restart_prop:s0
-r--r--r--  1 root root 131072 2024-12-16 04:19 u:object_r:ctl_start_prop:s0
-r--r--r--  1 root root 131072 2024-12-16 04:19 u:object_r:ctl_stop_prop:s0
- ctl.xxx 使用场景:

在这里插入图片描述

3. 属性技术原理

在这里插入图片描述

3.1 特点

3.1.1 客户端进程

  • shell
  • c/c++ 程序进程
  • app 进程
  • 系统服务进程等
  • 客户端的特点:所有进程理论上都可以访问属性共享内存(/dev/properties),因此 读是直接内存映射,非常快;写操作必须走属性服务(init 进程),因为要经过权限和安全上下文检查。

3.1.2 服务器(属性服务)

  • 属性服务进程:位于 init 进程,监听 socket(通常是 property_service socket)。
  • 主要功能:
    • 统一处理来自客户端的 setprop 请求;
    • 校验调用者的 UID、GID、SELinux 权限;
    • 更新共享内存中对应的属性值;
    • 如果是 persist.xx.xx 属性,还需要落盘保存到 /data/property/(或者对应的持久化分区),以保证重启后仍然生效。

3.1.3 属性文件与共享内存

  • 属性共享内存:/dev/__properties__
    • init 进程在启动时创建,供所有进程 mmap;
    • 每个属性存储为 key-value,使用树形结构进行快速查找;
    • 只读访问直接 mmap 查找即可,不需走 IPC。
  • 属性文件:
    • 作用:为属性共享内存提供 初始数据和持久化数据;
    • 文件存储路径:
      • /dev/__properties__/u:object_r:xxx_prop:s0 (按 SELinux 安全上下文拆分);
      • 每个文件大小固定为 1MB;
      • 存储结构是树形(prefix tree / trie)索引。

3.1.4 流程示意

  • 读属性(fast path):
    客户端 → 直接 mmap /dev/__properties__ → 查找 key → 返回 value。
  • 写属性(secure path):
    客户端 → 通过 property_service socket → 请求 init → 校验权限 → 更新共享内存 & 持久化(如 persist.xx.xx)。

在这里插入图片描述

3.1.5 示例

假设我们执行:

setprop persist.sys.locale en-US
  1. persist.sys.locale 属于 system_prop 域;
  2. setprop 命令调用 libc → 通过 property_service socket 向 init 发送请求;
key = "persist.sys.locale"
value = "en-US"
  1. init 检查调用者的 UID/GID/SELinux 权限是否能写入 system_prop;判断 key 属于哪个属性文件(此处是 system_prop);
  2. 若通过,则更新 /dev/__properties__/u:object_r:system_prop:s0 中的共享内存节点;
  3. 同时因为是 persist.xx.xx,还会落盘到 /data/property/persist.sys.locale。

3.1.6 例如 /dev/__properties__/u:object_r:system_prop:s0

  • 每个属性文件(例如 /dev/__properties__/u:object_r:system_prop:s0)本质是一个 mmap 共享内存区域。
  • 这块内存中存储的是一个属性表,它并不是简单的 KV 顺序表,而是:
    • 属性目录树 (property_info_area) → 用来快速定位 key;
    • 属性条目 (prop_info) → 实际存放 key 和 value。

3.2 属性文件哪里来

3.2.1 实例:初始化 ro.build.version.release=13 属性

我们以 ro.build.version.release=13 这个最常见的系统属性为例,看看它是如何:

  1. 从哪里来;
  2. 被 init 加载;
  3. 存到哪儿;
  4. 最终怎么被 getprop 读到。

3.2.2 第一步:属性来源

在源码中,这个属性最早定义在:build/make/core/sysprop.mk,然后编译到系统中,会写进 /system/build.prop

# /system/build.prop 中内容
ro.build.version.release=13
ro.build.version.sdk=33
ro.build.id=TQ3A.230805.001
...

其它属性定义还有:
/vendor/build.prop
/product/build.prop
/odm/build.prop
/default.prop

3.2.3 第二步:init 启动时加载 build.prop

在 init 启动时,会调用:

load_properties_from_file("/system/build.prop", /*required=*/true);
  • 这段代码会:
    • 打开 /system/build.prop
    • 一行一行读取 key=value
    • 把它们存到共享内存属性系统

3.2.4 第三步:写入共享内存节点 /dev/__properties__/u:object_r:system_prop:s0

根据属性的 SELinux 类型(由 property_contexts 文件决定),ro.build.version.release 被标记为:

ro.build.version.release   u:object_r:system_prop:s0

所以它会被写入共享内存文件:/dev/__properties__/u:object_r:system_prop:s0

{"ro.build.version.release": "13","ro.build.version.sdk": "33",...
}

3.2.5 第四步:客户端访问

当你在 shell 中运行:

getprop ro.build.version.release

或者 Java 中调用:

SystemProperties.get("ro.build.version.release");

流程是这样的:

  1. 客户端进程 mmap /dev/__properties__/u:object_r:system_prop:s0
  2. 查找 key “ro.build.version.release”
  3. 直接读取值 “13”
  4. 返回

4. 属性如何自定义

在这里插入图片描述

  1. 一般在 device/ 下面进行属性的定制(核心定制方式之一),比如:
    要生成 system/build.prop --> device/ 下面某个产品配置的 system.prop
    要生成 vendor/build.prop --> device/ 下面某个产品配置的 vendor.prop
    要生成 product/build.prop --> device/ 下面某个产品配置的 product.prop
  2. build/ 下面的一些编译脚本里的变量添加自定义属性,这些变量有:
    ADDITIONAL_SYSTEM_PROPERTIES
    ADDITIONAL_VENDOR_PROPERTIES
    ADDITIONAL_ODM_PROPERTIES
    ADDITIONAL_PRODUCT_PROPERTIES
    ADDITIONAL_BUILD_PROPERTIES
    举个例子:
    ADDITIONAL_SYSTEM_PROPERTIES += ro.actionable_compatible_property.enabled=true
  3. 我们在 build/core/main.mk 文件中发现:
ifneq ($(BOARD_PROPERTY_OVERRIDES_SPLIT_ENABLED), true)    ADDITIONAL_SYSTEM_PROPERTIES += $(PRODUCT_PROPERTY_OVERRIDES) 
else ifndef BOARD_VENDORIMAGE_FILE_SYSTEM_TYPE ADDITIONAL_SYSTEM_PROPERTIES += $(PRODUCT_PROPERTY_OVERRIDES)...  

PRODUCT_PROPERTY_OVERRIDES 会被添加到如 ADDITIONAL_SYSTEM_PROPERTIES 等变量中,因此我们可以在 aosp/device/ 下面的产品配置中添加自定义的属性信息在 PRODUCT_PROPERTY_OVERRIDES 变量中,这样就可以写入 ADDITIONAL_SYSTEM_PROPERTIES 等变量中然后被编译到属性文件。(这种方法通用性更好,不需要更改系统的核心编译源码)

如下面在 aosp/device 目录下搜索:有许多 PRODUCT_PROPERTY_OVERRIDES 来定义的自定义属性:

<you aosp dir>/device$ grep "PRODUCT_PROPERTY_OVERRIDES" ./* -rn
./amlogic/yukawa/device-yukawa.mk:22:PRODUCT_PROPERTY_OVERRIDES += ro.product.device=$(TARGET_DEV_BOARD)
./amlogic/yukawa/device-common.mk:175:PRODUCT_PROPERTY_OVERRIDES += ro.sf.lcd_density=320
./amlogic/yukawa/device-common.mk:190:PRODUCT_PROPERTY_OVERRIDES += \
./amlogic/yukawa/device-common.mk:212:PRODUCT_PROPERTY_OVERRIDES += wifi.interface=wlan0 \
./amlogic/yukawa/device-common.mk:258:PRODUCT_PROPERTY_OVERRIDES += ro.hdmi.device_type=4 \
./amlogic/yukawa/device-yukawa_sei510.mk:7:PRODUCT_PROPERTY_OVERRIDES += ro.product.device=sei510
./generic/goldfish/board/emu64a/details.mk:19:PRODUCT_PROPERTY_OVERRIDES += \
./generic/goldfish/board/emu64a16k/details.mk:19:PRODUCT_PROPERTY_OVERRIDES += \
./generic/goldfish/board/emu64x16k/details.mk:19:PRODUCT_PROPERTY_OVERRIDES += \
./generic/goldfish/board/emu64x/details.mk:19:PRODUCT_PROPERTY_OVERRIDES += \
./generic/car/emulator/audio/car_emulator_audio.mk:28:PRODUCT_PROPERTY_OVERRIDES += ro.hardware.audio.primary=caremu
  1. 除此以外,还有如下:
# from variable PRODUCT_SYSTEM_PROPERTIES
# from variable PRODUCT_SYSTEM_DEFAULT_PROPERTIES

5. 应用属性接口分析

5.1 C 语言属性 API 源码及头文件目录

  • 源码头文件路径 :aosp/system/core/libcutils/include/cutils/properties.h
  • 实现源码路径 :aosp/system/core/libcutils/properties.cpp
int property_get(const char* key, char* value, const char* default_value); 
int8_t property_get_bool(const char *key, int8_t default_value);
int64_t property_get_int64(const char *key, int64_t default_value);
int32_t property_get_int32(const char *key, int32_t default_value); 
int property_set(const char *key, const char *value); 

5.2 C++ 语言属性 API 源码及头文件目录

  • C++ 头文件路径:aosp/system/libbase/include/android-base/properties.h
  • C++ 源码路径 :aosp/system/libbase/properties.cpp
namespace android {
namespace base {
std::string GetProperty(const std::string& key, const std::string& default_value);
bool GetBoolProperty(const std::string& key, bool default_value);
template <typename T> T GetIntProperty(const std::string& key,T default_value,T min = std::numeric_limits<T>::min(),T max = std::numeric_limits<T>::max());
template <typename T> T GetUintProperty(const std::string& key,T default_value,T max = std::numeric_limits<T>::max());
bool SetProperty(const std::string& key, const std::string& value);

5.3 Java 系统级别的属性接口

  • 实现源码路径:framework/base/core/java/android/os/SystemProperties.java
    /*** Get the String value for the given {@code key}.** @param key the key to lookup* @param def the default value in case the property is not set or empty* @return if the {@code key} isn't found, return {@code def} if it isn't null, or an empty* string otherwise* @hide*/@NonNull@SystemApipublic static String get(@NonNull String key, @Nullable String def) {if (TRACK_KEY_ACCESS) onKeyAccess(key);return native_get(key, def);}// 请注意,@SystemApi  @hide的含义。public static int getInt(@NonNull String key, int def) {if (TRACK_KEY_ACCESS) onKeyAccess(key);return native_get_int(key, def);}public static long getLong(@NonNull String key, long def) {if (TRACK_KEY_ACCESS) onKeyAccess(key);return native_get_long(key, def);}public static boolean getBoolean(@NonNull String key, boolean def) {if (TRACK_KEY_ACCESS) onKeyAccess(key);return native_get_boolean(key, def);}
  • 应用例子:
import android.os.SystemProperties;
SystemProperties.set("my.work.time", 8);

6. 属性实战案例

6.1 C++ 属性编写实战:写一个 C++ 实战接口代码

#include <cutils/properties.h>  // 引入 Android 属性 API
#include <string>#define LOG_TAG "PropertyDemo"
#include <log/log.h>// 获取属性
void getSystemProperty(const std::string& property) {char value[PROP_VALUE_MAX];if (property_get(property.c_str(), value, "") > 0) {ALOGD("Property Value for %s: %s", property.c_str(), value);} else {ALOGE("Failed to get property: %s", property.c_str());}
}// 设置属性
void setSystemProperty(const std::string& property, const std::string& value) {if (property_set(property.c_str(), value.c_str()) == 0) {ALOGD("Successfully set %s to %s", property.c_str(), value.c_str());} else {ALOGE("Failed to set property: %s", property.c_str());}
}int main() {// 获取系统属性ALOGD("Getting system property 'ro.build.version.release':");getSystemProperty("ro.build.version.release");// 设置系统属性// ALOGD("Setting system property 'persist.sys.debug' to '1'");// setSystemProperty("persist.sys.debug", "1");setSystemProperty("vendor.my.work.time", "8");// 停止某个服务ALOGD("Stopping  service...");setSystemProperty("ctl.stop", "vendor.gatekeeper-1-0");// 启动某个服务ALOGD("Starting service...");setSystemProperty("ctl.start", "vendor.gatekeeper-1-0");while(1){usleep(1000*1000);ALOGD("sleep for one second.");}return 0;
}

6.2 Android.bp 编译脚本编写

cc_binary {name: "propertydemo",  // 模块名称srcs: ["main.cpp",               // 源代码文件],shared_libs: ["liblog",                 // 依赖 Android 日志库"libcutils",      //依赖属性接口],cflags: ["-Wall",                  // 编译时启用所有警告"-std=c++11",             // 使用 C++11 标准],vendor: true,
}

6.3 sepolicy 内容编写

file_contexts property_contexts propertydemo.te property.te
file_contexts:应用程序安全上下文标签描述
property_contexts:自定义属性上下文标签描述
propertydemo.te:应用程序selinux策略
property.te:自定义属性类型

6.3.1 file_contexts 编写

/vendor/bin/propertydemo  u:object_r:propertydemo_dt_exec:s0

6.3.2 property_contexts 编写

# first version# second version
# vendor.my.work.time  u:object_r:vendor_my_work_prop:s0

6.3.3 propertydemo.te 编写

type propertydemo_dt , domain;
type propertydemo_dt_exec, exec_type, vendor_file_type,file_type;init_daemon_domain(propertydemo_dt)domain_auto_trans(shell, propertydemo_dt_exec, propertydemo_dt)# 这个后面再打开
# set_prop(propertydemo_dt , vendor_my_work_prop);
# set_prop(propertydemo_dt , ctl_default_prop);
# set_prop(propertydemo_dt , ctl_start_prop);
# set_prop(propertydemo_dt , ctl_stop_prop);

6.3.4 property.te 编写

# first version# second version
# type vendor_my_work_prop, property_type;# third version
# type vendor_my_work_prop, property_type, vendor_property_type;

6.3.5 添加编译

添加到编译脚本中 build/make/target/board/emulator_x86_64/BoardConfig.mk,需要在这个文件中配置 sepolicy 所在的路径,这样系统可以将其加入编译到 selinux_policy。

BOARD_SEPOLICY_DIRS += device/hello/propertydemo/sepolicy  

6.3.6 结果

make selinux_policy
mmm device/hello/propertydemo/adb remount
adb push out/target/product/emulator_x86_64/vendor/bin/propertydemo   /vendor/bin/
adb push out/target/product/emulator_x86_64/vendor/etc/selinux   /vendor/etc/emulator -verbose -writable-system
6.3.6.1 first version
  • property_contexts
# first version# second version
# vendor.my.work.time  u:object_r:vendor_my_work_prop:s0
  • property.te
# first version# second version
# type vendor_my_work_prop, property_type;# third version
# type vendor_my_work_prop, property_type, vendor_property_type;
  • 编译通过。实际运行结果如下。

在这里插入图片描述
在这里插入图片描述

总的来说,就是设置自定义属性:vendor.my.work.time失败。执行"ctl.stop/start"属性失败,因为我们在策略 te 中并未添加相关的属性访问策略,先来查看下这些属性对应的安全上下文标签是哪些:
在这里插入图片描述

6.3.6.2 second version 编译报错
  • property_contexts
# first version# second version
vendor.my.work.time  u:object_r:vendor_my_work_prop:s0
  • property.te
# first version# second version
type vendor_my_work_prop, property_type;# third version
# type vendor_my_work_prop, property_type, vendor_property_type;
make selinux_policy

在这里插入图片描述

结合错误日志:libsepol.report_failure: neverallow on line 64 of system/sepolicy/private/property.te,找到文件 system/sepolicy/private/property.te 的第 64 行:

treble_sysprop_neverallow(`enforce_sysprop_owner(`neverallow domain {property_type-system_property_type-product_property_type-vendor_property_type}:file no_rw_file_perms;
')
.....

翻译一下就是说,不允许 domain 主域访问 property_type 类型的资源,除了 -system_property_type-product_property_pype-vendor_property_type。解决方法就是用这里豁免的vendor_property_type,修改 property.te 文件如下(即前面的 second version)可以编译通过:

# first version
# type vendor_my_work_prop, property_type;# second version
type vendor_my_work_prop, property_type, vendor_property_type;

在这里插入图片描述

6.3.6.3 third version 结果运行

根据前面 6.3.6.1 和 6.3.6.2 的分析,在 propertydemo.te 中添加以下访问属性的策略:使用属性宏 set_prop() 来添加访问权限。

type propertydemo_dt, domain;
type propertydemo_dt_exec, exec_type, vendor_file_type, file_type;init_daemon_domain(propertydemo_dt)
domain_auto_trans(shell, propertydemo_dt_exec, propertydemo_dt)# 这个后面再打开
set_prop(propertydemo_dt, vendor_my_work_prop);
set_prop(propertydemo_dt, ctl_default_prop);
set_prop(propertydemo_dt, ctl_start_prop);
set_prop(propertydemo_dt, ctl_stop_prop);
  • property.te
# first version# second version
# type vendor_my_work_prop, property_type;# third version
type vendor_my_work_prop, property_type, vendor_property_type;
  • property_contexts
# first version# second version
vendor.my.work.time  u:object_r:vendor_my_work_prop:s0

重新编译后,push 到系统中,重新验证:得到正确结论。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

6.4 扩展

  • 思维扩展:如果一个vendor分区的进程想访问并设置 system_prop 的属性,如 persist.sys. 开头的属性,怎么办?
  • 定义 init 服务: 在 init.rc 文件中,创建一个负责设置属性的服务:
on property:vendor.sys.request_debug=1     setprop persist.sys.debug 1
http://www.dtcms.com/a/352143.html

相关文章:

  • 蓝思科技中报:深耕业务增量,AI硬件打开想象空间
  • Pandas vs Polars Excel 数据加载对比报告
  • Coze Studio系统架构深度剖析:从分层设计到领域驱动的技术实践- 第二篇
  • vue实现拖拉拽效果,类似于禅道首页可拖拽排布展示内容(插件-Grid Layout)
  • 用 Allure 生成 pytest 测试报告:从安装到使用全流程
  • STM32 定时器(互补输出+刹车)
  • yggjs_rbutton React按钮组件v1.0.0 多主题系统使用指南
  • 什么叫API对接HR系统?
  • 2025年8月技术问答第3期
  • 03MySQL——DCL权限控制,四种常用函数解析
  • SSM入门到实战: 3.6 SpringMVC RESTful API开发
  • 基于muduo库的图床云共享存储项目(一)
  • vs2019安装cpu版本的fftw 以实现傅里叶变换
  • 《护理学》10月版面征稿论文速递
  • 【46页PPT】AI智能中台用ABC+IOT重新定义制造(附下载方式)
  • SQLBot:一款基于大语言模型和RAG的智能数据分析工具
  • AI人工智能一体化HR系统如何选型?
  • 重塑金融管理会计核心引擎,容智管会智能体打造智能决策新基建
  • 手写MyBatis第35弹:@Select、@Insert等注解的背后原理
  • 【软考论文】论DevOps及其应用
  • BotCash:2025 中国算力大会——国家级数据标注产业供需对接大会
  • 【自记】Python 局部变量、全局变量及global的示例
  • Python实现RANSAC进行点云直线、平面、曲面、圆、球体和圆柱拟合
  • 负载均衡之平滑加权轮询(Smooth Weighted Round Robin)详解与实现
  • 前沿技术趋势与应用:探索数字世界的下一个十年
  • 本地通过阿里云ECS建立SSH隧道连接阿里云RDS MYSQL数据库
  • 【P2P】RELAY服务2:cmake+ c实现及ubuntu运行
  • 淘宝/天猫商品详情API数据解析【附代码】
  • 软件检测报告:XML外部实体(XXE)注入漏洞原因和影响
  • 【Erdas实验教程】031:遥感图像频率域增强(傅立叶变换)