IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Android12系统之系统属性

    FranzKafka95发表于 2023-09-19 07:40:10
    love 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
    1 100 %
    Sad
    Sad
    0 0 %
    Excited
    Excited
    0 0 %
    Sleepy
    Sleepy
    0 0 %
    Angry
    Angry
    0 0 %
    Surprise
    Surprise
    0 0 %

    The post Android12系统之系统属性 first appeared on FranzKafka Blog.



沪ICP备19023445号-2号
友情链接