[Framework]SystemProperties

栏目: Android · 发布时间: 7年前

内容简介:在Android 系统中,为统一管理系统的属性,设计了一个统一的属性系统。每个属性都有一个名称和值,他们都是字符串格式。属性被大量使用在Android系统中,用来记录系统设置或进程之间的信息交换。属性是在整个系统中全局可见的。每个进程可以get/set属性。在编译的过程中会将各种系统参数汇总到build.prop 以及default.prop 这两个文件中,主要属性集中在build.prop中。系统在开机后将读取配置信息并构建共享缓冲区,加快查询速度。另外一个方面,SettingsProvider会在系统

在Android 系统中,为统一管理系统的属性,设计了一个统一的属性系统。每个属性都有一个名称和值,他们都是字符串格式。属性被大量使用在Android系统中,用来记录系统设置或进程之间的信息交换。属性是在整个系统中全局可见的。每个进程可以get/set属性。在编译的过程中会将各种系统参数汇总到build.prop 以及default.prop 这两个文件中,主要属性集中在build.prop中。

系统在开机后将读取配置信息并构建共享缓冲区,加快查询速度。另外一个方面,SettingsProvider会在系统第一次初始化时(刷机第一次启动)后,将从Defaults.xml中读取数据然后写入数据库Settings.db 目录。并构建一个缓冲系统供其他应用查询。下面将详细讲述。

属性类型

系统属性根据不同的应用类型,分为:

  • 不可变型

    属性名称以“ro.”开头,那么这个属性被视为只读属性。一旦设置,属性值不能改变。

  • 持久型

    属性名称以“persist.”开头,当设置这个属性时,其值也将写入/data/property。

  • 网络型

    属性名称以“net.”开头,当设置这个属性时,“net.change”属性将会自动设置,以加入到最后修改的属性名。(这是很巧妙的。 netresolve模块的使用这个属性来追踪在net.*属性上的任何变化。)

  • 启动和停止服务

    属性“ ctrl.start ”和“ ctrl.stop ”是用来启动和停止服务。每一项服务必须在/init.rc中定义.系统启动时,与init守护进程将解析init.rc和启动属性服务。一旦收到设置“ ctrl.start ”属性的请求,属性服务将使用该属性值作为服务名找到该服务,启动该服务。这项服务的启动结果将会放入“ init.svc.<服务名>“属性中 。客户端应用程序可以轮询那个属性值,以确定结果。

源码流程

首先,关于属性,是有长度定义的:

Bionic/libc/include/sys/system_properties.h

#define PROP_NAME_MAX	32
#define PROP_VALUE_MAX	92

即属性名长度最大32字节,属性值长度最大92字节。

如果把属性值修改超出最大长度,会报错:

error: ro.product.model cannot exceed 91 bytes: xxxxxxxxxxxxx...xxxxxxxxx

在系统初始化过程中,Android系统会分配一块共享内存用来存储properties。这些是由 init 守护进程完成的,其源代码位于: system/core/initinit 守护进程将启动一个属性服务。

属性服务在 init 守护进程中运行。每一个客户端想要设置属性时,必须连接属性服务,再向其发送信息。属性服务将会在共享内存区中修改和创建属性。客户端想获得属性信息,可以从共享内存直接读取。这提高了读取性能。

// system/core/init/init.cpp
int main(int argc, char** argv) {
    ...
    if (!is_first_stage) {
        property_init();
    }
    ....
    property_load_boot_defaults();
    ...
    start_property_service();
}

看具体调用

// system/core/init/property_service.cpp

void property_init() {
    if (__system_property_area_init()) { // 分配内存
        ERROR("Failed to initialize property area\n");
        exit(1);
    }
}

void property_load_boot_defaults() {
    load_properties_from_file(PROP_PATH_RAMDISK_DEFAULT, NULL);
}
/*
 * Filter is used to decide which properties to load: NULL loads all keys,
 * "ro.foo.*" is a prefix match, and "ro.foo.bar" is an exact match.
 */
static void load_properties_from_file(const char* filename, const char* filter) {
    Timer t;
    std::string data;
    if (read_file(filename, &data)) {
        data.push_back('\n');
        load_properties(&data[0], filter);
    }
    NOTICE("(Loading properties from %s took %.2fs.)\n", filename, t.duration());
}

在加载默认属性的时候property_load_boot_defaults,读取的 PROP_PATH_RAMDISK_DEFAULT 来自于

// bionic/libc/include/sys/_system_properties.h
/* 旧版本 */
#define PROP_PATH_RAMDISK_DEFAULT  "/default.prop"
#define PROP_PATH_SYSTEM_BUILD     "/system/build.prop"
#define PROP_PATH_SYSTEM_DEFAULT   "/system/default.prop"
#define PROP_PATH_LOCAL_OVERRIDE   "/data/local.prop"

