应用层在开发串口功用时经常需求跟驱动节点打交道,当咱们新增一个硬件设备,一般会供给对应的节点给上层(framework,app)拜访

拜访节点的方式

  • java IO
  • adb shell cat

以usb节点为例,其途径为/sys/devices/platform/soc/a600000.ssusb/mode,通常咱们运用java标准的IO流

    public static final String NODE_PATH = "/sys/devices/platform/soc/a600000.ssusb/mode";
	public void readNode(View view) {
        String result = FileUtils.readFileByLines(NODE_PATH);
        tvShow.setText(result);
		Log.d(TAG, "node data:"+result);
    }
	public static String readFileByLines(String fileName) {
        BufferedReader bufferedReader = null;
        FileReader fileReader = null;
        try {
            File file = new File(fileName);
            if (!file.exists()) {
                return "文件不存在";
            }
            fileReader = new FileReader(file);
            bufferedReader = new BufferedReader(fileReader);
            String string;
            StringBuffer sb = new StringBuffer();
            while ((string = bufferedReader.readLine()) != null) {
                sb.append(string);
            }
            return sb.toString();
        } catch (IOException e) {
            e.printStackTrace();
            return e.getMessage();
        } finally {
            if (fileReader != null) {
                try {
                    fileReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

或者进入adb shell,直接cat对应节点称号来拜访节点数据

App访问串口节点需要解决的权限问题

权限问题

但直接拜访肯定是不可的,会报权限过错

App访问串口节点需要解决的权限问题

那么实现上层拜访底层节点的核心就在于处理权限问题,首要包含两个方向的权限处理:

  • SEAndroid权限
  • 节点文件自身的读写权限

首先,app归于application层,驱动节点归于kernel层,由于SEAndroid的约束,application层是无法拜访到kernel层的驱动节点文件的,这个咱们应该很好了解,假如应用层能随随便便拜访到kernel里面的东西,那Android也太不安全了,这个时候需求修改SEAndroid的权限约束,让第三方app也能拜访到kernel节点文件。

SEAndroid介绍

在处理这个SEAndroid权限问题之前,咱们先了解一下什么是SEAndroid,这儿我用通俗易懂的言语简单介绍下:

了解SEAndroid前,需求先了解一下SELinux。SELinux是由美国NSA和 SCC 开发的 Linux的一个扩张强制拜访控制安全模块。而SEAndroid相当于定制版SELinux,专门用于Android体系的安全办理。在Android4.4以前,进程的权限与当前用户的权限共同,也就是说,假如我以root用户启动某个进程,那这个进程就具有了root权限,就能够随心所欲了,这种形式称之为DAC(Discretionary Access Control)。明显,DAC太过于宽松了,所以加入了SEAndroid,任何进程想干任何事情,都必须在《安全策略文件》中赋予权限,这便是MAC(Mandatory Access Control)形式。

实际上现在的Android体系一起采用了DAC和MAC的形式,体系会先做DAC检查,假如不通过,则直接操作失利。通过DAC检查之后再做MAC检查。

关于MAC安全策略文件,SELinux有一套规矩来编写,这套规矩称之为SELinux Policy言语。关于这套言语的基本规矩,可参阅此文章《Android体系SELinux(SEAndroid)》。

权限处理

处理权限问题的思路,一般先封闭SELinux验证,一起将方针节点文件的读写权限设到最大,履行

adb root
adb shell setenforce 0
adb shell chmod 777 /sys/devices/platform/soc/a600000.ssusb/mode

看看能否成功拜访节点,假如成功了,阐明确实是权限的问题,然后分别抓取logcat日志(framework)和dmesg日志(kernel),搜索关键字avc,即可得到跟权限相关的过错日志

履行

# 抓取logcat日志
adb logcat | findstr avc > E://logcat.txt
# 抓取dmesg日志
adb shell dmesg | findstr avc > E://dmesg.txt

抓到日志结果如下

[  312.961648] type=1400 audit(1684478793.923:22): avc: denied { read } for comm=".jason.nodetest" name="soc" dev="sysfs" ino=10272 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:sysfs:s0 tclass=dir permissive=0
[  312.962343] type=1400 audit(1684478793.923:23): avc: denied { read } for comm=".jason.nodetest" name="soc" dev="sysfs" ino=10272 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:sysfs:s0 tclass=dir permissive=0
[  314.145472] type=1400 audit(1684478795.107:24): avc: denied { read } for comm=".jason.nodetest" name="mode" dev="sysfs" ino=29874 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:vendor_sysfs_usb_device:s0 tclass=file permissive=0
[  314.886338] type=1400 audit(1684478795.847:25): avc: denied { write } for comm=".jason.nodetest" name="mode" dev="sysfs" ino=29874 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:vendor_sysfs_usb_device:s0 tclass=file permissive=0
[  315.523664] type=1400 audit(1684478796.483:26): avc: denied { write } for comm=".jason.nodetest" name="mode" dev="sysfs" ino=29874 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:vendor_sysfs_usb_device:s0 tclass=file permissive=0
[  315.945379] type=1400 audit(1684478796.907:27): avc: denied { write } for comm=".jason.nodetest" name="mode" dev="sysfs" ino=29874 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:vendor_sysfs_usb_device:s0 tclass=file permissive=0
[  316.294456] type=1400 audit(1684478797.255:28): avc: denied { read } for comm=".jason.nodetest" name="mode" dev="sysfs" ino=29874 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:vendor_sysfs_usb_device:s0 tclass=file permissive=0

以其中一条日志来具体剖析

[  314.145472] type=1400 audit(1684478795.107:24): avc: denied { read } for comm=".jason.nodetest" name="mode" dev="sysfs" ino=29874 scontext=u:r:untrusted_app:s0:c66,c256,c522,c768 tcontext=u:object_r:vendor_sysfs_usb_device:s0 tclass=file permissive=0
  • avc 代表这是SELinux相关的过错
  • denied { read } 表明短少读权限
  • comm=".jason.nodetest" 表明是哪个进程在拜访
  • name="mode" 表明方针文件
  • dev="sysfs" 表明.jason.nodetest进程要拜访的方针文件的文件类型
  • scontext=u:r:untrusted_app 表明拜访进程的类型,这儿表明第三方app
  • tcontext=u:object_r:vendor_sysfs_usb_device:s0 表明方针文件的空间称号

翻译过来就是第三方app要拜访vendor_sysfs_usb_device空间下的mode文件,需求read权限

OK,现在报错原因知道了,那咱们就给它增加权限。

第一步:找到方针节点文件的声明,在genfs_contexts文件中

genfscon sysfs /devices/soc/a800000.ssusb/a800000.dwc3/xhci-hcd.0.auto/usb1 u:object_r:vendor_sysfs_usb_device:s0
genfscon sysfs /devices/soc/a800000.ssusb/a800000.dwc3/xhci-hcd.0.auto/usb2 u:object_r:vendor_sysfs_usb_device:s0
genfscon sysfs /devices/platform/soc/a600000.ssusb/mode                 u:object_r:vendor_sysfs_usb_device:s0
# 方针节点
genfscon sysfs /devices/platform/soc/a800000.ssusb/mode                 u:object_r:vendor_sysfs_usb_device:s0

发现确实是界说在vendor_sysfs_usb_device空间下,而且该空间下还界说了其他节点

第二步:找到vendor_sysfs_usb_device空间的类型界说,在file.te文件中

type vendor_sysfs_usb_device, sysfs_type, fs_type;

类型为sysfsfs

第三步:在untrusted_app_25.te文件中增加第三方app拜访vendor_sysfs_usb_device空间的权限

allow untrusted_app vendor_sysfs_usb_device:file rw_file_perms;

至于这个语法怎样写,之前我在另一篇文章《高通Android 11平台上移植GNSS》中提到过,下面再贴一下

App访问串口节点需要解决的权限问题

假如你觉得这样不好记,也能够借助linux供给的audit2allow工具来主动剖析生成

audit2allow -i dmesg.txt

App访问串口节点需要解决的权限问题

直接将生成的te命令装备到te文件中

装备完后,一定要记住,安全策略不能跟neverallow冲突,咱们检查neverallow规矩发现,SEAndroid规则了第三方app不允许拜访sysfs和fs类型的文件

# Do not allow any write access to files in /sys
# 第三方app不允许拜访sysfs类型文件
neverallow all_untrusted_apps sysfs_type:file { no_w_file_perms no_x_file_perms };
# Apps may never access the default sysfs label.
# 第三方app不允许拜访sysfs类型文件
neverallow all_untrusted_apps sysfs:file no_rw_file_perms;
neverallow { all_untrusted_apps -mediaprovider } {
# 不允许拜访fs类型文件
  fs_type
  -sdcard_type
  file_type
  -app_data_file            # The apps sandbox itself
  -privapp_data_file
  -app_exec_data_file       # stored within the app sandbox directory
  -media_rw_data_file       # Internal storage. Known that apps can
                            # leave artfacts here after uninstall.
  -user_profile_data_file   # Access to profile files
  userdebug_or_eng(`
    -method_trace_data_file # only on ro.debuggable=1
    -coredump_file          # userdebug/eng only
  ')
}:dir_file_class_set { create unlink };

咱们将这些neverallow去掉,留意system/sepolicy/private/app_neverallows.tesystem/sepolicy/prebuilts/api/30.0/private/app_neverallows.te都要去掉,这两个文件必须保持共同,不然编译报错。

到这儿,咱们就能够全编验证了,此刻会发现读节点没问题,但往节点写数据还存在权限问题,这是由于节点文件自身只有read权限,所以还需求在体系启动之后给节点文件增加write权限,在system/core/rootdir/init.rc中的on boot条件下增加

chmod 0777 /sys/devices/platform/soc/a600000.ssusb/mode

这时编译运行,仍会报权限过错,那是由于此节点文件的所有者是root

App访问串口节点需要解决的权限问题

而init进程的所有者是systemsystem所有者直接修改root所有者的文件权限是不行的,需求给init进程增加拜访vendor_sysfs_usb_device权限,在init.te文件中增加

allow init vendor_sysfs_usb_device:file setattr;

一起还要将vendor_sysfs_usb_device界说为mlstrustedobject类型

type vendor_sysfs_usb_device, sysfs_type, fs_type, mlstrustedobject;

此刻再编译验证,第三方app就能够直接读写驱动节点了

读节点

App访问串口节点需要解决的权限问题

写节点

App访问串口节点需要解决的权限问题

总结

此文能够让做Android开发的朋友认识到,底层硬件要让上层能够拜访,怎么去处理权限问题,SELinux相关的知识点对于初学者来说十分的笼统和难以了解,但只需处理几个相关的问题就能马上搞清楚是怎样回事了。