Android12系统之系统属性

Android系统属性相关知识点记录
Views: 535
0 0
Read Time:3 Minute, 14 Second

在安卓的日常开发中,系统属性是会经常遇到并使用的。系统属性在作用上比较类似于安卓系统中的环境变量,但相对于环境变量,系统属性具备更多的功能特性以及更高的灵活性,本篇文章就Android12为例总结一下安卓系统属性的相关知识。

系统属性定义

在我们了解与使用系统属性时,我们应该要先了解系统属性的定义,在进行自定义属性时遵循其定义规范。

属性名称原则上可以为任意字符串,不过Google针对属性的名称制定了一套规范,大家在制定属性时应尽可能遵守该规范:

[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]

其中prefix字段可以分为三种:

1)使用“”,一般我们会将“”进行忽略,这种系统属性会在系统重新启动之后丢失,在单个运行周期内可读写

2)使用ro,该prefix表明该属性是read only,无法被在系统运行期间改变,重启仍会保留

3)使用persist,该prefix表明属性是断电保存的,也就是说重启(包含冷启动与热启动)不会导致属性丢失,在单个运行周期内可读写

接下来是group字段,其用于聚合相关属性,一般用android子系统的名称进行标识,如audio,camera,graphics,telephony等,我们应当借鉴SELinux配置system/sepolicy/private/property_contexts中对android各个常见子系统的定义,如下所示:

子系统定义
蓝牙相关bluetooth
内核相关boot
编译相关build
电话相关telephony
音频相关audio
图形相关graphics
vold相关vold

如果是新增的group字段,我们需要补齐对应的SELinux上下文定义,否则会在SELinux规则检查时报错,在permissive权限下出现avc报错,在enforcing权限下则可能会导致程序无法运行。

除了group,我们还可以通过subgroup进一步地进行限定,一个group下可以拥有多个subgroup,定义subgroup应尽量避免重复字段或含义重复。

接下来是name和type,其中name 用于标识群组中的属性具体含义,属于高度概括的字段,而type 是一个可选元素,用于阐明属性name所对应的类型或 intent。对于type的使用,并没有严格的规定,不过官方仍旧给出了一些使用参考:

enabled:如果类型是用于启用或停用功能的布尔值系统属性,请使用此类型。

config:如果 intent 是为了阐明系统属性不代表系统的动态状态,请使用此类型;它表示一个预配置的值(例如只读对象)。

List:如果系统属性的值为列表,请使用此类型。

Timeoutmillis:如果是超时值(以毫秒为单位)的系统属性,请使用此类型

预设系统属性

这里讲的预设系统属性,是指我们在编译前进行设置,在编译构建之后这些预设的属性跟随镜像一起分发,在系统启动时自动进行设定的使用场景。这种场景也是较为常见的,尤其是当我们的运行程序需要依赖于系统属性的值做差异化处理时,我们往往都希望在编译构建前就能完成系统属性的差异化设定。

事实上,我们可以在编译前通过在makefile中设置变量来达到系统启动时自动设置环境变量,在Android12中所支持的变量如下:

PRODUCT_SYSTEM_PROPERTIES
PRODUCT_VENDOR_PROPERTIES
PRODUCT_ODM_PROPERTIES
PRODUCT_PRODUCT_PROPERTIES

这里给出一个使用的示例:

PRODUCT_SYSTEM_PROPERTIES += ro.launcher.blur.appLaunch=0

在构建之后,我们在系统启动之后就会看到ro.launcher.blur.appLaunch的值为0。

上述变量所定义的属性值最终都会汇聚到每个partion下的build.prop,init进程启动时会读取这些build.prop并进行设定,在Android12中所有的build.prop如下:

./product/etc/build.prop
./vendor_dlkm/etc/build.prop
./system/build.prop
./vendor/odm_dlkm/etc/build.prop
./vendor/odm/etc/build.prop
./vendor/build.prop
./system_ext/etc/build.prop

相关的执行逻辑源码位于system/core/init/property_service.cpp中,这里摘录其核心部分:

//system/core/init/property_service.cpp
void PropertyLoadBootDefaults() {
    .....  
    LoadPropertiesFromSecondStageRes(&properties);
    load_properties_from_file("/system/build.prop", nullptr, &properties);
    load_properties_from_partition("system_ext", /* support_legacy_path_until */ 30);
    // TODO(b/117892318): uncomment the following condition when vendor.imgs for aosp_* targets are
    // all updated.
    // if (SelinuxGetVendorAndroidVersion() <= __ANDROID_API_R__) {
    load_properties_from_file("/vendor/default.prop", nullptr, &properties);
    // }
    load_properties_from_file("/vendor/build.prop", nullptr, &properties);
    load_properties_from_file("/vendor_dlkm/etc/build.prop", nullptr, &properties);
    load_properties_from_file("/odm_dlkm/etc/build.prop", nullptr, &properties);
    load_properties_from_partition("odm", /* support_legacy_path_until */ 28);
    load_properties_from_partition("product", /* support_legacy_path_until */ 30);
    .....
    property_initialize_ro_product_props();
    property_initialize_build_id();
    property_derive_build_fingerprint();
    property_derive_legacy_build_fingerprint();
    property_initialize_ro_cpu_abilist();

    update_sys_usb_config();
}

属性获取设定

在日常使用时,我们经常需要获取与设定系统属性。首先是从命令行进行获取或设置,这里我们会用到两个命令:getprop与setprop;前者用于获取属性,而后者用于设置系统属性。如下所示:

//getprop + grep
trout_x86:/ $ getprop | grep audio
[init.svc.audioserver]: [running]
[init.svc.vendor.audio-hal]: [running]

//setprop
trout_x86:/ $ setprop persist.evs.camera.mode 2
trout_x86:/ $ getprop | grep "evs.camera"
[persist.evs.camera.mode]: [2]

除了命令行,我们也经常需要在代码中对属性进行获取和设定,Android为此提供了快捷的API,包含C/C++与Java,如下示例:

//C++ binding,system/core/libcutils/include/cutils/properties.h
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);

//Java binding,frameworks/base/core/java/android/os/SystemProperties.java
public static String get(@NonNull String key) {
        if (TRACK_KEY_ACCESS) onKeyAccess(key);
        return native_get(key);
}

public static String get(@NonNull String key, @Nullable String def) {
        if (TRACK_KEY_ACCESS) onKeyAccess(key);
        return native_get(key, def);
}

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

//属性变化添加回调
public static void addChangeCallback(@NonNull Runnable callback) {
        synchronized (sChangeCallbacks) {
            if (sChangeCallbacks.size() == 0) {
                native_add_change_callback();
            }
            sChangeCallbacks.add(callback);
        }
}

配合RC使用

有些时候我们需要在rc中通过属性值的变化来启动一些程序,这时候就需要在rc中设定相应的Actions来达成这一目的。这里给出一个示例:

on property:persist.automotive.evs.mode=0
    # stop EVS and automotive display services
    stop automotive_display
    stop evs_sample_driver
    stop evs_manager
    stop evs_app

在该示例中,当属性persist.automotive.evs.mode值为0时,即停止service:automotive_display、evs_sample_driver、evs_manager、evs_app。

Happy
Happy
100 %
Sad
Sad
0 %
Excited
Excited
0 %
Sleepy
Sleepy
0 %
Angry
Angry
0 %
Surprise
Surprise
0 %
FranzKafka95
FranzKafka95

极客,文学爱好者。如果你也喜欢我,那你大可不必害羞。

Articles: 86

Leave a Reply

Your email address will not be published. Required fields are marked *

en_USEN