目录
应用闪退:java.lang.RuntimeException: failed to set system property
万能百度:SeLinux机制,非系统应用&未注册前缀不可设置
-
前景提要
今天在系统应用开发维护中遇到了一个很有意思的问题。
为了减少重复工作量,所有平台会共同依赖一个共用协议栈接口,对接系统各项定制功能实现。此为前景提要。
在内置应用进行迭代开发的时候,为了优化原有与其他模块/应用对接的方式,从原本读取contentProvider的方式改为了系统属性prop。因此在协议栈接口中做了兼容处理,如下:
public void setXXXXEnable(boolean enable) {
……
// 新增语句
SystemProperties.set("persist.company.xxxxxx.xxxx.enable", enable? "1" : "0");
}
public boolean getXXXXEnable() {
……
// 新增语句
return SystemProperties.getInt("persist.company.xxxxxx.xxxx.enable", 1) == 1;
}
// 后续还有多个类似修改
在完成了系统测试且上线后,在半年后的今天,出现了问题。
-
应用闪退:java.lang.RuntimeException: failed to set system property
当收到这个问题之后,首先查看了下闪退的crash,如下:
12-10 16:18:35.948 E/AndroidRuntime( 3193): FATAL EXCEPTION: main
12-10 16:18:35.948 E/AndroidRuntime( 3193): Process: xxxxx, PID: 3193
12-10 16:18:35.948 E/AndroidRuntime( 3193): java.lang.RuntimeException: failed to set system property
12-10 16:18:35.948 E/AndroidRuntime( 3193): at android.os.SystemProperties.native_set(Native Method)
12-10 16:18:35.948 E/AndroidRuntime( 3193): at android.os.SystemProperties.set(SystemProperties.java:130)
12-10 16:18:35.948 E/AndroidRuntime( 3193): at // 指向协议栈接口实现
12-10 16:18:35.948 E/AndroidRuntime( 3193): ……
当收到某个平台报出的这个问题之后,首先非常疑惑,这个实现已经上线了半年之久,为什么突然会在某个平台出现闪退的情况呢?于是问了下对应平台最近有没有什么特殊更新,回复最近才对协议栈接口逻辑大更新了一波。于是判定确实是新增的Systemprop实现导致的问题。
手动在adb试了一下setprop的命令,也打出了failed to set system property的提示。判定不是定制的逻辑问题,而是系统逻辑问题了。接下来开始查问题:
-
万能百度:SeLinux机制,非系统应用&未注册前缀不可设置
遇到没见过的问题,首先百度下看看。发现大家都是一样的回答:
1.Selinux开启
2.设置property的必须是系统应用,即属于系统组:AndroidManifest下设置 android:sharedUserId="android.uid.system"
3.property_contexts 中有对应prop和权限组(对应2的uid应该要有是系统权限)的声明
※ 注:在源码中,property_contexts 对应的目录应该是 /system/sepolicy/private/property_contexts 、 /system/sepolicy/public/property_contexts (我查阅的是Android9.0源码)。而对应的rom定制系统,应该是在device下厂家自加入的property_contexts 文件。系统编译时会搜索到device下的property_contexts, 并以合并的方式追加到原有system下property_contexts 后。
对着上面的条件进行查验:应用是系统级应用,uid组是系统组,于是去查对应的property_contexts。在device目录下查找persist.company的声明,发现没有。不死心直接在根目录find -name property_contexts找到所有的property_contexts目录,均未发现persist.company的痕迹。在adb里查找了一下prop:
getprop | grep persist.company
[persist.company.xxxxxx]: [true]
[persist.company.xxxxxxxx]: [0]
[persist.company.xxxxxxxxxx]: [0]
[persist.company.xxxxxxxxxx]: [0]
[persist.company.xxxx]: [1]
[persist.company.xxxxxxx]: [1]
发现均有结果。这里和判定的条件出现了冲突,貌似是selinux的限制并没有其效果。想起来我们的系统都是关闭了selinux的,adb 查看了一下系统状态:( Permissive —— 宽容模式,相当于关闭; enforce —— 强制模式,相当于打开)
xxxxxxx:/ # getenforce
Permissive
果然是关闭了,所以不是这个原因,万能百度探究失败。
-
发现端倪:android系统版本限制
在上一步adb查看系统内所有属性的时候,发现应用内与这个设置失败的属性同时设置的其他属性出现了几个,开头同为persist.company.xxxxx。于是判断并不是应用设置实现方式出现的问题。这时候我闲着无事,把原来的 persist.company.xxxxxx.xxxx.enable 删掉了一部分,修改成了 persist.company.xxxxxx.xxxx。这时候发现adb设置成功了!所以判断是prop的名字出现了问题。于是逐渐递增尝试:
setprop persist.company.xxxxxx.xxxx.e --> 成功
setprop persist.company.xxxxxx.xxxx.en --> 成功
setprop persist.company.xxxxxx.xxxx.ena --> 成功
setprop persist.company.xxxxxx.xxxx.enab --> 成功
setprop persist.company.xxxxxx.xxxx.enabl --> 失败
setprop persist.company.xxxxxx.xxxx.enable --> 失败
等到失败的时候,数了下长度,prop的长度是33。至少现在已经有方向了,依旧靠万能百度查看一下。
然后确认在bionic/libc/include/sys/system_properties.h 中定义了和prop相关的属性:
// 当前查看源码版本:Android 5.1
#define PROP_NAME_MAX 32
#define PROP_VALUE_MAX 92
和试验的结果一样!于是换成了其他系统查看相同的文件配置:
// 当前查看源码版本:Android 8.0
#define PROP_VALUE_MAX 92
/* Deprecated. In Android O and above, there's no limit on property name length. */
#define PROP_NAME_MAX 32
结案! 源码中已经做了标识,在Android O(Android8.0)以下的系统,PROP_NAME长度限制成了32,而Android O及以上则没有这个限制。在进行迭代开发测试的时候,测试环境只有8.0及以上的系统,5.1作为一个尾单系统没有被兼顾到,因此出现了这个问题。因此,对协议栈中的实现进行了修改:
- xxxxEnable = SystemProperties.getInt("persist.company.xxxxxx.xxxx.enable", 1) == 1;
+ xxxxEnable = SystemProperties.getInt("persist.company.xxxxxx.xxxx.on", 1) == 1;
- SystemProperties.set("persist.company.xxxxxx.xxxx.enable", enable? "1" : "0");
+ SystemProperties.set("persist.company.xxxxxx.xxxx.on", enable? "1" : "0");
解决!
-
拓展
查看一下源码:
android_os_SystemProperties.cpp