转载

[Framework]SystemProperties

在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

原文  http://wossoneri.github.io/2018/11/14/[Android][Framework]SystemProperties/
正文到此结束
Loading...