Android 系统属性(SystemProperties)介绍
我们在开发过程中有时需要使用系统属性,例如获取系统软件版本,获取设备名名称等;有时也需要设置自己的属性,为了全面的介绍系统属性,本文将基于Android 10(Q)来介绍Android的属性使用,围绕以下几点问题展开介绍:
- prop的作用,如何调试和使用?
- prop实现原理,代码流程,prop存储在哪里,何时init?
- 如何添加自定义prop?
系统属性简单来说是用来存储系统中某些键值对数据,具有全局性、存取灵活方便的特点。
一、终端prop命令
在终端设备中,可以通过以下命令进行prop调试。
1.1、查看prop
#查看系统所有props
$getprop
...
[persist.sys.timezone]: [Asia/Shanghai] //时区
[ro.system.build.type]: [userdebug] //系统编译类型
[vendor.display.lcd_density]: [320] //屏幕密度
...
#获取时区属性persist.sys.timezone的值
$getprop persist.sys.timezone
Asia/Shanghai
#过滤属性名或属性值含有关键字"timezone"的属性
$getprop | grep timezone
[persist.sys.timezone]: [Asia/Shanghai]
#获取不存在的属性时结果为空
$getprop hello
1.2、设置prop
$getprop my.prop.test //属性my.prop.test为空
$setprop my.prop.test 123 //设置属性my.prop.test为123
$getprop my.prop.test //获取属性my.prop.test为123
123
setprop 可以给属性设置int、bool、string等基本类型
1.3、监听prop
显示属性值发生变化的值
$watchprops
[my.prop.test]: [123456]
二、get和set prop代码流程
2.1、get和set prop代码流程图
涉及的代码路径汇总如下:
frameworksasecorejavaandroidosSystemProperties.java
frameworksasecorejniandroid_os_SystemProperties.cpp
systemcoreaseproperties.cpp
systemcoreinitmain.cpp
systemcoreinitinit.cpp
systemcoreinitproperty_service.cpp
systemcoreproperty_servicelibpropertyinfoparserproperty_info_parser.cpp
bioniclibcincludesys\_system_properties.h
bioniclibcincludesyssystem_properties.h
bioniclibcionicsystem_property_set.cpp
bioniclibcionicsystem_property_api.cpp
bioniclibcsystem_propertiescontexts_serialized.cpp
bioniclibcsystem_propertiessystem_properties.cpp
bioniclibcsystem_propertiesprop_area.cpp
代码流程整体时序图如下:
系统属性架构设计如下:
2.2、代码流程介绍
frameworksasecorejavaandroidosSystemProperties.java
Systemproperties类在android.os包下,因此使用时需要
import android.os.Systemproperties;
SystemProperties提供了setprop和多种返回数据类型的getprop,采用键值对(key-value)的数据格式进行操作,具体如下:
public class SystemProperties {
...
public static final int PROP_VALUE_MAX = 91;
@UnsupportedAppUsage
private static native String native_get(String key);
private static native String native_get(String key, String def);
private static native int native_get_int(String key, int def);
@UnsupportedAppUsage
private static native long native_get_long(String key, long def);
private static native boolean native_get_boolean(String key, boolean def);
private static native void native_set(String key, String def);
private static native void native_add_change_callback();
private static native void native_report_sysprop_change();
...
//获取属性key的值,如果没有该属性则返回默认值def
public static String get(@NonNull String key, @Nullable String def) {
if (TRACK_KEY_ACCESS) onKeyAccess(key);
return native_get(key, def);
}
...
//设置属性key的值为val,其不可为空、不能是"ro."开头的只读属性
//长度不能超过PROP_VALUE_MAX(91)
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);
}
}
通过JNI上述 native_set()和native_get()走到
frameworksasecorejniandroid_os_SystemProperties.cpp
//获取属性的方法最终调用GetProperty()
jstring SystemProperties_getSS(JNIEnv *env, jclass clazz, jstring keyJ,
jstring defJ)
{
auto handler = [&](const std::string& key, jstring defJ) {
std::string prop_val = android::base::GetProperty(key, "");
...
};
return ConvertKeyAndForward(env, keyJ, defJ, handler);
}
jstring SystemProperties_getS(JNIEnv *env, jclass clazz, jstring keyJ)
{
return SystemProperties_getSS(env, clazz, keyJ, nullptr);
}
//设置属性的接口最终调用SetProperty()
void SystemProperties_set(JNIEnv *env, jobject clazz, jstring keyJ,
jstring valJ)
{
auto handler = [&](const std::string& key, bool) {
...
return android::base::SetProperty(key, val);
};
...
}
...
//关联SystemProperties.java与android_os_SystemProperties.cpp的方法
int register_android_os_SystemProperties(JNIEnv *env)
{
const JNINativeMethod method_table[] = {
{ "native_get", "(Ljava/lang/String;)Ljava/lang/String;",
(void*) SystemProperties_getS },
{ "native_get",
"(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
(void*) SystemProperties_getSS },
{ "native_set", "(Ljava/lang/String;Ljava/lang/String;)V",
(void*) SystemProperties_set },
...
};
return RegisterMethodsOrDie(env, "android/os/SystemProperties",
method_table, NELEM(method_table));
}
跟着调用关系,接着进入:
systemcoreaseproperties.cpp
//调用__system_property_find()查找属性值
std::string GetProperty(const std::string& key, const std::string& default_value) {
...
const prop_info* pi = __system_property_find(key.c_str());
...
}
//调用__system_property_set()设置属性值
bool SetProperty(const std::string& key, const std::string& value) {
return (__system_property_set(key.c_str(), value.c_str()) == 0);
}
观察流程图会发现这里会出现“岔路”,查询和设置属性的接口暂时走向不同文件接口
2.2.1、set prop流程
_system_properties.h头文件定义PROP_SERVICE_NAME
bioniclibcincludesys\_system_properties.h
#define PROP_SERVICE_NAME "property_service"
bioniclibcionicsystem_property_set.cpp
static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;
static const char* kServiceVersionPropertyName = "ro.property_service.version";
...
int __system_property_set(const char* key, const char* value) {
if (g_propservice_protocol_version == 0) {
detect_protocol_version();//获取属性服务协议版本
}
//旧协议版本限定prop name最大长度为PROP_NAME_MAX,prop val最大长度为PROP_VALUE_MAX
if (g_propservice_protocol_version == kProtocolVersion1) {
//在bioniclibcincludesyssystem_properties.h中定义
//#define PROP_NAME_MAX 32
//#define PROP_VALUE_MAX 92
if (strlen(key) >= PROP_NAME_MAX) return -1;
if (strlen(value) >= PROP_VALUE_MAX) return -1;
...
return send_prop_msg(&msg);//send_prop_msg()也通过Socket与property_service进行通信
} else {//进入新版本协议,仅对prop val长度有要求,不超过92,且属性不为只读属性
// New protocol only allows long values for ro. properties only.
if (strlen(value) >= PROP_VALUE_MAX && strncmp(key, "ro.", 3) != 0) return -1;
...
SocketWriter writer(&connection);//通过Socket与property_service进行通信
...
}
...
static const char* kServiceVersionPropertyName = "ro.property_service.version";
static constexpr uint32_t kProtocolVersion1 = 1;
static constexpr uint32_t kProtocolVersion2 = 2; // current
static atomic_uint_least32_t g_propservice_protocol_version = 0;
static void detect_protocol_version() {
//从ro.property_service.version中获取协议版本,可在平台终端getprop ro.property_service.version
//在后续2.2.5小节中可找到设置该属性的位置
if (__system_property_get(kServiceVersionPropertyName, value) == 0) {
g_propservice_protocol_version = kProtocolVersion1;
} else {
uint32_t version = static_cast<uint32_t>(atoll(value));
if (version >= kProtocolVersion2) {
g_propservice_protocol_version = kProtocolVersion2;
} else {
g_propservice_protocol_version = kProtocolVersion1;
}
...
}
2.2.2、属性服务(property_service)
流程到了这里会发现,最终需要通过Socket与property_service来通信进行设置属性值,那么问题来了:
- property_service是谁创建的?
- property_service是怎么启动的?
- property_service是如何setprop的?
熟悉Android开机流程的同学会马上联想到init进程,property_service正是init进程来初始化和启动的。
systemcoreinitinit.cpp
int SecondStageMain(int argc, char** argv) {
...
property_init();//属性初始化
...
property_load_boot_defaults(load_debug_prop);//加载开机默认属性配置
StartPropertyService(&epoll);//启动属性服务
...
}
2.2.3、属性初始化
systemcoreinitproperty_service.cpp
void property_init() {
mkdir("/dev/__properties__", S_IRWXU | S_IXGRP | S_IXOTH);
CreateSerializedPropertyInfo();//创建属性信息property_info
if (__system_property_area_init()) {//创建共享内存
LOG(FATAL) << "Failed to initialize property area";
}
if (!property_info_area.LoadDefaultPath()) {
LOG(FATAL) << "Failed to load serialized property info file";
}
}
...
...
//读取selinux模块中的property相关文件,解析并加载到property_info中
void CreateSerializedPropertyInfo() {
auto property_infos = std::vector<PropertyInfoEntry>();
if (access("/system/etc/selinux/plat_property_contexts", R_OK) != -1) {
if (!LoadPropertyInfoFromFile("/system/etc/selinux/plat_property_contexts",
&property_infos)) {
return;
}
// Don't check for failure here, so we always have a sane list of properties.
// E.g. In case of recovery, the vendor partition will not have mounted and we
// still need the system / platform properties to function.
if (!LoadPropertyInfoFromFile("/vendor/etc/selinux/vendor_property_contexts",
&property_infos)) {
// Fallback to nonplat_* if vendor_* doesn't exist.
LoadPropertyInfoFromFile("/vendor/etc/selinux/nonplat_property_contexts",
&property_infos);
}
if (access("/product/etc/selinux/product_property_contexts", R_OK) != -1) {
LoadPropertyInfoFromFile("/product/etc/selinux/product_property_contexts",
&property_infos);
}
if (access("/odm/etc/selinux/odm_property_contexts", R_OK) != -1) {
LoadPropertyInfoFromFile("/odm/etc/selinux/odm_property_contexts", &property_infos);
}
} else {
if (!LoadPropertyInfoFromFile("/plat_property_contexts", &property_infos)) {
return;
}
if (!LoadPropertyInfoFromFile("/vendor_property_contexts", &property_infos)) {
// Fallback to nonplat_* if vendor_* doesn't exist.
LoadPropertyInfoFromFile("/nonplat_property_contexts", &property_infos);
}
LoadPropertyInfoFromFile("/product_property_contexts", &property_infos);
LoadPropertyInfoFromFile("/odm_property_contexts", &property_infos);
}
auto serialized_contexts = std::string();
auto error = std::string();
if (!BuildTrie(property_infos, "u:object_r:default_prop:s0", "string", &serialized_contexts,
&error)) {
LOG(ERROR) << "Unable to serialize property contexts: " << error;
return;
}
//将序列化属性写入/dev/__properties__/property_info
constexpr static const char kPropertyInfosPath[] = "/dev/__properties__/property_info";
if (!WriteStringToFile(serialized_contexts, kPropertyInfosPath, 0444, 0, 0, false)) {
PLOG(ERROR) << "Unable to write serialized property infos to file";
}
selinux_android_restorecon(kPropertyInfosPath, 0);
}
bioniclibcionicsystem_property_api.cpp
int __system_property_area_init() {
bool fsetxattr_failed = false;
return system_properties.AreaInit(PROP_FILENAME, &fsetxattr_failed) && !fsetxattr_failed ? 0 : -1;
}
初始化属性内存共享区域
bioniclibcsystem_propertiessystem_properties.cpp
bool SystemProperties::AreaInit(const char* filename, bool* fsetxattr_failed) {
if (strlen(filename) >= PROP_FILENAME_MAX) {
return false;
}
strcpy(property_filename_, filename);
contexts_ = new (contexts_data_) ContextsSerialized();
if (!contexts_->Initialize(true, property_filename_, fsetxattr_failed)) {
return false;
}
initialized_ = true;
return true;
}
bioniclibcsystem_propertiescontexts_serialized.cpp
bool ContextsSerialized::Initialize(bool writable, const char* filename, bool* fsetxattr_failed) {
filename_ = filename;
if (!InitializeProperties()) {
return false;
}
...
}
bool ContextsSerialized::InitializeProperties() {
if (!property_info_area_file_.LoadDefaultPath()) {
return false;
}
...
}
systemcoreproperty_servicelibpropertyinfoparserproperty_info_parser.cpp
bool PropertyInfoAreaFile::LoadDefaultPath() {
return LoadPath("/dev/__properties__/property_info");
}
bool PropertyInfoAreaFile::LoadPath(const char* filename) {
int fd = open(filename, O_CLOEXEC | O_NOFOLLOW | O_RDONLY);
...
void* map_result = mmap(nullptr, mmap_size, PROT_READ, MAP_SHARED, fd, 0);
...
}
__system_property_area_init()经过一系列的方法调用,最终通过mmap()将/dev/__property __/property_info加载到共享内存。
2.2.4、加载开机默认属性配置
接着init进程中property_load_boot_defaults(load_debug_prop)函数分析
systemcoreinitproperty_service.cpp
void property_load_boot_defaults(bool load_debug_prop) {
// TODO(b/117892318): merge prop.default and build.prop files into one
// We read the properties and their values into a map, in order to always allow properties
// loaded in the later property files to override the properties in loaded in the earlier
// property files, regardless of if they are "ro." properties or not.
std::map<std::string, std::string> properties;
//加载属性build.prop、default.prop至properties
if (!load_properties_from_file("/system/etc/prop.default", nullptr, &properties)) {
// Try recovery path
if (!load_properties_from_file("/prop.default", nullptr, &properties)) {
// Try legacy path
load_properties_from_file("/default.prop", nullptr, &properties);
}
}
load_properties_from_file("/system/build.prop", nullptr, &properties);
load_properties_from_file("/vendor/default.prop", nullptr, &properties);
load_properties_from_file("/vendor/build.prop", nullptr, &properties);
if (SelinuxGetVendorAndroidVersion() >= __ANDROID_API_Q__) {
load_properties_from_file("/odm/etc/build.prop", nullptr, &properties);
} else {
load_properties_from_file("/odm/default.prop", nullptr, &properties);
load_properties_from_file("/odm/build.prop", nullptr, &properties);
}
load_properties_from_file("/product/build.prop", nullptr, &properties);
load_properties_from_file("/product_services/build.prop", nullptr, &properties);
load_properties_from_file("/factory/factory.prop", "ro.*", &properties);
...
//将properties中属性通过PropertySet存入属性属性
for (const auto& [name, value] : properties) {
std::string error;
if (PropertySet(name, value, &error) != PROP_SUCCESS) {
LOG(ERROR) << "Could not set '" << name << "' to '" << value
<< "' while loading .prop files" << error;
}
}
property_initialize_ro_product_props();//初始化ro_product前缀的只读属性
property_derive_build_props();//初始化编译相关属性
update_sys_usb_config();//设置persist.sys.usb.config属性,用于控制USB调试和文件传输功能
}
从上面代码可以看出从多个属性文件.prop中系统属性加载至properties变量,再通过PropertySet()将properties添加到系统中,并初始化只读、编译、usb相关属性值。
PropertySet()流程在2.2.1小节中有介绍,通过Socket与属性服务进行通信,具体过程即将揭晓。
2.2.5、启动属性服务
2.2.2小节中提到init进程StartPropertyService(&epoll)启动了属性服务。
systemcoreinitproperty_service.cpp
void StartPropertyService(Epoll* epoll) {
selinux_callback cb;
cb.func_audit = SelinuxAuditCallback;
selinux_set_callback(SELINUX_CB_AUDIT, cb);
//这里设置了2.2.1小节提到的属性ro.property_service.version
property_set("ro.property_service.version", "2");
property_set_fd = CreateSocket(PROP_SERVICE_NAME, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK,
false, 0666, 0, 0, nullptr);
if (property_set_fd == -1) {
PLOG(FATAL) << "start_property_service socket creation failed";
}
listen(property_set_fd, 8);//设置Socket连接数为8
//注册epoll,监听property_set_fd改变时调用handle_property_set_fd
if (auto result = epoll->RegisterHandler(property_set_fd, handle_property_set_fd); !result) {
PLOG(FATAL) << result.error();
}
}
static void handle_property_set_fd() {
...
switch (cmd) {
case PROP_MSG_SETPROP: {//设置属性
...
uint32_t result =
HandlePropertySet(prop_name, prop_value, socket.source_context(), cr, &error);
...
}
case PROP_MSG_SETPROP2: {
...
uint32_t result = HandlePropertySet(name, value, socket.source_context(), cr, &error);
...
}
...
}
uint32_t HandlePropertySet(const std::string& name, const std::string& value,
const std::string& source_context, const ucred& cr, std::string* error) {
if (auto ret = CheckPermissions(name, value, source_context, cr, error); ret != PROP_SUCCESS) {
return ret;
}
if (StartsWith(name, "ctl.")) {//ctl属性:ctl.start启动服务,ctl.stop关闭服务
HandleControlMessage(name.c_str() + 4, value, cr.pid);
return PROP_SUCCESS;
}
// sys.powerctl is a special property that is used to make the device reboot. We want to log
// any process that sets this property to be able to accurately blame the cause of a shutdown.
if (name == "sys.powerctl") {//sys.powerctl属性可控制设备重启
std::string cmdline_path = StringPrintf("proc/%d/cmdline", cr.pid);
std::string process_cmdline;
std::string process_log_string;
if (ReadFileToString(cmdline_path, &process_cmdline)) {
// Since cmdline is null deliminated, .c_str() conveniently gives us just the process
// path.
process_log_string = StringPrintf(" (%s)", process_cmdline.c_str());
}
LOG(INFO) << "Received sys.powerctl='" << value << "' from pid: " << cr.pid
<< process_log_string;
}
if (name == "selinux.restorecon_recursive") {
return PropertySetAsync(name, value, RestoreconRecursiveAsync, error);
}
return PropertySet(name, value, error);//设置属性
}
...
static uint32_t PropertySet(const std::string& name, const std::string& value, std::string* error) {
size_t valuelen = value.size();
if (!IsLegalPropertyName(name)) {//检测属性合法性
*error = "Illegal property name";
return PROP_ERROR_INVALID_NAME;
}
if (valuelen >= PROP_VALUE_MAX && !StartsWith(name, "ro.")) {
*error = "Property value too long";
return PROP_ERROR_INVALID_VALUE;
}
if (mbstowcs(nullptr, value.data(), 0) == static_cast<std::size_t>(-1)) {
*error = "Value is not a UTF8 encoded string";
return PROP_ERROR_INVALID_VALUE;
}
//检测属性是否已存在
prop_info* pi = (prop_info*) __system_property_find(name.c_str());
if (pi != nullptr) {
// ro.* properties are actually "write-once".
if (StartsWith(name, "ro.")) {
*error = "Read-only property was already set";
return PROP_ERROR_READ_ONLY_PROPERTY;
}
//属性已存在,并且非ro只读属性,更新属性值
__system_property_update(pi, value.c_str(), valuelen);
} else {
//属性不存在,添加属性值
int rc = __system_property_add(name.c_str(), name.size(), value.c_str(), valuelen);
if (rc < 0) {
*error = "__system_property_add failed";
return PROP_ERROR_SET_FAILED;
}
}
// Don't write properties to disk until after we have read all default
// properties to prevent them from being overwritten by default values.
if (persistent_properties_loaded && StartsWith(name, "persist.")) {
WritePersistentProperty(name, value);//将persist属性持久化到disk
}
property_changed(name, value);//特殊属性值(如sys.powerctl)改变后系统需要立即处理。
return PROP_SUCCESS;
}
2.2.6、更新或添加属性
bioniclibcionicsystem_property_api.cpp
int __system_property_update(prop_info* pi, const char* value, unsigned int len) {
return system_properties.Update(pi, value, len);//更新属性值
}
int __system_property_add(const char* name, unsigned int namelen, const char* value,
unsigned int valuelen) {
return system_properties.Add(name, namelen, value, valuelen);//添加属性值
}
bioniclibcsystem_propertiessystem_properties.cpp
int SystemProperties::Update(prop_info* pi, const char* value, unsigned int len) {
...
prop_area* pa = contexts_->GetSerialPropArea();
uint32_t serial = atomic_load_explicit(&pi->serial, memory_order_relaxed);
serial |= 1;
atomic_store_explicit(&pi->serial, serial, memory_order_relaxed);
atomic_thread_fence(memory_order_release);
strlcpy(pi->value, value, len + 1);//属性值更新
...
return 0;
}
int SystemProperties::Add(const char* name, unsigned int namelen, const char* value,
unsigned int valuelen) {
...
prop_area* serial_pa = contexts_->GetSerialPropArea();
prop_area* pa = contexts_->GetPropAreaForName(name);
bool ret = pa->add(name, namelen, value, valuelen);//向共享内存添加新属性
...
return 0;
}
bioniclibcsystem_propertiesprop_area.cpp
bool prop_area::add(const char* name, unsigned int namelen, const char* value,
unsigned int valuelen) {
return find_property(root_node(), name, namelen, value, valuelen, true);
}
...
const prop_info* prop_area::find_property(prop_bt* const trie, const char* name, uint32_t namelen,
const char* value, uint32_t valuelen,
bool alloc_if_needed) {
...
prop_bt* root = nullptr;
uint_least32_t children_offset = atomic_load_explicit(¤t->children, memory_order_relaxed);
if (children_offset != 0) {//找到属性节点
root = to_prop_bt(¤t->children);
} else if (alloc_if_needed) {//未找到时新建节点
uint_least32_t new_offset;
root = new_prop_bt(remaining_name, substr_size, &new_offset);
if (root) {
atomic_store_explicit(¤t->children, new_offset, memory_order_release);
}
}
...
current = find_prop_bt(root, remaining_name, substr_size, alloc_if_needed);
remaining_name = sep + 1;
}
uint_least32_t prop_offset = atomic_load_explicit(¤t->prop, memory_order_relaxed);
if (prop_offset != 0) {
return to_prop_info(¤t->prop);//返回已存在的prop_info
} else if (alloc_if_needed) {
uint_least32_t new_offset;
//添加新属性
prop_info* new_info = new_prop_info(name, namelen, value, valuelen, &new_offset);
...
}
}
prop_info* prop_area::new_prop_info(const char* name, uint32_t namelen, const char* value,
uint32_t valuelen, uint_least32_t* const off) {
...
prop_info* info;
if (valuelen >= PROP_VALUE_MAX) {
uint32_t long_value_offset = 0;
char* long_location = reinterpret_cast<char*>(allocate_obj(valuelen + 1, &long_value_offset));
if (!long_location) return nullptr;
memcpy(long_location, value, valuelen);
long_location[valuelen] = '