背景
遇到一个需求,需要在Settings中添加一个开关,开关需要控制一个驱动节点,打开时向节点写1,关闭时写0
看起来是个很普通的问题,但坑却不小。
跳坑
需求大部分已经写好,只需要在开关上写值就可以。于是很快的去加上写值的代码。
/packages/apps/Settings/src/com/android/settings/gestures/GestureSettings.java
在onPreferenceTreeClick()中适当位置加入以下任意方法
第一种写法:
public static void setNode(String node_path){
BufferedWriter bufWriter = null;
bufWriter = new BufferedWriter(new FileWriter(NODE_PATH));
bufWriter.write("1"); // 写操作
bufWriter.close();
Toast.makeText(getApplicationContext(),"功能已激活",Toast.LENGTH_SHORT).show();
Log.d(TAG,"功能已激活 angle " + getString(ANGLE_PATH));
}
复制代码
第二种写法:
public static void writeSysFile(String node_path){
Process p = null;
DataOutputStream os = null;
try {
p = Runtime.getRuntime().exec("sh");
os = new DataOutputStream(p.getOutputStream());
os.writeBytes("echo 1 > "+node_path + "\n");
os.writeBytes("exit\n");
os.flush();
} catch (IOException e) {
e.printStackTrace();
Log.e(MainActivity.TAG, " can't write " + sys_path+e.getMessage());
} finally {
if(p != null){
p.destroy();
}
if(os != null){
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
复制代码
这两种写法都是对驱动节点写值,本质上等同于
adb root
adb remount
adb shell
:/ echo 1 > /sys/bus/i2c/drivers/fts_ts/3-0038/fts_gesture_mode
:/ cat /sys/bus/i2c/drivers/fts_ts/3-0038/fts_gesture_mode
Gesture Mode: On
Reg(0xD0) = 0
复制代码
其中 /sys/bus/i2c/drivers/fts_ts/3-0038/fts_gesture_mode 就是 node_path,这里随便写了一个做示范。
这里看起来还行,编译也不会出错。
出现问题
试了两种写法都未生效,这里第二种方法相当于sh,因而只管写入,不生效也没有log比较坑。 第一种写法报出的log很明显是SELinux问题。
avc: denied { write } for name="fts_gesture_mode" dev="sysfs" ino=29040 scontext=u:r:system_app:s0 tcontext=u:object_r:sysfs:s0 tclass=file permissive=0
复制代码
关于这类报错,有很多介绍说明。谷歌官方 ;快速解决
简单的说就是分解下面这段
denied { write } *** context=u:r:system_app:s0 tcontext=u:object_r:sysfs:s0 tclass=file permissive=0
复制代码
写成这种格式 allow context tcontext:tclass denied;
得到这样的语句 allow system_app sysfs:file write;ps(因为添加上述代码在/packages/apps/Settings/中,即Settings应用,拥有android.uid.system,属于system_app)
于是去“system_app.te”添加allow权限。(system_app.te在QCOM平台放置在devices/qcom/sepolicy/msm89**)
allow system_app sysfs:file write;
或
allow system_app sysfs:file rw_file_perms;
复制代码
添加完开始编译~~
libsepol.report failure: neverallow on line 489 of system/sepolicy/private/app.te (or line 22022 of policy.conf) violated by allow system_app sysfs:file { write );
libsepol.check_assertions: 1 neverallow failures occurred
Error while expanding policy
复制代码
然鹅编译报错,看报错信息是 1 neverallow failures occurred
基本上,出现neverallow说明此路不通,如果只是临时验证,可以临时关闭,临时关闭的话,此时:
adb root
adb remount
adb shell
:/ setenforce 0 ##设置SELinux 成为permissive模式(SELinux开启,但对违反selinux规则的行为只记录,不会阻止)
:/ getenforce ##获取SELinux状态(permissive,enforcing,disabled)
Permissive
复制代码
这样再操作开关就可以正常写值,不会报错。这可以用来验证上层代码是否OK。 但是作为系统版本不能这么做。强行修改neverallow语法会导致CTS问题,那就是挖了个更大的坑。
解决办法
以上方法走不通,于是找了一会。发现有更简便的方法。
init.rc 这个开机启动并持续运行的服务可以解决很多问题。
回到需求本身,只是在settings的开关状态改变时,对应的改变驱动节点值。 那么init.rc可以做到监听一个状态的改变,并执行命令。(在QCOM平台上init.rc对应为init.target.rc位于devices/qcom/msm89**)
如下格式:
on <trigger> [&& <trigger>]* //设置触发器
<command>
<command> //动作触发之后要执行的命令
<command>
复制代码
于是立刻写上:
on property:persist.gesture.dclick=1
write /sys/bus/i2c/devices/3-0038/fts_gesture_mode 1
on property:persist.gesture.dclick=0
write /sys/bus/i2c/devices/3-0038/fts_gesture_mode 0
复制代码
那么“persist.gesture.dclick”则是在settings中点击时赋值。
private void setDoubleTap(int value) {
try{
Log("Write double tap on value--" + value);
SystemProperties.set(PERSIST_GESTURES_DLICK, Integer. tostring(value));
} catch (IllegalArgumentException e) {
Log.w( TAG, e.toString());
}
}
复制代码
于是通过 SystemProperties、init.rc 实现了上层对驱动节点的写操作。
同样的,上层对驱动节点没有权限的操作、特殊的开机services、状态监听等等,都可以用此种方式解决。稳得不行。
小结
刚入framework开发,不能局限于app开发思维,需要往底层走走看看。