前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[Android][Framework]SystemProperties

[Android][Framework]SystemProperties

作者头像
wOw
发布2020-01-20 16:48:04
2K0
发布2020-01-20 16:48:04
举报
文章被收录于专栏:wOw的Android小站wOw的Android小站

属性简介

在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

代码语言:javascript
复制
#define PROP_NAME_MAX	32
#define PROP_VALUE_MAX	92

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

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

代码语言:javascript
复制
error: ro.product.model cannot exceed 91 bytes: xxxxxxxxxxxxx...xxxxxxxxx

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

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

代码语言:javascript
复制
// system/core/init/init.cpp
int main(int argc, char** argv) {
    ...
    if (!is_first_stage) {
        property_init();
    }
    ....
    property_load_boot_defaults();
    ...
    start_property_service();
}

看具体调用

代码语言:javascript
复制
// 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来自于

代码语言:javascript
复制
// 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中会从系统文件中读取默认的属性,并写入共享内存中。相同的属性会被后读入的属性替换。

代码语言:javascript
复制
// 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();
}

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

代码语言:javascript
复制
// 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文件对应的接口:

代码语言:javascript
复制
// frameworks/base/core/jni/AndroidRuntime.cpp
namespace android {
extern int register_android_os_SystemProperties(JNIEnv *env);
}
代码语言:javascript
复制
// 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,相关的定义如下:

代码语言:javascript
复制
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前缀

代码语言:javascript
复制
/* 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文件

代码语言:javascript
复制
#
# 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添加自定义属性

  1. 在buildinfo.sh中添加自定义property
  2. 最简单的就是在system.prop里添加一行,然后编译会将其追加到目标文件的
  3. ADDITIONAL_BUILD_PROPERTIES 是MakeFile的一个声明,也就是在MakeFile中通过ADDITIONAL_BUILD_PROPERTIES += persist.sys.xxxx=1这种方式就可以添加自定义的属性。

和自定属性相关的实例

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

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

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

代码语言:javascript
复制
persist.sys.hotspot.enable=off

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

代码语言:javascript
复制
// 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");
	}
	...
}

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

代码语言:javascript
复制
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

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018-11-14,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 属性简介
  • 属性类型
  • 源码流程
  • system/build.prop
    • system/build.prop文件
      • system/build.prop生成过程
        • 在system/build.prop添加自定义属性
        • 和自定属性相关的实例
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档