/* 新版本 N+ */
#define PROP_PATH_RAMDISK_DEFAULT	"/default.prop"
#define PROP_PATH_SYSTEM_BUILD		"/system/build.prop"
#define PROP_PATH_VENDOR_BUILD		"/vendor/build.prop"
#define PROP_PATH_LOCAL_OVERRIDE	"/data/local.prop"
#define PROP_PATH_FACTORY			"/factory/factory.prop"

在builtins.cpp中会从系统文件中读取默认的属性,并写入共享内存中。相同的属性会被后读入的属性替换。

// system/core/init/property_service.cpp
void load_system_props() {
    load_properties_from_file(PROP_PATH_SYSTEM_BUILD, NULL);
    load_properties_from_file(PROP_PATH_VENDOR_BUILD, NULL);
    load_properties_from_file(PROP_PATH_FACTORY, "ro.*");
    load_recovery_id_prop();
}

再看上层如何访问属性的。

// SystemProperties.java 定义了get和set方法
private static native String native_get(String key);
private static native String native_get(String key, String def);
/**
 * Get the value for the given key.
 * @return an empty string if the key isn't found
 * @throws IllegalArgumentException if the key exceeds 32 characters
 */
public static String get(String key) {
    if (key.length() > PROP_NAME_MAX) {
        throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
    }
    return native_get(key);
}

/**
 * Get the value for the given key.
 * @return if the key isn't found, return def if it isn't null, or an empty string otherwise
 * @throws IllegalArgumentException if the key exceeds 32 characters
 */
public static String get(String key, String def) {
    if (key.length() > PROP_NAME_MAX) {
        throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
    }
    return native_get(key, def);
}

该接口类在初始化运行环境中注册对应的cpp接口android_os_SystemProperties.cpp,实际操作通过JNI调用的是cpp文件对应的接口:

// frameworks/base/core/jni/AndroidRuntime.cpp
namespace android {
extern int register_android_os_SystemProperties(JNIEnv *env);
}
// frameworks/base/core/jni/android_os_SystemProperties.cpp
static void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ, jstring valJ)
{
	int err;
	const char* key;
	const char* val;
	key = env->GetStringUTFChars(keyJ, NULL);
	if (valJ == NULL) {
		val = "";       /* NULL pointer not allowed here */
	} else {
		val = env->GetStringUTFChars(valJ, NULL);
	}
	err = property_set(key, val);
	env->ReleaseStringUTFChars(keyJ, key);        
	if (valJ != NULL) {
		env->ReleaseStringUTFChars(valJ, val);
	}
}

设置key的value时,需要作鉴权,根据设置程序所在进程的fd获知uid值,比如system server进程可以设置net打头的key,不可以设置gsm打头的key,相关的定义如下:

system/core/include/private/android_filesystem_config.h
#define AID_ROOT             0  /* traditional unix root user */
#define AID_SYSTEM        1000  /* system server */
#define AID_RADIO         1001  /* telephony subsystem, RIL */
#define AID_DHCP          1014  /* dhcp client */
#define AID_SHELL         2000  /* adb and debug shell user */
#define AID_CACHE         2001  /* cache access */
#define AID_APP          10000 /* first app user */

通过查看property_service.c,我们可以明确以下事实:

1、 属性名不是随意取的。在property_perms数组中定义了当前系统上可用的所有属性的前缀,以及相对应的存取权限UID。对属性的设置要满足权限要求,同时命名也要在这些定义的范围内。

2、 PA_COUNT_MAX指定了系统(共享内存区域中)最多能存储多少个属性。

这一段可以从property_set_impl方法逻辑看property前缀

/* White list of permissions for setting property services. */
struct {
    const char *prefix;
    unsigned int uid;
    unsigned int gid;
} property_perms[] = {
    { "net.rmnet0.",      AID_RADIO,    0 },
    { "net.gprs.",        AID_RADIO,    0 },
    { "net.ppp",          AID_RADIO,    0 },
    { "net.qmi",          AID_RADIO,    0 },
    { "net.lte",          AID_RADIO,    0 },
    { "net.cdma",         AID_RADIO,    0 },
    { "ril.",             AID_RADIO,    0 },
    { "gsm.",             AID_RADIO,    0 },
    { "persist.radio",    AID_RADIO,    0 },
    { "net.dns",          AID_RADIO,    0 },
    { "sys.usb.config",   AID_RADIO,    0 },
    { "net.",             AID_SYSTEM,   0 },
    { "dev.",             AID_SYSTEM,   0 },
    { "runtime.",         AID_SYSTEM,   0 },
    { "hw.",              AID_SYSTEM,   0 },
    { "sys.",             AID_SYSTEM,   0 },
    { "sys.powerctl",     AID_SHELL,    0 },
    { "service.",         AID_SYSTEM,   0 },
    { "wlan.",            AID_SYSTEM,   0 },
    { "gps.",             AID_GPS,      0 },
    { "bluetooth.",       AID_BLUETOOTH,   0 },
    { "dhcp.",            AID_SYSTEM,   0 },
    { "dhcp.",            AID_DHCP,     0 },
    { "debug.",           AID_SYSTEM,   0 },
    { "debug.",           AID_SHELL,    0 },
    { "log.",             AID_SHELL,    0 },
    { "service.adb.root", AID_SHELL,    0 },
    { "service.adb.tcp.port", AID_SHELL,    0 },
    { "persist.logd.size",AID_SYSTEM,   0 },
    { "persist.sys.",     AID_SYSTEM,   0 },
    { "persist.service.", AID_SYSTEM,   0 },
    { "persist.security.", AID_SYSTEM,   0 },
    { "persist.gps.",      AID_GPS,      0 },
    { "persist.service.bdroid.", AID_BLUETOOTH,   0 },
    { "selinux."         , AID_SYSTEM,   0 },
    { "wc_transport.",     AID_BLUETOOTH,   AID_SYSTEM },
    { "build.fingerprint", AID_SYSTEM,   0 },
    { "partition."        , AID_SYSTEM,   0},
#ifdef DOLBY_UDC
    { "dolby.audio",      AID_MEDIA,    0 },
#endif // DOLBY_UDC
#ifdef DOLBY_DAP
    // used for setting Dolby specific properties
    { "dolby.", AID_SYSTEM,   0 },
#endif // DOLBY_DAP
    { "sys.audio.init",   AID_MEDIA,    0 },
    { NULL, 0, 0 }
};

