一、系统属性值
(1)系统属性定义了Android 系统的一些公共系统属性,都是字符串格式,可用于业务代码中区分不同机型、系统版本等
业务代码中可读取属性值后进行特定业务逻辑,比如区分手机或者平板设备等,/system/build.prop
文件能看到部分系统属性,可通过adb shell getprop
查看全部系统属性,相关命令如下:
adb shell getprop vendor.xxx.xxxadb
查看特定属性值
adb shell setprop [key] [value]
设置指定key的属性值
watchprops
命令可监听系统属性的变化
这三个命令都是toolbox的子命令,对应的源码:system/core/toolbox/
部分属性举例:
getprop ro.serialno 序列号
getprop ro.carrier CID号
getprop ro.hardware 板子代号
getprop ro.bootloader SPL(Hboot)版本号
getprop ro.build.version.sdk Android版本号
getprop ro.build.characteristics 如果是平板属性值为tablet,可用于区分手机和平板
ps.只有有权限的进程才能修改属性
系统属性的四个优点,当然缺点也很明显,只能支持三种基本类型:string、int、boolean
1)全局:只要拥有对应的权限,就可以同步获取和修改;
2)通用:在Java层,native层,shell层都可以获取和修改;
3)初始化早:属性服务实在 init 进程中启动的;
4)简单:主要就两个方法 set 和 get
(2)主要属性简介:
- ro开头的是只读属性,一旦赋值不能修改
- persist.开头以文件的形式保存在/data/property路径下,persist.属性由于将其保存在了用户空间中,所以在property_init中是不能对其更新的,只能将其更新过程交给用户来处理
- ctl. 开头虽然是以属性的形式来进行设置,其实它的目的是为了启动或关闭它指定的service 属性“ ctrl.start ”和“ ctrl.stop ”是用来启动和停止服务。每一项服务必须在/init.rc中定义.系统启动时,init守护进程将解析init.rc和启动属性服务。一旦收到设置“ ctrl.start ”属性的请求,属性服务将使用该属性值作为服务名找到该服务,启动该服务。这项服务的启动结果将会放入“ init.svc.<服务名>“属性中 。客户端应用程序可以轮询那个属性值,以确定结果
- gsm开头的是移动电话的一些版本信息(软硬件,基带版本等信息)、所在国家、漫游状态、序列号等信息等一系列与sim相关的信息
- sys开头的是系统信息
- init.svc手机当前进程的状态(启动的显示running未启动的显示stopped),进程是否启动由init.rc文件定义
- net开头的是与网络相关的信息,比如dns、主机名、为各功能预留的缓冲区大小
- dhcp.wlan 是网关、IP地址等无线网络相关信息;
- external_sd_path 是外部存储SD卡的路径
- fmradio.driver 是FMradio相关属性
(3)Java反射读取系统属性
private static final String CLASS_SYSTEM_PROPERTIES = "android.os.SystemProperties";
/**
* 获取get prop 字符串类型配置值(SystemProperties读取的配置)
*
* @param cfgName 配置名
* @param defValue 获取不到的默认值
* @return 成功返回实际的值,识别返回默认值
*/
public static String getStringOfRoConfig(String cfgName, String defValue) {
Object result = invoke(CLASS_SYSTEM_PROPERTIES, "get", new Class<?>[] {String.class, String.class},
cfgName, defValue);
if (result instanceof String) {
return (String) result;
}
return defValue;
}
/**
* 反射调用指定类中的指定方法
*
* @param className 方法所在的类名
* @param methodName 方法名
* @param parameterTypes 可变参数class类型数组,注意args要和其一致
* @param args 方法可变参数
* @return 方法调用返回的结果
*/
public static Object invoke(String className, String methodName, Class<?>[] parameterTypes, Object... args) {
Object value = null;
try {
Class<?> clz = Class.forName(className);
Method method = clz.getDeclaredMethod(methodName, parameterTypes);
value = method.invoke(clz, args);
} catch (NoSuchMethodException | IllegalAccessException | IllegalArgumentException | InvocationTargetException
| SecurityException | ClassNotFoundException e) {
Logger.e(TAG, "invoke " + e.getClass().getSimpleName());
} catch (Exception e) {
Logger.e(TAG, "unknown Exception in invoke");
}
return value;
}
(4)Native层C++读取属性值
NDKndk\21.0.6113669\toolchains\llvm\prebuilt\windows-x86_64\sysroot\usr\include\bits
路径下get_device_api_level_inlines.h
代码
// Avoid circular dependencies since this is exposed from <sys/cdefs.h>.
int __system_property_get(const char* __name, char* __value);
int atoi(const char* __s) __attribute_pure__;
__BIONIC_GET_DEVICE_API_LEVEL_INLINE int android_get_device_api_level() {
char value[92] = { 0 };
if (__system_property_get("ro.build.version.sdk", value) < 1) return -1;
int api_level = atoi(value);
return (api_level > 0) ? api_level : -1;
}
故可通过__system_property_get
函数读取系统属性,同时还提供了获取android版本的属性例子方法android_get_device_api_level
这里char value[92]设置为92是由于属性key的最大字符长度为31,value最大字符长度为91,可看到源码中有如下限制:
public static final int PROP_NAME_MAX = 31;
public static final int PROP_VALUE_MAX = 91;
......
public static void set(String key, String val) {
if (key.length() > PROP_NAME_MAX) {
throw new IllegalArgumentException("key.length > " + PROP_NAME_MAX);
}
if (val != null && val.length() > PROP_VALUE_MAX) {
throw new IllegalArgumentException("val.length > " +
PROP_VALUE_MAX);
}
native_set(key, val); // 调用Native方法设置属性
}
二、Android设置项开关值
(1)设置App中的各个开关值,存储在/data/system/users/0/下的三个文件中,这些文件在手机中有被加密,具体路径如下:
/data/system/users/0/settings_global.xml
/data/system/users/0/settings_secure.xml
/data/system/users/0/settings_system.xml
业务中可读取或者监听开关变化进行自身业务逻辑,App有写设置的权限可往其中写开关值。比如业务可以自定义一个开关值判断,当开关通过adb打开时,执行一些测试逻辑。
adb shell settings put global intelliability_enabled 1
比如通过adb打开intelliability_enabled开关,开关在global文件中
adb shell settings get global intelliability_enabled
通过adb查看开关值
settings_global.xml文件中部分设置项示例如下:
<setting id="89" name="adb_wifi_enabled" value="0" package="android" defaultValue="0" defaultSysSet="true" />
<setting id="40" name="low_battery_sound_timeout" value="0" package="android" defaultValue="0" defaultSysSet="true" />
<setting id="32" name="car_undock_sound" value="/system/media/audio/ui/Undock.ogg" package="android" defaultValue="/system/media/audio/ui/Undock.ogg" defaultSysSet="true" />
<setting id="152" name="keyguard_occluded_notify" value="keyguard_no_occluded" package="com.android.systemui" defaultValue="keyguard_no_occluded" defaultSysSet="true" />
<setting id="169" name="captive_portal_notification_shown" value="0" package="android" defaultValue="0" defaultSysSet="true" />
<setting id="157" name="aps_display_resolution" value="2" package="com.huawei.android.hwaps" defaultValue="2" defaultSysSet="true" />
通过Java读取设置项的值,需要先知道设置项的名称和所在的文件
Settings.System.getInt(context.getContentResolver(), xxSettingName, 0); // System表示存在于settings_system.xml中的开关
Settings.Global.getInt(context.getContentResolver(), xxSettingName, 0);
Settings.Secure.getInt(context.getContentResolver(), xxSettingName, 0);
ps.有些应用或以SDK的形式附加在其它使用此SDK的应用,获取到修改设置的权限后可能会写唯一标识到设置项文件中,使得用户设备被唯一标识,因为其它APP也能读取到这个值,故能用于精准广告推荐。
还有些设置开关是广播,比如位置信息开关,广播Aciton是 LocationManager.MODE_CHANGED_ACTION
收到广播后再根据LocationManager查询是否打开
LocationManager lm = (LocationManager) context.getSystemService(Service.LOCATION_SERVICE);
boolean isEnabled = lm.isLocationEnabled();
(2)监听设置项开关值变化
根据上述,通过文件名+字段名方式构造URI,注册设置变化监听
content://settings/设置文件/文件中的具体设置值
Uri uri = Uri.parse("content://settings/secure/ui_night_mode"); // 监听深色模式开关
ContentObserver contentObserver = new ContentObserver(null) {
@Override
public void onChange(boolean isSelfChange, Uri uri) {
if (uri == null) {
Logger.e(TAG, "setting item uri is null");
return;
}
Logger.d(TAG, "onChange uri = " + uri);
// 可通过uri.toString()字符串中解析文件名和字段名,然后Settings.System.getInt()查询变化后的开关值
}
};
context.getContentResolver().registerContentObserver(uri, true, contentObserver);
注意:不监听时需解注册内容观察者
context.getContentResolver().unregisterContentObserver(uriObserverStore.get(name));
参考:
Android属性系统简介及使用
Android系统属性set、get详解
Android source官网-添加系统属性
网友评论