当前位置:   article > 正文

Android之系统属性(SystemProperties)_android.os.systemproperties

android.os.systemproperties

一、系统属性

        系统属性是系统中具有特殊含义的键值对数据,我们在开发过程中有时需要使用系统属性,例如获取系统软件版本,获取设备名名称等,有时也需要设置自定义属性。系统属性具有全局性的特点,存取方便。

二、获取和设置

2.1 架构

2.2 使用终端

  1. //获取系统属性值
  2. getprop my.prop.test
  3. //设置系统属性值
  4. setprop my.prop.test
  5. //监听系统属性变化
  6. watchprops my.prop.test

2.3 Java代码中

        在Android源码中的android.os下有一个隐藏类:SystemProperties,通过SystemProperties.set及SystemProperties.get可以设置和获取系统属性。        

Systemproperties部分源码如下:frameworks\base\core\java\android\os\SystemProperties.java

  1. ...
  2. public class SystemProperties {
  3. ...
  4. //获取属性key的值,如果没有该属性则返回默认值def
  5. @SystemApi
  6. public static String get(@NonNull String key, @Nullable String def) {
  7. if (TRACK_KEY_ACCESS) onKeyAccess(key);
  8. return native_get(key, def);
  9. }
  10. ...
  11. //设置属性key的值为val
  12. @SystemApi
  13. public static void set(@NonNull String key, @Nullable String val) {
  14. if (val != null && !val.startsWith("ro.") && val.length() > PROP_VALUE_MAX) {
  15. throw new IllegalArgumentException("value of system property '" + key
  16. + "' is longer than " + PROP_VALUE_MAX + " characters: " + val);
  17. }
  18. if (TRACK_KEY_ACCESS) onKeyAccess(key);
  19. native_set(key, val);
  20. }
  21. ....
  22. }

但是该类的接口也不是对外开放的,需要通过反射的方式来使用。下面是我的一个 SystemProperties 工具类:

  1. public class SystemProperties {
  2. private final static String TAG = "SystemProperties";
  3. public static String get(String key) {
  4. Class<?> SysProp = null;
  5. Method method = null;
  6. String value = null;
  7. try {
  8. SysProp = Class.forName("android.os.SystemProperties");
  9. method = SysProp.getMethod("get", String.class);
  10. value = (String) method.invoke(null, key);
  11. Log.i(TAG, "value:" + value);
  12. } catch (Exception e) {
  13. Log.e(TAG,"read SystemProperties error",e);
  14. }
  15. return value;
  16. }
  17. public static String get(String key, String defaultValue) {
  18. Class<?> SysProp = null;
  19. Method method = null;
  20. String value = null;
  21. try {
  22. SysProp = Class.forName("android.os.SystemProperties");
  23. method = SysProp.getMethod("get", String.class, String.class);
  24. value = (String) method.invoke(null, key, defaultValue);
  25. } catch (Exception e) {
  26. Log.e(TAG,"read SystemProperties error",e);
  27. }
  28. return value;
  29. }
  30. public static int getInt(String key, int defaultValue) {
  31. Class<?> SysProp = null;
  32. Method method = null;
  33. int value = 0;
  34. try {
  35. SysProp = Class.forName("android.os.SystemProperties");
  36. method = SysProp.getMethod("getInt", String.class, int.class);
  37. value = (Integer) method.invoke(null, key, defaultValue);
  38. } catch (Exception e) {
  39. Log.e(TAG,"read SystemProperties error",e);
  40. }
  41. return value;
  42. }
  43. public static long getLong(String key, long defaultValue) {
  44. Class<?> SysProp = null;
  45. Method method = null;
  46. long value = 0;
  47. try {
  48. SysProp = Class.forName("android.os.SystemProperties");
  49. method = SysProp.getMethod("getLong", String.class, long.class);
  50. value = (Long) method.invoke(null, key, defaultValue);
  51. } catch (Exception e) {
  52. Log.e(TAG,"read SystemProperties error",e);
  53. }
  54. return value;
  55. }
  56. public static boolean getBoolean(String key, boolean defaultValue) {
  57. Class<?> SysProp = null;
  58. Method method = null;
  59. boolean value = false;
  60. try {
  61. SysProp = Class.forName("android.os.SystemProperties");
  62. method = SysProp.getMethod("getBoolean", String.class, boolean.class);
  63. value = (Boolean) method.invoke(null, key, defaultValue);
  64. } catch (Exception e) {
  65. Log.e(TAG,"read SystemProperties error",e);
  66. }
  67. return value;
  68. }
  69. public static void set(String key, String value) {
  70. Class<?> SysProp = null;
  71. Method method = null;
  72. try {
  73. SysProp = Class.forName("android.os.SystemProperties");
  74. method = SysProp.getMethod("set", String.class, String.class);
  75. method.invoke(null, key, value);
  76. } catch (Exception e) {
  77. Log.e(TAG,"read SystemProperties error",e);
  78. }
  79. }
  80. public static void addChangeCallback(Runnable runnable) {
  81. Class<?> SysProp = null;
  82. Method method = null;
  83. try {
  84. SysProp = Class.forName("android.os.SystemProperties");
  85. method = SysProp.getMethod("addChangeCallback", Runnable.class);
  86. method.invoke(null, runnable);
  87. } catch (Exception e) {
  88. Log.e(TAG,"read SystemProperties error",e);
  89. }
  90. }
  91. }

