Android 系统属性完全指南 目录
版本说明 文档版本 : 1.1适用 Android 版本 : Android 8.0+ (API 26+)最后更新 : 2025-01-15
重要提示 :
本文档主要基于 Android 8.0+ 的实现,早期版本可能存在差异
不同设备制造商可能对属性系统进行定制
部分功能需要 root 权限或系统签名
实际使用时请根据具体设备和版本进行验证
勘误与更新 :
1. 系统属性概述 1.1 什么是系统属性 Android 系统属性(System Properties)是一个全局的键值对数据库 ,用于存储和共享系统级别的配置信息。它类似于 Windows 的注册表或 Linux 的 sysctl,但更加轻量级。
核心特性 :
键值对结构 : key=value 格式
全局共享 : 所有进程都可以访问(受权限控制)
快速访问 : 驻留在内存中,访问速度极快
字符串类型 : 所有值都以字符串形式存储
大小限制 :
属性名最大长度:31 字符(PROP_NAME_MAX = 32,保留1字节给 \0)
属性值最大长度:91 字符(PROP_VALUE_MAX = 92,保留1字节给 \0)
1.2 系统属性的作用
系统配置管理
存储设备信息(型号、版本、序列号等)
保存系统行为配置(语言、时区、调试开关等)
进程间通信
调试与诊断
动态控制日志级别
开启/关闭调试功能
性能分析开关
功能开关
Feature Flag 管理
A/B Testing 配置
灰度发布控制
1.3 系统属性架构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 ┌─────────────────────────────────────────────────────┐ │ Android 应用层 │ │ SystemProperties.get/set() (Java API) │ └─────────────────┬───────────────────────────────────┘ │ ┌─────────────────▼───────────────────────────────────┐ │ Framework 层 │ │ android.os.SystemProperties (隐藏API) │ └─────────────────┬───────────────────────────────────┘ │ ┌─────────────────▼───────────────────────────────────┐ │ Native 层 │ │ property_get/property_set (C/C++ API) │ │ libcutils / libc │ └─────────────────┬───────────────────────────────────┘ │ ┌─────────────────▼───────────────────────────────────┐ │ Property Service │ │ /dev/__properties__ (共享内存) │ │ property_contexts (SELinux 上下文) │ └─────────────────────────────────────────────────────┘
2. 系统属性分类 2.1 按前缀分类 Android 系统属性使用命名空间 来组织,前缀决定了属性的特性和用途。
2.1.1 ro.* (Read-Only 只读属性) 特点 :
✅ 系统启动时设置,之后不可修改
✅ 通常从 build.prop 或内核命令行加载
✅ 存储设备的固有信息
常见属性 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ro.product.model =Pixel 6 # 设备型号 ro.product.brand =Google # 品牌 ro.product.manufacturer =Google # 制造商 ro.product.device =oriole # 设备代号 ro.build.version.release =13 # Android 版本号 ro.build.version.sdk =33 # SDK API Level ro.build.id =TP1A.220624.014 # 构建 ID ro.build.type =user # 构建类型 (user/userdebug/eng) ro.build.date =Sun Jun 26 00:00:00 UTC 2022 ro.hardware =oriole # 硬件平台 ro.board.platform =gs101 # SoC 平台 ro.product.cpu.abi =arm64-v8a # CPU 架构
存储位置 :
/system/build.prop - 系统构建属性
/vendor/build.prop - 厂商属性
/product/build.prop - 产品属性
/odm/build.prop - ODM 属性
2.1.2 persist.* (持久化属性) 特点 :
✅ 可读可写(需要权限)
✅ 重启后仍然保留
✅ 存储在 /data 分区
常见属性 :
1 2 3 4 5 6 7 8 9 10 11 persist.sys.usb.config =adb # USB 模式 persist.log.tag =V # 全局日志级别 persist.sys.dalvik.vm.lib.2 =libart.so persist.sys.strictmode.visual =false persist.sys.cached_app_freezer =true # 应用冻结开关 persist.sys.timezone =Asia/Shanghai # 时区
存储位置 :
/data/property/persistent_properties - 所有 persist.* 属性的二进制存储文件
Android 8.0+ 统一使用此文件,替代了早期版本的每个属性独立文件方式
2.1.3 sys.* (系统运行时属性) 特点 :
✅ 可读可写(需要权限)
❌ 重启后丢失(非持久化)
✅ 用于运行时状态和临时配置
常见属性 :
1 2 3 4 5 6 7 8 sys.boot_completed =1 # 启动完成标志 sys.powerctl =reboot # 电源控制 sys.usb.state =adb # 当前 USB 状态 sys.logbootcomplete =1 sys.retaildemo.enabled =0
2.1.4 debug.* (调试属性) 特点 :
✅ 用于开发和调试
✅ 通常在 user 版本中被忽略
✅ 可动态修改
常见属性 :
1 2 3 4 5 6 7 8 debug.atrace.tags.enableflags =0 debug.sf.enable_hwc_vds =1 # SurfaceFlinger 调试 debug.choreographer.skipwarning =1 debug.performance.tuning =1 debug.hwui.profile =true
2.1.5 service.* (服务状态属性) 特点 :
✅ 用于服务状态管理
✅ 通常由 init 系统管理
常见属性 :
1 2 service.bootanim.exit =1 # 开机动画退出 service.adb.tcp.port =5555 # ADB TCP 端口
2.1.6 init.* (Init 进程属性) 特点 :
常见属性 :
1 2 3 init.svc.adbd =running # adbd 服务状态 init.svc.zygote =running # zygote 进程状态 init.svc.surfaceflinger =running # SurfaceFlinger 状态
2.2 按生命周期分类
类型
持久化
可写
重启后保留
典型前缀
构建时属性
文件
❌
✅
ro.*
持久化属性
文件
✅
✅
persist.*
运行时属性
内存
✅
❌
sys., debug.
服务属性
内存
✅
❌
init.svc.*
3. 存储机制与持久化 3.1 存储架构 1 2 3 4 5 6 7 8 9 10 11 12 ┌──────────────────────────────────────────────────────┐ │ 系统属性存储层次 │ └──────────────────────────────────────────────────────┘ │ ┌────────────────┼────────────────┐ │ │ │ ▼ ▼ ▼ ┌───────────────┐ ┌──────────────────────┐ ┌─────────────┐ │ 共享内存区域 │ │ 持久化文件 │ │ 构建文件 │ │ /dev/__prop* │ │ /data/property/ │ │ build.prop │ │ (运行时) │ │ persistent_properties│ │ (ro.*) │ └───────────────┘ └──────────────────────┘ └─────────────┘
3.2 共享内存机制 Property Service 使用共享内存实现高效的跨进程访问 :
实现细节 :
1 2 3 4 5 6 7 8 9 10 11 12 #define PROP_SERVICE_NAME "property_service" #define PROP_FILENAME_MAX 1024 struct prop_area { uint32_t magic; uint32_t version; uint32_t serial; uint32_t size; };
访问流程 :
应用调用 SystemProperties.get()
通过 Binder 调用到 Property Service
Property Service 从共享内存读取
返回属性值
3.3 持久化文件存储 3.3.1 构建属性文件 build.prop 文件格式 :
1 2 3 4 5 6 7 8 ro.product.model =Example Device ro.build.version.sdk =33 ro.build.version.release =13 ro.build.date =${BUILD_DATE}
加载顺序 (优先级从低到高):
/system/build.prop
/vendor/build.prop
/odm/build.prop
/product/build.prop
后加载的同名属性会覆盖先加载的
3.3.2 持久化属性文件 persist. 属性的存储 *:
在 Android 8.0+ 版本中,所有 persist.* 属性统一存储在一个二进制文件中:
1 2 3 4 5 6 /data/property/persistent_properties $ ls -l /data/property/ -rw------- 1 root root 15406 2025-10-15 14:43 persistent_properties
特点 :
✅ 所有 persist.* 属性存储在一个文件中
✅ 使用 Protocol Buffer 或自定义二进制格式
✅ 文件权限:root:root 0600(仅 root 可访问)
✅ 写入时原子操作,保证数据一致性
历史变化 :
Android 7.x 及以前:每个属性一个文件(/data/property/persist.*)
Android 8.0+:统一存储在 persistent_properties 文件中
注意 :
⚠️ 不要直接修改此文件,应使用 setprop 命令
⚠️ 文件格式是二进制的,无法直接用文本编辑器查看
3.4 内核命令行属性 从内核命令行加载属性 :
1 2 3 4 5 6 7 8 9 androidboot.hardware=oriole androidboot.serialno=1234567890ABCDEF ro.boot.hardware=oriole ro.boot.serialno=1234567890ABCDEF ro.hardware=oriole ro.serialno=1234567890ABCDEF
转换规则 :
androidboot.* → ro.boot.*
某些特殊属性会同时创建简化版本(如 ro.hardware)
4. 系统属性的生命周期 4.1 启动阶段的属性加载 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 启动流程 属性加载 ──────────────────────────────────────── Bootloader │ ▼ Kernel │ ────────────► 加载内核命令行属性 ▼ androidboot.* → ro.boot.* Init (第一阶段) │ ────────────► 设置基础属性 ▼ ro.boot.*, ro.hardware Init (第二阶段) │ ────────────► 加载 build.prop ▼ 解析 /system/build.prop │ 解析 /vendor/build.prop │ 解析 /product/build.prop │ ────────────► 加载 persist.* 属性 ▼ 从 /data/property/ 读取 Property Service 启动 │ ────────────► 启动属性服务 ▼ 监听 socket 连接 │ 处理 set/get 请求 Zygote │ ▼ System Server │ ────────────► 设置运行时属性 ▼ sys.boot_completed=0 Boot Complete │ ────────────► 标记启动完成 ▼ sys.boot_completed=1
4.2 运行时属性变更流程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 应用进程 Property Service ──────────────────────────────────────────────── SystemProperties.set() │ ├──► 检查权限 │ ├──► Binder Call ───────► 接收请求 │ │ │ ├──► SELinux 权限检查 │ │ (property_contexts) │ │ │ ├──► 更新共享内存 │ │ │ ├──► persist.* ? │ │ └─► 写入文件 │ │ /data/property/* │ │ │ └──► 触发 init 脚本 │ (on property:xxx=*) │ ◄────── 返回 ───────────────
4.3 属性监听机制 Java 层监听 :
1 2 3 4 5 6 7 8 SystemProperties.addChangeCallback(new Runnable () { @Override public void run () { String value = SystemProperties.get("persist.sys.example" ); Log.d(TAG, "Property changed: " + value); } });
Native 层监听 :
1 2 3 4 5 6 7 8 #include <sys/system_properties.h> void property_callback (void *cookie, const char *name, const char *value, uint32_t serial) { ALOGD ("Property %s changed to %s (serial=%u)" , name, value, serial); } __system_property_set_callback(property_callback, nullptr );
5. 读取与设置方法 5.1 Java API 5.1.1 读取属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import android.os.SystemProperties;String value = SystemProperties.get("ro.product.model" );String value = SystemProperties.get("debug.my.prop" , "default_value" );int sdkVersion = SystemProperties.getInt("ro.build.version.sdk" , 0 );long value = SystemProperties.getLong("my.long.prop" , 0L );boolean isEnabled = SystemProperties.getBoolean("persist.sys.example" , false );
注意 :
⚠️ SystemProperties 是隐藏 API(@hide),需要通过反射调用
✅ 系统应用或具有系统签名可以直接调用
反射调用示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class SystemPropertiesProxy { private static Class<?> sSystemProperties; static { try { sSystemProperties = Class.forName("android.os.SystemProperties" ); } catch (ClassNotFoundException e) { e.printStackTrace(); } } public static String get (String key, String def) { try { Method get = sSystemProperties.getMethod("get" , String.class, String.class); return (String) get.invoke(null , key, def); } catch (Exception e) { return def; } } public static void set (String key, String val) { try { Method set = sSystemProperties.getMethod("set" , String.class, String.class); set.invoke(null , key, val); } catch (Exception e) { e.printStackTrace(); } } } String model = SystemPropertiesProxy.get("ro.product.model" , "Unknown" );
5.1.2 设置属性 1 2 3 4 5 6 7 8 SystemProperties.set("persist.sys.example" , "value" ); SystemProperties.set("persist.sys.count" , String.valueOf(123 )); SystemProperties.set("persist.sys.example" , "" );
权限要求 :
需要 android.permission.WRITE_SECURE_SETTINGS 权限
或者系统签名(platformSignature)
或者 root 权限
5.2 Native C/C++ API 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <cutils/properties.h> char value[PROPERTY_VALUE_MAX];property_get ("ro.product.model" , value, "Unknown" );int sdk = property_get_int32 ("ro.build.version.sdk" , 0 );bool isEnabled = property_get_bool ("persist.sys.example" , false );property_set ("persist.sys.example" , "value" );
5.3 Shell 命令 5.3.1 getprop - 读取属性 1 2 3 4 5 6 7 8 9 10 11 12 13 adb shell getprop ro.product.model adb shell getprop adb shell getprop | grep "ro.build"
5.3.2 setprop - 设置属性 1 2 3 4 5 6 7 8 9 10 11 adb shell setprop persist.sys.example "test_value" adb shell setprop debug.level 3 adb shell setprop persist.sys.example "" adb shell getprop persist.sys.example
限制 :
普通应用无法通过 setprop 设置属性
需要 adb root 或 su root
受 SELinux 策略限制
5.4 批量操作 导出所有属性 :
1 2 3 4 5 6 adb shell getprop > properties.txt adb shell getprop | grep "^\\[ro\\." > ro_properties.txt adb shell getprop | grep "^\\[persist\\." > persist_properties.txt
比较属性差异 :
1 2 3 4 5 6 7 adb shell getprop > props_before.txt adb shell getprop > props_after.txt diff props_before.txt props_after.txt
6. 系统更新的影响 6.1 OTA 更新对属性的影响
属性类型
OTA 更新后
恢复出厂设置后
数据擦除后
ro. *
✅ 更新
✅ 保留
✅ 保留
persist. *
✅ 保留
❌ 清除
❌ 清除
sys. , debug.
N/A(内存)
N/A(内存)
N/A(内存)
build.prop
✅ 更新
✅ 更新
✅ 更新
6.2 分区影响 1 2 3 4 5 6 7 分区结构 属性来源 OTA 影响 ──────────────────────────────────────────────────── /system build.prop ✅ 会更新 /vendor build.prop ✅ 会更新 /product build.prop ✅ 可能更新 /odm build.prop ✅ 可能更新 /data/property/ persist.* 文件 ❌ 不影响
6.3 属性备份与恢复 6.3.1 手动备份脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #!/system/bin/sh BACKUP_DIR=/sdcard/property_backup TIMESTAMP=$(date +%Y%m%d_%H%M%S) BACKUP_FILE=$BACKUP_DIR /persist_props_$TIMESTAMP .txt mkdir -p $BACKUP_DIR echo "# Property Backup - $TIMESTAMP " > $BACKUP_FILE getprop | grep "^\[persist\." >> $BACKUP_FILE echo "Properties backed up to $BACKUP_FILE "
6.3.2 恢复脚本 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #!/system/bin/sh BACKUP_FILE=$1 if [ -z "$BACKUP_FILE " ]; then echo "Usage: $0 <backup_file>" exit 1 fi while IFS= read -r line; do if [[ $line =~ ^#.* ]]; then continue fi if [[ $line =~ ^\[(.+)\]:\ \[(.+)\]$ ]]; then key="${BASH_REMATCH[1]} " value="${BASH_REMATCH[2]} " echo "Restoring: $key =$value " setprop "$key " "$value " fi done < "$BACKUP_FILE " echo "Properties restored from $BACKUP_FILE "
6.4 升级后属性验证 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #!/system/bin/sh CRITICAL_PROPS=( "persist.sys.timezone" "persist.sys.locale" "persist.sys.usb.config" ) echo "=== Verifying Critical Properties ===" for prop in "${CRITICAL_PROPS[@]} " ; do value=$(getprop "$prop " ) if [ -z "$value " ]; then echo "⚠️ WARNING: $prop is not set!" else echo "✅ $prop = $value " fi done
7. 常用系统属性详解 7.1 设备信息属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ro.product.model # 设备型号(用户可见) ro.product.brand # 品牌名称 ro.product.manufacturer # 制造商 ro.product.device # 设备代号(内部使用) ro.product.name # 产品名称 ro.serialno # 设备序列号 ro.boot.serialno # 从 bootloader 传递的序列号 [ro.product.model] : [Pixel 6] [ro.product.brand] : [Google] [ro.product.manufacturer] : [Google] [ro.product.device] : [oriole]
7.2 系统版本属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ro.build.version.release # 版本号(如 13) ro.build.version.sdk # API Level(如 33) ro.build.version.security_patch # 安全补丁日期 ro.build.id # 构建 ID ro.build.display.id # 显示的构建 ID ro.build.version.incremental # 增量版本号 ro.build.type # 构建类型(user/userdebug/eng) [ro.build.version.release] : [13] [ro.build.version.sdk] : [33] [ro.build.version.security_patch] : [2023-01-05] [ro.build.type] : [user]
7.3 硬件信息属性 1 2 3 4 5 6 7 8 9 10 11 12 ro.hardware # 硬件名称 ro.board.platform # SoC 平台(如 gs101) ro.product.cpu.abi # 主 CPU 架构(如 arm64-v8a) ro.product.cpu.abilist # 支持的 CPU 架构列表 ro.product.cpu.abilist64 # 64位架构列表 ro.product.cpu.abilist32 # 32位架构列表 [ro.hardware] : [oriole] [ro.board.platform] : [gs101] [ro.product.cpu.abi] : [arm64-v8a]
7.4 调试相关属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 persist.sys.usb.config # USB 模式配置 ro.debuggable # 是否可调试(0/1) persist.adb.tcp.port # ADB TCP 端口 ro.adb.secure # ADB 安全模式 persist.log.tag # 全局日志级别(V/D/I/W/E) persist.log.tag.xxx # 特定 TAG 的日志级别 debug.logpersist # 持久化日志 debug.sf.showfps # 显示 FPS debug.sf.disable_backpressure # 禁用背压 debug.hwui.profile # HWUI 性能分析
7.5 系统行为属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 persist.sys.locale # 系统语言(如 zh-CN) persist.sys.timezone # 时区(如 Asia/Shanghai) persist.sys.lcd_density # 屏幕密度 ro.sf.lcd_density # 默认屏幕密度 sys.powerctl # 电源控制(reboot/shutdown) sys.boot_completed # 启动完成标志 persist.sys.wifi.always_on # WiFi 常开 net.hostname # 设备主机名
7.6 功能开关属性 1 2 3 4 5 6 7 8 9 10 11 12 13 persist.sys.cached_app_freezer # 应用冻结功能 persist.device_config.runtime_native_boot.enable_uffd_gc # UFFD GC persist.sys.dalvik.vm.lib.2 # Dalvik 虚拟机库 ro.secure # Secure Boot 状态 ro.boot.verifiedbootstate # 验证启动状态 ro.boot.flash.locked # Bootloader 锁定状态 persist.sys.strictmode.visual # 严格模式可视化 persist.sys.overlayfs.override_creds # OverlayFS 覆盖
8. 实践应用场景 8.1 特性开关(Feature Toggle) 场景 :通过系统属性控制功能的启用/禁用,便于灰度发布和 A/B 测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class FeatureManager { private static final String FEATURE_PREFIX = "persist.feature." ; public static boolean isFeatureEnabled (String featureName) { String propName = FEATURE_PREFIX + featureName; return SystemProperties.getBoolean(propName, false ); } public static void enableFeature (String featureName) { String propName = FEATURE_PREFIX + featureName; SystemProperties.set(propName, "true" ); } public static void disableFeature (String featureName) { String propName = FEATURE_PREFIX + featureName; SystemProperties.set(propName, "false" ); } } if (FeatureManager.isFeatureEnabled("new_ui" )) { } else { }
8.2 动态日志控制 场景 :在生产环境中动态调整日志级别,便于问题诊断。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class LogManager { private static final String LOG_TAG_PREFIX = "persist.log.tag." ; public static void setLogLevel (String tag, String level) { String propName = LOG_TAG_PREFIX + tag; SystemProperties.set(propName, level); } public static String getLogLevel (String tag) { String propName = LOG_TAG_PREFIX + tag; return SystemProperties.get(propName, "I" ); } public static boolean isLoggable (String tag, int level) { return Log.isLoggable(tag, level); } } LogManager.setLogLevel("MyModule" , "V" ); if (LogManager.isLoggable("MyModule" , Log.VERBOSE)) { Log.v("MyModule" , "Detailed debug info..." ); } LogManager.setLogLevel("MyModule" , "I" );
Shell 命令 :
1 2 3 4 5 6 7 8 adb shell setprop persist.log.tag.MyModule V adb logcat MyModule:V *:S adb shell setprop persist.log.tag.MyModule I
8.3 设备配置管理 场景 :在不同设备或配置下使用不同的参数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public class DeviceConfig { public static String getDeviceModel () { return SystemProperties.get("ro.product.model" , "Unknown" ); } public static boolean isTablet () { String characteristics = SystemProperties.get("ro.build.characteristics" , "" ); return characteristics.contains("tablet" ); } public static int getRamSizeGB () { ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); ActivityManager.MemoryInfo memInfo = new ActivityManager .MemoryInfo(); am.getMemoryInfo(memInfo); return (int ) (memInfo.totalMem / (1024 * 1024 * 1024 )); } public static int getOptimalThreadCount () { int ram = getRamSizeGB(); if (ram >= 8 ) { return 4 ; } else if (ram >= 4 ) { return 2 ; } else { return 1 ; } } }
8.4 性能监控与优化 场景 :通过属性控制性能监控和优化策略。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class PerformanceMonitor { public static boolean isPerformanceMonitorEnabled () { return SystemProperties.getBoolean("debug.performance.monitor" , false ); } public static int getSamplingRate () { return SystemProperties.getInt("debug.performance.sampling_rate" , 10 ); } public static void reportPerformance (String metric, long value) { if (!isPerformanceMonitorEnabled()) { return ; } int samplingRate = getSamplingRate(); if (Math.random() * 100 < samplingRate) { Analytics.report(metric, value); } } } long startTime = SystemClock.elapsedRealtime();long duration = SystemClock.elapsedRealtime() - startTime;PerformanceMonitor.reportPerformance("operation_duration" , duration);
8.5 A/B Testing 实现 场景 :基于系统属性实现 A/B 测试。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class ABTestManager { private static final String AB_TEST_PREFIX = "persist.abtest." ; public static String getExperimentGroup (String experimentName) { String propName = AB_TEST_PREFIX + experimentName; String group = SystemProperties.get(propName, "" ); if (group.isEmpty()) { group = Math.random() < 0.5 ? "A" : "B" ; SystemProperties.set(propName, group); } return group; } public static boolean isGroupA (String experimentName) { return "A" .equals(getExperimentGroup(experimentName)); } public static void resetExperiment (String experimentName) { String propName = AB_TEST_PREFIX + experimentName; SystemProperties.set(propName, "" ); } } if (ABTestManager.isGroupA("new_algorithm" )) { useNewAlgorithm(); } else { useOldAlgorithm(); }
9. 调试技巧 9.1 查看和过滤属性 常用 Shell 命令 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 adb shell getprop adb shell getprop | grep "ro.build" adb shell getprop | grep "persist." adb shell getprop | grep "debug." adb shell getprop | grep -E "ro\.(product|build)" adb shell getprop ro.product.model adb shell getprop | wc -l adb shell ls -lt /data/property/ | head -20
9.2 监控属性变化 实时监控脚本 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #!/bin/bash PROP_NAME=$1 INTERVAL=${2:-1} if [ -z "$PROP_NAME " ]; then echo "Usage: $0 <property_name> [interval_seconds]" exit 1 fi echo "Monitoring property: $PROP_NAME " echo "Press Ctrl+C to stop" echo "─────────────────────────────────────" LAST_VALUE="" while true ; do CURRENT_VALUE=$(adb shell getprop "$PROP_NAME " ) if [ "$CURRENT_VALUE " != "$LAST_VALUE " ]; then TIMESTAMP=$(date +"%Y-%m-%d %H:%M:%S" ) echo "[$TIMESTAMP ] $PROP_NAME : $LAST_VALUE -> $CURRENT_VALUE " LAST_VALUE="$CURRENT_VALUE " fi sleep "$INTERVAL " done
使用示例 :
1 2 3 4 5 ./monitor_property.sh sys.usb.state ./monitor_property.sh sys.boot_completed
9.3 导出与对比 导出脚本 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #!/bin/bash OUTPUT_DIR="./property_exports" TIMESTAMP=$(date +"%Y%m%d_%H%M%S" ) DEVICE_MODEL=$(adb shell getprop ro.product.model | tr -d '\r' ) mkdir -p "$OUTPUT_DIR " echo "Exporting properties from $DEVICE_MODEL ..." adb shell getprop > "$OUTPUT_DIR /all_${DEVICE_MODEL} _${TIMESTAMP} .txt" adb shell getprop | grep "^\[ro\." > "$OUTPUT_DIR /ro_${DEVICE_MODEL} _${TIMESTAMP} .txt" adb shell getprop | grep "^\[persist\." > "$OUTPUT_DIR /persist_${DEVICE_MODEL} _${TIMESTAMP} .txt" adb shell getprop | grep "^\[sys\." > "$OUTPUT_DIR /sys_${DEVICE_MODEL} _${TIMESTAMP} .txt" adb shell getprop | grep "^\[debug\." > "$OUTPUT_DIR /debug_${DEVICE_MODEL} _${TIMESTAMP} .txt" echo "Properties exported to $OUTPUT_DIR "
对比脚本 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #!/bin/bash FILE1=$1 FILE2=$2 if [ -z "$FILE1 " ] || [ -z "$FILE2 " ]; then echo "Usage: $0 <file1> <file2>" exit 1 fi echo "Comparing properties..." echo "File 1: $FILE1 " echo "File 2: $FILE2 " echo "─────────────────────────────────────" echo "" echo "=== New Properties ===" diff <(cut -d: -f1 "$FILE1 " ) <(cut -d: -f1 "$FILE2 " ) | grep "^>" | cut -d' ' -f2- echo "" echo "=== Removed Properties ===" diff <(cut -d: -f1 "$FILE1 " ) <(cut -d: -f1 "$FILE2 " ) | grep "^<" | cut -d' ' -f2- echo "" echo "=== Changed Properties ===" diff "$FILE1 " "$FILE2 " | grep "^[<>]" | grep -v "^---"
9.4 性能分析 测量属性访问性能 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class PropertyBenchmark { public static void benchmarkGet () { int iterations = 10000 ; String key = "ro.product.model" ; long startTime = System.nanoTime(); for (int i = 0 ; i < iterations; i++) { SystemProperties.get(key); } long endTime = System.nanoTime(); long avgTime = (endTime - startTime) / iterations; Log.d("Benchmark" , String.format( "Average get time: %d ns (%d µs)" , avgTime, avgTime / 1000 )); } public static void benchmarkSet () { int iterations = 1000 ; String key = "debug.benchmark.test" ; long startTime = System.nanoTime(); for (int i = 0 ; i < iterations; i++) { SystemProperties.set(key, String.valueOf(i)); } long endTime = System.nanoTime(); long avgTime = (endTime - startTime) / iterations; Log.d("Benchmark" , String.format( "Average set time: %d ns (%d µs)" , avgTime, avgTime / 1000 )); } }
典型性能指标 :
property_get(): ~1-5 µs(微秒)
property_set(): ~50-200 µs(需要 Binder IPC)
9.5 调试日志收集 完整日志收集脚本 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #!/system/bin/sh LOG_DIR=/sdcard/debug_logs TIMESTAMP=$(date +%Y%m%d_%H%M%S) LOG_FILE=$LOG_DIR /debug_$TIMESTAMP .txt mkdir -p $LOG_DIR echo "=== System Properties Debug Info ===" > $LOG_FILE echo "Timestamp: $(date) " >> $LOG_FILE echo "" >> $LOG_FILE echo "=== All Properties ===" >> $LOG_FILE getprop >> $LOG_FILE echo "" >> $LOG_FILE echo "=== Persist Property Storage ===" >> $LOG_FILE ls -la /data/property/ >> $LOG_FILE echo "" >> $LOG_FILE echo "=== Persist Properties (from getprop) ===" >> $LOG_FILE getprop | grep "^\[persist\." >> $LOG_FILE echo "" >> $LOG_FILE echo "=== Build Property Files ===" >> $LOG_FILE for file in /system/build.prop /vendor/build.prop /product/build.prop; do if [ -f "$file " ]; then echo "--- $file ---" >> $LOG_FILE cat "$file " >> $LOG_FILE echo "" >> $LOG_FILE fi done echo "=== Recent Logcat ===" >> $LOG_FILE logcat -d -t 500 >> $LOG_FILE cd $LOG_DIR tar -czf debug_$TIMESTAMP .tar.gz debug_$TIMESTAMP .txt rm debug_$TIMESTAMP .txtecho "Debug logs collected: $LOG_DIR /debug_$TIMESTAMP .tar.gz"
10. 安全与权限 10.1 SELinux 策略 系统属性的访问受 SELinux 策略控制,策略定义在 property_contexts 文件中。
property_contexts 文件位置 :
/system/etc/selinux/plat_property_contexts
/vendor/etc/selinux/vendor_property_contexts
示例策略 :
1 2 3 4 5 6 # property_contexts 格式 # property_name selinux_context ro. u:object_r:default_prop:s0 persist.sys. u:object_r:system_prop:s0 debug. u:object_r:debug_prop:s0
权限检查流程 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 应用进程 │ ├──► SystemProperties.set() │ ▼ Property Service │ ├──► SELinux 策略检查 │ property_contexts │ │ │ ├──► 检查进程的 SELinux 上下文 │ │ (u:r:untrusted_app:s0) │ │ │ └──► 检查属性的 SELinux 上下文 │ (u:object_r:system_prop:s0) │ ├──► 允许? │ ├──► Yes ──► 设置属性 │ └──► No ──► 返回权限错误
10.2 权限分类
属性前缀
SELinux 上下文
读权限
写权限
说明
ro.*
default_prop
所有进程
❌ 无
只读属性
persist.sys.*
system_prop
所有进程
system_server, init
系统属性
debug.*
debug_prop
所有进程
shell, root, eng 构建
调试属性
sys.*
system_prop
所有进程
system_server, init
运行时系统属性
vendor.*
vendor_prop
所有进程
vendor 进程
厂商属性
10.3 添加自定义属性权限 步骤1: 定义 SELinux 类型
1 2 # device/vendor/sepolicy/private/property.te type custom_prop, property_type;
步骤2: 添加到 property_contexts
1 2 # device/vendor/sepolicy/private/property_contexts persist.custom. u:object_r:custom_prop:s0
步骤3: 授权进程访问
1 2 3 4 5 6 # device/vendor/sepolicy/private/system_app.te # 允许系统应用读取 get_prop(system_app, custom_prop) # 允许系统应用设置 set_prop(system_app, custom_prop)
10.4 权限检查示例 Java 层权限检查 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class PropertyPermissionChecker { public static boolean canSetProperty (String propName) { try { SystemProperties.set(propName, SystemProperties.get(propName, "" )); return true ; } catch (Exception e) { Log.e("Permission" , "Cannot set property: " + propName, e); return false ; } } public static boolean setPropertySafely (String propName, String value) { try { SystemProperties.set(propName, value); return true ; } catch (SecurityException e) { Log.e("Permission" , "Permission denied for: " + propName); return false ; } catch (Exception e) { Log.e("Permission" , "Failed to set property: " + propName, e); return false ; } } }
检查当前进程的 SELinux 上下文 :
1 2 3 4 5 adb shell ps -eZ | grep <process_name> u:r:untrusted_app:s0:c123,c256 u0_a123 12345 ... com.example.app
11. 最佳实践 11.1 命名规范 推荐的命名模式 :
1 2 3 4 5 6 7 8 [prefix].[module].[feature].[name] 示例: persist.sys.power.low_battery_threshold │ │ │ └─ 具体功能名称 │ │ └─ 模块名称 │ └─ 子系统 └─ 属性类型前缀
命名建议 :
使用有意义的前缀
1 2 3 4 5 6 7 persist.app.myapp.feature_enabled persist.custom.mycompany.config persist.x persist.temp
保持层次清晰
1 2 3 4 5 6 7 persist.network.wifi.auto_reconnect persist.network.mobile.prefer_5g persist.wifi_reconnect persist.mobile_5g
避免特殊字符
1 2 3 4 5 6 persist.my.app.setting_value persist.my.app.setting-value # 使用连字符 persist.my.app.setting value # 包含空格
11.2 性能优化 1. 缓存频繁访问的属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public class CachedPropertyManager { private static final Map<String, String> sCache = new ConcurrentHashMap <>(); private static final Set<String> sCachedKeys = new HashSet <>(); static { sCachedKeys.add("ro.product.model" ); sCachedKeys.add("ro.build.version.sdk" ); } public static String get (String key, String defaultValue) { if (sCachedKeys.contains(key)) { String cached = sCache.get(key); if (cached != null ) { return cached; } String value = SystemProperties.get(key, defaultValue); sCache.put(key, value); return value; } return SystemProperties.get(key, defaultValue); } public static void clearCache () { sCache.clear(); } }
2. 批量读取优化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public class BatchPropertyReader { public static Map<String, String> getProperties (String... keys) { Map<String, String> result = new HashMap <>(); for (String key : keys) { result.put(key, SystemProperties.get(key, "" )); } return result; } public static Map<String, String> getPropertiesByPrefix (String prefix) { Map<String, String> result = new HashMap <>(); try { Process process = Runtime.getRuntime().exec("getprop" ); BufferedReader reader = new BufferedReader ( new InputStreamReader (process.getInputStream()) ); String line; while ((line = reader.readLine()) != null ) { if (line.contains(prefix)) { Matcher matcher = Pattern.compile("\\[(.+?)\\]: \\[(.+?)\\]" ) .matcher(line); if (matcher.find()) { result.put(matcher.group(1 ), matcher.group(2 )); } } } } catch (IOException e) { Log.e("BatchReader" , "Failed to read properties" , e); } return result; } }
11.3 错误处理 健壮的属性访问 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 public class SafePropertyAccess { public static String getString (String key, String defaultValue) { try { return SystemProperties.get(key, defaultValue); } catch (Exception e) { Log.w("Property" , "Failed to get property: " + key, e); return defaultValue; } } public static int getInt (String key, int defaultValue) { try { String value = SystemProperties.get(key, "" ); if (value.isEmpty()) { return defaultValue; } return Integer.parseInt(value); } catch (NumberFormatException e) { Log.w("Property" , "Invalid integer value for: " + key); return defaultValue; } catch (Exception e) { Log.w("Property" , "Failed to get property: " + key, e); return defaultValue; } } public static boolean getBoolean (String key, boolean defaultValue) { try { String value = SystemProperties.get(key, "" ).toLowerCase(); if (value.isEmpty()) { return defaultValue; } return value.equals("true" ) || value.equals("1" ) || value.equals("yes" ) || value.equals("on" ); } catch (Exception e) { Log.w("Property" , "Failed to get property: " + key, e); return defaultValue; } } public static boolean set (String key, String value) { try { SystemProperties.set(key, value); return true ; } catch (IllegalArgumentException e) { Log.e("Property" , "Invalid property name or value" , e); return false ; } catch (SecurityException e) { Log.e("Property" , "Permission denied" , e); return false ; } catch (Exception e) { Log.e("Property" , "Failed to set property" , e); return false ; } } }
11.4 测试建议 单元测试示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 @RunWith(AndroidJUnit4.class) public class PropertyTest { @Test public void testReadOnlyProperty () { String model = SystemProperties.get("ro.product.model" ); assertNotNull(model); assertFalse(model.isEmpty()); } @Test public void testPropertyDefaultValue () { String value = SystemProperties.get("test.nonexistent.prop" , "default" ); assertEquals("default" , value); } @Test public void testPropertyTypes () { assertEquals(33 , SystemProperties.getInt("ro.build.version.sdk" , 0 )); assertTrue(SystemProperties.getBoolean("ro.debuggable" , false ) || !SystemProperties.getBoolean("ro.debuggable" , false )); } @Test @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public void testSetProperty () { String testKey = "debug.test.property" ; String testValue = "test_value_" + System.currentTimeMillis(); try { SystemProperties.set(testKey, testValue); assertEquals(testValue, SystemProperties.get(testKey)); } finally { SystemProperties.set(testKey, "" ); } } }
集成测试脚本 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #!/system/bin/sh echo "=== Property System Test ===" echo "Test 1: Read ro.* properties" MODEL=$(getprop ro.product.model) if [ -n "$MODEL " ]; then echo "✅ PASS: ro.product.model = $MODEL " else echo "❌ FAIL: Cannot read ro.product.model" fi echo "" echo "Test 2: Set/Get persist.* properties" TEST_KEY="persist.test.property" TEST_VALUE="test_$(date +%s) " setprop "$TEST_KEY " "$TEST_VALUE " RESULT=$(getprop "$TEST_KEY " ) if [ "$RESULT " = "$TEST_VALUE " ]; then echo "✅ PASS: Property set/get successful" else echo "❌ FAIL: Property set/get failed" fi setprop "$TEST_KEY " "" echo "" echo "Test 3: Check persistent storage" PERSIST_FILE="/data/property/persistent_properties" if [ -f "$PERSIST_FILE " ]; then FILE_SIZE=$(stat -c%s "$PERSIST_FILE " 2>/dev/null || stat -f%z "$PERSIST_FILE " 2>/dev/null) echo "✅ PASS: Persistent properties file exists (size: $FILE_SIZE bytes)" else echo "❌ FAIL: Persistent properties file not found" fi echo "" echo "=== Test Complete ==="
11.5 文档化 属性清单文档模板 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 # 应用系统属性清单 ## persist.app.myapp.* ### persist.app.myapp.feature_enabled - **类型** : Boolean - **默认值** : false - **说明** : 新功能开关 - **修改方式** : `setprop persist.app.myapp.feature_ enabled true`- **生效时机** : 应用重启后生效- **影响范围** : 主功能模块### persist.app.myapp.log_level - **类型** : String (V/D/I/W/E) - **默认值** : I (Info) - **说明** : 应用日志级别 - **修改方式** : `setprop persist.app.myapp.log_ level D`- **生效时机** : 立即生效- **影响范围** : 所有日志输出## debug.app.myapp.* ### debug.app.myapp.performance_monitor - **类型** : Boolean - **默认值** : false - **说明** : 性能监控开关 - **修改方式** : `setprop debug.app.myapp.performance_ monitor true`- **生效时机** : 立即生效- **影响范围** : 性能统计模块- **注意事项** : 仅在 debug 版本有效
12. 参考资料 12.1 源码文件 核心源码位置 :
1 2 3 4 5 bionic/libc/bionic/system_properties.cpp # Native 实现 frameworks/base/core/jni/AndroidRuntime.cpp # JNI 绑定 frameworks/base/core/java/android/os/SystemProperties.java # Java API system/core/init/property_service.cpp # Property Service system/sepolicy/private/property_contexts # SELinux 策略
12.2 相关文档
12.3 工具
工具
用途
命令
getprop
读取属性
adb shell getprop [key]
setprop
设置属性
adb shell setprop key value
watchprops
监控属性变化(部分版本)
adb shell watchprops
dumpsys
系统诊断
adb shell dumpsys
12.4 常见问题 Q1: 为什么 setprop 设置后属性没有变化?
A: 可能的原因:
权限不足(需要 root 或特定权限)
SELinux 策略阻止
属性是只读的(ro.* 前缀)
属性值超过长度限制(91 字符)
Q2: persist. 属性在哪些情况下会丢失? *
A: 会丢失的情况:
恢复出厂设置(Factory Reset)
清除数据(Wipe Data)
/data 分区被格式化
不会丢失的情况:
Q3: 如何判断一个属性是否存在?
A: 方法:
1 2 3 4 String value = SystemProperties.get("key" , null );if (value != null && !value.isEmpty()) { }
或使用 shell:
1 2 3 if [ -n "$(getprop key) " ]; then echo "Property exists" fi
总结 Android 系统属性是一个功能强大且高效的配置管理系统,正确使用可以:
✅ 简化配置管理 - 统一的键值对接口 ✅ 提升开发效率 - 动态调试和功能开关 ✅ 增强系统灵活性 - 运行时配置调整 ✅ 支持跨进程通信 - 轻量级数据共享
但也需要注意:
⚠️ 权限控制 - 严格的 SELinux 策略 ⚠️ 性能考虑 - 避免频繁的 set 操作 ⚠️ 命名规范 - 遵循统一的命名约定 ⚠️ 持久化策略 - 理解不同前缀的生命周期
通过本文的系统学习,您应该能够:
理解系统属性的架构和机制
正确使用各种 API 读写属性
根据场景选择合适的属性类型
遵循最佳实践开发健壮的代码
有效调试和诊断属性相关问题
文档版本 : 1.1最后更新 : 2025-01-15作者 : Android Framework 团队适用版本 : Android 8.0+ (API 26+)修订说明 : 修正持久化属性存储机制描述(感谢用户反馈)
相关文档 :