Android 系统属性

本文转载自:Android之系统属性(SystemProperties)

1.简介

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

2.获取和设置

2.1 架构

系统属性_01.png

2.2 使用终端

//获取系统属性值
getprop my.prop.test

//设置系统属性值
setprop my.prop.test

//监听系统属性变化
watchprops my.prop.test

2.3 Java代码中

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

// 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);
    }
....
}

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

public class SystemProperties {

    private final static String TAG = "SystemProperties";

    public static String get(String key) {
        Class<?> SysProp = null;
        Method method = null;
        String value = null;
        try {
            SysProp = Class.forName("android.os.SystemProperties");
            method = SysProp.getMethod("get", String.class);
            value = (String) method.invoke(null, key);
            Log.i(TAG, "value:" + value);
        } catch (Exception e) {
            Log.e(TAG,"read SystemProperties error",e);
        }
        return value;
    }

    public static String get(String key, String defaultValue) {
        Class<?> SysProp = null;
        Method method = null;
        String value = null;
        try {
            SysProp = Class.forName("android.os.SystemProperties");
            method = SysProp.getMethod("get", String.class, String.class);
            value = (String) method.invoke(null, key, defaultValue);
        } catch (Exception e) {
            Log.e(TAG,"read SystemProperties error",e);
        }
        return value;
    }

    public static int getInt(String key, int defaultValue) {
        Class<?> SysProp = null;
        Method method = null;
        int value = 0;
        try {
            SysProp = Class.forName("android.os.SystemProperties");
            method = SysProp.getMethod("getInt", String.class, int.class);
            value = (Integer) method.invoke(null, key, defaultValue);
        } catch (Exception e) {
            Log.e(TAG,"read SystemProperties error",e);
        }
        return value;
    }

    public static long getLong(String key, long defaultValue) {
        Class<?> SysProp = null;
        Method method = null;
        long value = 0;
        try {
            SysProp = Class.forName("android.os.SystemProperties");
            method = SysProp.getMethod("getLong", String.class, long.class);
            value = (Long) method.invoke(null, key, defaultValue);
        } catch (Exception e) {
            Log.e(TAG,"read SystemProperties error",e);
        }
        return value;
    }

    public static boolean getBoolean(String key, boolean defaultValue) {
        Class<?> SysProp = null;
        Method method = null;
        boolean value = false;
        try {
            SysProp = Class.forName("android.os.SystemProperties");
            method = SysProp.getMethod("getBoolean", String.class, boolean.class);
            value = (Boolean) method.invoke(null, key, defaultValue);
        } catch (Exception e) {
            Log.e(TAG,"read SystemProperties error",e);
        }
        return value;
    }

    public static void set(String key, String value) {
        Class<?> SysProp = null;
        Method method = null;
        try {
            SysProp = Class.forName("android.os.SystemProperties");
            method = SysProp.getMethod("set", String.class, String.class);
            method.invoke(null, key, value);
        } catch (Exception e) {
            Log.e(TAG,"read SystemProperties error",e);
        }
    }

    public static void addChangeCallback(Runnable runnable) {
        Class<?> SysProp = null;
        Method method = null;
        try {
            SysProp = Class.forName("android.os.SystemProperties");
            method = SysProp.getMethod("addChangeCallback", Runnable.class);
            method.invoke(null, runnable);
        } catch (Exception e) {
            Log.e(TAG,"read SystemProperties error",e);
        }
    }
}

下面以ActivityManagerService为例,列举源码中使用prop的例子:

// frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java
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);
}

2.4 C++代码中

  下面以MPEG4Writer.cpp为例,列举源码中使用prop的例子:

// 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)
    ...
}

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

  1. include <cutils/properties.h>;

  2. Android.mk或Android.bp或Makefile中需要链接libcutils库。

// frameworks\av\media\libstagefright\Android.bp
shared_libs: [
        ...
        "libcutils",
        ...

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);

3.特殊属性

3.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'

3.2 persist持久属性

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

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

3.3 ctl 控制属性

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

3.4 sys.powerctl属性

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

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

3.5 普通属性

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

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

3.6 添加系统默认属性

  系统开机时会加载 *.prop属性配置文件中的属性,因此开机后就有了默认属性。init进程中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;
    //加载属性build.prop、default.prop至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);
    ...
    //将properties中属性通过PropertySet存入属性属性
    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_props();//初始化编译相关属性
    update_sys_usb_config();//设置persist.sys.usb.config属性,用于控制USB调试和文件传输功能
}

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

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

# 添加自己的系统默认属性
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,则说明基本添加成功。

4.添加定制的*.prop

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

device/qcom/qssi/hello.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文件中,下面以添加hello.prop为例说明添加过程。

4.1 device下添加hello.prop

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

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

4.2 配置预置路径

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

#将hello.prop预置到system/hello.prop
PRODUCT_COPY_FILES += \
    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。

on fs
   chmod 0600 /system/hello.prop

4.5 加载hello.prop

  仅仅将hello.prop预置到system/hello.prop还不够,系统启动时需要load hello.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 预置的hello.prop ,最后load保证其配置属性优先级更高
   load_properties_from_file("/system/hello.prop", nullptr, &properties);

4.6 验证

  Android全编译后正常情况可找到生成的out/target/product/qssi/system/hello.prop,检查其内容应与device/qcom/qssi/hello.prop内容保持一致。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345