下面以ActivityManagerService为例,frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java
列举源码中使用prop的例子:

  1. final void finishBooting() {
  2. ...
  3. //设置开机完成标志属性sys.boot_completed
  4. SystemProperties.set("sys.boot_completed", "1");
  5. }
  6. ...
  7. private static void maybePruneOldTraces(File tracesDir) {
  8. ...
  9. //获取tombstoned.max_anr_count属性值
  10. final int max = SystemProperties.getInt("tombstoned.max_anr_count", 64);
  11. }

 2.4 C++代码中

下面以MPEG4Writer.cpp为例,

frameworks\av\media\libstagefright\MPEG4Writer.cpp
列举源码中使用prop的例子:

  1. #include <cutils/properties.h>
  2. ...
  3. void MPEG4Writer::addDeviceMeta() {
  4. ...
  5. if (property_get("ro.build.version.release", val, NULL)
  6. ...
  7. if (property_get("ro.product.model", val, NULL)
  8. ...
  9. }

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

  1. include <cutils/properties.h>
  2. Android.mk或Android.bp或Makefile中需要链接libcutils库frameworks\av\media\libstagefright\Android.bp
  1. shared_libs: [
  2. ...
  3. "libcutils",
  4. ...

properties.h源码在:system/core/libcutils/include/cutils/properties.h

  1. int property_get(const char* key, char* value, const char* default_value);
  2. int property_set(const char *key, const char *value);
  3. int property_list(void (*propfn)(const char *key, const char *value, void *cookie), void *cookie);

三、特殊属性

3.1 ro只读属性

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

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

3.2 persist持久属性

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

  1. $ getprop persist.hello.test //属性为空
  2. $ setprop persist.hello.test abc //设置属性persist.hello.test值为abc
  3. $ getprop persist.hello.test abc //属性get正常
  4. abc
  5. $reboot //重启设备
  6. $ getprop persist.hello.test //属性为abc
  7. abc

3.3 ctl 控制属性

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

3.4 sys.powerctl属性

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

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

3.5 普通属性

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

  1. $ getprop hello.test //属性为空
  2. $ setprop hello.test 123//设置属性persist.hello.test值为abc
  3. $ getprop hello.test 123//属性get正常
  4. 123
  5. $reboot //重启设备
  6. $ getprop hello.test //属性为空

3.7 添加系统默认属性

系统开机时会加载 *.prop属性配置文件中的属性,因此开机后就有了默认属性。init进程中property_load_boot_defaults(load_debug_prop)函数分析。

位置:system\core\init\property_service.cpp

  1. void property_load_boot_defaults(bool load_debug_prop) {
  2. std::map<std::string, std::string> properties;
  3. //加载属性build.prop、default.prop至properties
  4. if (!load_properties_from_file("/system/etc/prop.default", nullptr, &properties)) {
  5. // Try recovery path
  6. if (!load_properties_from_file("/prop.default", nullptr, &properties)) {
  7. // Try legacy path
  8. load_properties_from_file("/default.prop", nullptr, &properties);
  9. }
  10. }
  11. load_properties_from_file("/system/build.prop", nullptr, &properties);
  12. load_properties_from_file("/vendor/default.prop", nullptr, &properties);
  13. load_properties_from_file("/vendor/build.prop", nullptr, &properties);
  14. if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
  15. load_properties_from_file("/odm/etc/build.prop", nullptr, &properties);
  16. } else {
  17. load_properties_from_file("/odm/default.prop", nullptr, &properties);
  18. load_properties_from_file("/odm/build.prop", nullptr, &properties);
  19. }
  20. load_properties_from_file("/product/build.prop", nullptr, &properties);
  21. load_properties_from_file("/product_services/build.prop", nullptr, &properties);
  22. load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
  23. ...
  24. //将properties中属性通过PropertySet存入属性属性
  25. for (const auto& [name, value] : properties) {
  26. std::string error;
  27. if (PropertySet(name, value, &error) != PROP_SUCCESS) {
  28. LOG(ERROR) << "Could not set '" << name << "' to '" << value
  29. << "' while loading .prop files" << error;
  30. }
  31. }
  32. property_initialize_ro_product_props();//初始化ro_product前缀的只读属性
  33. property_derive_build_props();//初始化编译相关属性
  34. update_sys_usb_config();//设置persist.sys.usb.config属性,用于控制USB调试和文件传输功能
  35. }

从上面代码可以看出从多个属性文件.prop中将系统属性加载至properties变量,再通过PropertySet()将properties添加到系统中,并初始化只读、编译、usb相关属性值。在PropertySet()中是通过Socket与属性服务进行通信,将props存储共享内存。

下面举一个自己在device/google/marlin/system.prop中添加系统属性的例子:

  1. # 添加自己的系统默认属性
  2. persist.hello.world=hello

注意:这里添加的属性前缀必须是在system/sepolicy/private/property_contexts中被定义过的,否则无效。make android后在out/target/product/xxx/system/build.prop 或out/target/product/xxx/vendor/build.prop可找到添加的属性persist.hello.world,则说明基本添加成功。

四、添加定制的*.prop

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

  1. device/qcom/qssi/hello.prop
  2. device/qcom/qssi/qssi.mk
  3. device/qcom/sepolicy/generic/private/property_contexts
  4. system/core/rootdir/init.rc
  5. system/core/init/property_service.cpp

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

4.1 device下添加hello.prop

device/qcom/qssi/hello.prop

  1. #
  2. # system.prop for qssi
  3. #
  4. ro.hello.year=2022 //添加ro属性
  5. persist.hello.month=07 //添加persist属性
  6. hello.day=25 //添加hello属性
  7. ro.product.model=HelloWorld //定制系统已有ro.product.model属性

4.2 配置预置路径

修改device下的device.mk来指定hello.prop的预置路径device/qcom/qssi/qssi.mk

  1. #将hello.prop预置到system/hello.prop
  2. PRODUCT_COPY_FILES += \
  3. device/qcom/qssi/hello.prop:system/hello.prop

4.3 SELinux权限配置

hello.开头的属性是新添加的配置,需要在配置对应的SELinux规则,否则无效,配置方法如下:device/qcom/sepolicy/generic/private/property_contexts

hello.                             u:object_r:system_prop:s0

4.4 配置hello.prop权限

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

  1. on fs
  2. chmod 0600 /system/hello.prop

4.5 加载hello.prop

仅仅将hello.prop预置到system/hello.prop还不够,系统启动时需要load hello.prop才能使其生效system/core/init/property_service.cpp

  1. load_properties_from_file("/system/build.prop", nullptr, &properties);
  2. load_properties_from_file("/vendor/default.prop", nullptr, &properties);
  3. load_properties_from_file("/vendor/build.prop", nullptr, &properties);
  4. if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
  5. load_properties_from_file("/odm/etc/build.prop", nullptr, &properties);
  6. } else {
  7. load_properties_from_file("/odm/default.prop", nullptr, &properties);
  8. load_properties_from_file("/odm/build.prop", nullptr, &properties);
  9. }
  10. load_properties_from_file("/product/build.prop", nullptr, &properties);
  11. load_properties_from_file("/product_services/build.prop", nullptr, &properties);
  12. load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
  13. //load 预置的hello.prop ,最后load保证其配置属性优先级更高
  14. load_properties_from_file("/system/hello.prop", nullptr, &properties);

4.6 验证

Android全编译后正常情况可找到生成的out/target/product/qssi/system/hello.prop

检查其内容应与device/qcom/qssi/hello.prop内容保持一致。

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号