oreo上system_app向驅動節點寫值

黃燜雞米花發表於2018-05-01

背景

遇到一個需求,需要在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開發思維,需要往底層走走看看。

相關文章