在开机启动后的init操作中,会执行一个loop循环,当检测到有新的设置时,进入设置流程,鉴权失败会提示相关的异常,如sys_prop: permission denied uid:1000 name:gsm.phone.id

system/build.prop

system/build.prop文件

#
# ADDITIONAL_BUILD_PROPERTIES
#
...
dalvik.vm.heapminfree=6m
dalvik.vm.heapstartsize=14m
dalvik.vm.heapgrowthlimit=192m
dalvik.vm.heapsize=512m
dalvik.vm.heaptargetutilization=0.75
dalvik.vm.heapmaxfree=8m
...

import /system/vendor/vendor.prop

#IMPORT REGIONALIZATION VENDOR PROP PATH LAST IN ORDER TO OVERRIDE PROPERTIES#
import /persist/speccfg/vendor_persist.prop

import /system/vendor/default.prop

import /system/vendor/power.prop

system/build.prop生成过程

从build.prop输出,从注释内容可以看到:

  1. 执行build/tools/buildinfo.sh
  2. 把device/qcom/msmxxxx/system.prop的内容拷贝到$(OUT_TARGET_DEVICE)/system/build.prop
  3. 将ADDITIONAL_BUILD_PROPERTIES也添加到$(OUT_TARGET_DEVICE)/system/build.prop

在system/build.prop添加自定义属性

ADDITIONAL_BUILD_PROPERTIES += persist.sys.xxxx=1

和自定属性相关的实例

可以使用System Properties记录用户习惯。

比如,我的设备需要提供Wifi热点功能,当用户主动打开热点后,需要用一个属性记录用户习惯,当设备关机重启后,根据该属性自动打开热点。

所以首先创建一个persist属性,写在 /device/平台/型号/system.prop 文件最后。

persist.sys.hotspot.enable=off

然后在手动开关热点的时候,记录用户的操作到该属性中:

// ConnectivityManager.java
@SystemApi
public void startTethering(int type, boolean showProvisioningUi,
		final OnStartTetheringCallback callback, Handler handler) {
    ...
    if (type == ConnectivityManager.TETHERING_WIFI) {
        SystemProperties.set("persist.sys.hotspot.enable", "on");
    }
    ...
}

@SystemApi
public void stopTethering(int type) {
	...
	if (type == ConnectivityManager.TETHERING_WIFI) {
		SystemProperties.set("persist.sys.hotspot.enable", "off");
	}
	...
}

最后在开机的时候根据记录的用户习惯,自动打开热点:

private void startWifiTether() {
    String state = SystemProperties.get("persist.sys.hotspot.enable", "off");
    if (TextUtils.equals(state, "on")) {
        WifiManager wifimanager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
        wifimanager.setWifiEnabled(false); // disable wifi when using wifi hotspot
        wifimanager.setWifiApEnabled(null, true);
    }
}

Ref

https://www.cnblogs.com/l2rf/p/6610348.html

https://www.cnblogs.com/Peter-Chen/p/3946129.html

https://blog.csdn.net/ameyume/article/details/8056492


以上所述就是小编给大家介绍的《[Framework]SystemProperties》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Pro JavaScript Techniques

Pro JavaScript Techniques

John Resig / Apress / 2006-12-13 / USD 44.99

Pro JavaScript Techniques is the ultimate JavaScript book for the modern web developer. It provides everything you need to know about modern JavaScript, and shows what JavaScript can do for your web s......一起来看看 《Pro JavaScript Techniques》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

MD5 加密
MD5 加密

MD5 加密工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换