Android系统 init.rc sys/class系统节点写不进解决方案和原理分析

简介: Android系统 init.rc sys/class系统节点写不进解决方案和原理分析

Android 9.0及以上版本中,由于系统对安全要求越来越严格,不允许app往/sys/class这样的系统节点写入数据,即使是系统应用也没有这个权限了。这给一些需要控制硬件设备的app带来了困难。例如,如果app需要控制LED灯的亮度,但是没有节点权限,怎么办呢?本文将介绍一种解决方案,利用init.rc文件中的write命令和on property触发器,来实现在app中正确往系统节点写数据的功能。

系列文章

Android系统 init.rc 第一次开机创建文件节点实现和原理分析

Android系统 init.rc开机执行shell脚本

Android系统 init.rc sys/class系统节点写不进解决方案和原理分析

Android系统 init.rc文件详解

Android系统 自定义动态修改init.custom.rc

为什么没有权限写入?

在Android 9.0及以上版本中,Google对系统进行了一些修改,增加了对文件系统的加密和保护。这意味着一些敏感的目录,如/data, /mnt等,不能被任意访问和修改。只有在特定的阶段,才能对这些目录进行操作。

而/sys/class目录是一个特殊的目录,它是一个虚拟文件系统,用于提供内核和用户空间之间的通信接口。它包含了一些硬件设备的状态和控制信息,如LED灯、电池、温度等。如果任何app都可以随意往这些节点写入数据,可能会造成系统的不稳定或安全风险。因此,Google限制了对/sys/class目录的访问权限,只有root用户或者拥有特定SELinux策略的进程才能操作这些节点。

从app的角度来看,app运行在一个沙盒环境中,它只能访问自己的数据目录和一些公共目录。它不能直接访问/sys/class目录或其他系统目录。即使是系统应用,也需要申请一些特殊的权限或者签名才能访问这些目录。因此,在Android 9.0及以上版本中,app往/sys/class目录写入数据是不可能的。

从adb的角度来看,adb是一个调试工具,它可以通过USB或网络连接到Android设备上,并执行一些命令或操作。adb可以以root用户或者shell用户身份运行。如果以root用户身份运行,adb可以访问/sys/class目录并往其中写入数据。但是,在Android 9.0及以上版本中,默认情况下adb是以shell用户身份运行的,并且不能切换到root用户身份。除非你对设备进行了root操作或者解锁了bootloader,并且安装了一个支持root的内核或者ROM。因此,在Android 9.0及以上版本中,默认情况下adb也不能往/sys/class目录写入数据。

解决方案

解决方案1: 把设备root

如果你要在adb shell去写入 请看方案2

如果你要在app 去写入/sys/class 节点 , 你需要设备root权限 , 方案非常多

可以看我另外一篇: 链接: Android13 Root实现和原理分析

然后在网上随便抄个shell utils工具类 , 通过root执行 非常简单 不过多赘述 , 如果实在不清楚 可以留言私信。

ShellUtils.execCmd("echo '2 0 1' > /sys/devices/platform/leds/leds/led ", true);

解决方案2: 把节点权限改成

1|rk3568_r:/sys # ls -ll devices/platform/leds/leds/led
-rw-r--r-- 1 root root 4096 2023-07-27 17:38:16.813334118 +0800 devices/platform/leds/leds/led

在Android系统中,节点的权限是由设备驱动在创建节点时设置的,通常情况下,这些权限是固定的,不能被修改。在我的例子中,节点的权限是rw-r--r--,这意味着只有root用户可以写入这个节点,其他用户只能读取这个节点。

如果你想让普通app和adb shell能写入这个节点,你需要修改设备驱动的代码,让它在创建节点时设置更宽松的权限。但是,这可能会带来安全风险,因为任何app都可以修改这个节点,可能会导致系统不稳定或者硬件设备损坏。

如果你的设备已经root,你可以使用chmod命令来修改节点的权限。例如,你可以使用以下命令来修改节点的权限:

chmod 777 /sys/devices/platform/leds/leds/led

这个命令会把节点的权限修改为rwxrwxrwx,这意味着任何用户都可以读取和写入这个节点。

但是,需要注意的是,这个修改只是临时的,当设备重启后,节点的权限会恢复到原来的状态。如果你想让这个修改永久生效,你需要修改设备驱动的代码,或者在init.rc文件中添加一个命令,让系统在启动时自动修改节点的权限。

# 这里证明, 已经修改了权限 也验证了 之前默认adb shell是无法写入的 , 我们尝试在init.rc 修改了权限777 所有用户均可读写。 
rk3568_r:/ # ls -ll /sys/devices/platform/leds/leds/led  
-rwxrwxrwx 1 root root 4096 2023-07-28 10:51:24.256688797 +0800 /sys/devices/platform/leds/leds/led
rk3568_r:/ # exit
rk3568_r:/ $ echo "2 0 1" > /sys/devices/platform/leds/leds/led

解决方案3: 通过on property触发器

既然app和adb都不能直接往/sys/class目录写入数据,那么我们是否有其他方法可以实现这个功能呢?答案是有的。我们可以利用init.rc文件中的write命令和on property触发器来实现这个功能。

init.rc文件是Android系统启动时由init进程读取的初始化配置文件,用于启动Android中的各种服务,以及配置系统参数。init.rc文件使用一种类似于shell脚本的语法,

在init.rc文件中,我们可以使用以下两种命令来实现我们的目的:

  • write [ ]*:向指定的文件写入一个或多个字符串。例如,write /sys/class/leds/brightness 0,就是向/sys/class/leds/brightness文件写入0,表示关闭LED灯。
  • on property:=:当指定的系统属性的值变化时,执行相应的动作。例如,on property:persist.sys.enable=1,就是当persist.sys.enable属性的值变为1时,执行后面的动作。

结合这两种命令,我们可以在init.rc文件中添加一些代码,来监听app设置的系统属性的变化,并根据不同的值来往/sys/class目录写入数据。例如,如果我们想控制LED灯的亮度,我们可以在app中使用SystemProperties.set(“persist.sys.led”, “0”)或SystemProperties.set(“persist.sys.led”, “1”)来设置persist.sys.led属性的值。然后,在init.rc文件中添加以下代码:

# 监听persist.sys.led属性的变化
on property:persist.sys.led=0
    # 当persist.sys.led属性的值为0时,关闭LED灯
    write /sys/class/leds/leds/led "2 0 1"
on property:persist.sys.led=1
    # 当persist.sys.led属性的值为1时,打开LED灯
    write /sys/class/leds/leds/led "2 0 0"

这样,当app设置了persist.sys.led属性的值后,init进程就会根据这个值来往/sys/class/leds/leds/led文件写入数据,从而控制LED灯的亮度。

为什么app和adb无法访问,而init.rc可以写入?

这是因为init进程在系统启动时以root用户身份运行,它有权限访问/sys/class目录并往其中写入数据。而app和adb默认是以非root用户身份运行的,它们没有权限访问/sys/class目录。这是Android系统为了保护系统安全和稳定性,防止非授权的访问和修改系统节点而设置的限制。

怎么解决写入节点值是动态的如何传参?

如果需要传递参数,可以在write命令后面添加参数。例如,如果需要控制LED灯的亮度和颜色,可以在app中使用SystemProperties.set(“persist.sys.led”, “1 0 1”)或SystemProperties.set(“persist.sys.led”, “1 0 0”)来设置persist.sys.led属性的值。然后,在init.rc文件中添加

以下代码:

# 监听persist.sys.led属性的变化
on property:persist.sys.led=*
    # 当persist.sys.led属性的值变化时,根据其值来控制LED灯
    write /sys/class/leds/leds/led ${persist.sys.led}

这样,当app设置了persist.sys.led属性的值后,init进程就会根据这个值来往/sys/class/leds/leds/led文件写入数据,从而控制LED灯的亮度和颜色。

测试验证

130|rk3568_r:/ # setprop persist.sys.led "2 0 0" //我们尝试写入把第3组的第1个LED打开
rk3568_r:/ # getprop persist.sys.led //查看一下写入的值
2 0 0  
rk3568_r:/ # logcat | grep persist.sys.led //可以看到action已经被触发
01-01 08:00:06.017     0     0 E init    : Could not set 'persist.sys.led' to '2 >> out/target/product/rk3568_r/vendor/build.prop;  echo 0 >> out/target/product/rk3568_r/vendor/build.prop;  echo 0' while loading .prop filesProperty value too long
07-28 10:48:43.868     0     0 I init    : processing action (persist.sys.led=*) from (/system/etc/init/hw/init.rc:535)
07-28 10:49:56.382     0     0 I init    : processing action (persist.sys.led=*) from (/system/etc/init/hw/init.rc:535)
07-28 10:50:37.042     0     0 I init    : processing action (persist.sys.led=*) from (/system/etc/init/hw/init.rc:535)
07-28 10:51:19.331     0     0 I init    : processing action (persist.sys.led=*) from (/system/etc/init/hw/init.rc:535)
07-28 10:51:24.381     0     0 I init    : processing action (persist.sys.led=*) from (/system/etc/init/hw/init.rc:535)
^C
130|rk3568_r:/ # dmesg | grep led_   //这是我加的driver打印 证明已经写入了
[    4.399010] 11 led_probe 55
[    4.399179] led_init_state to request GPIO pin 14 for LED 0 color 0
[    4.399197] led_init_state to request GPIO pin 13 for LED 0 color 1
[    4.399213] led_init_state to request GPIO pin 19 for LED 1 color 0
[    4.399229] led_init_state to request GPIO pin 18 for LED 1 color 1
[    4.399245] led_init_state to request GPIO pin 91 for LED 2 color 0
[    4.399260] led_init_state to request GPIO pin 97 for LED 2 color 1
[    4.399274] led_init_state to request GPIO pin 59 for LED 3 color 0
[    4.399288] led_init_state to request GPIO pin 58 for LED 3 color 1
[    4.399303] led_init_state to request GPIO pin 57 for LED 4 color 0
[    4.399317] led_init_state to request GPIO pin 56 for LED 4 color 1
[    4.399331] led_init_state to request GPIO pin 99 for LED 5 color 0
[    4.399345] led_init_state to request GPIO pin 98 for LED 5 color 1
[    4.399361] led_init_state to request GPIO pin 94 for LED 6 color 0
[    4.399376] led_init_state to request GPIO pin 90 for LED 6 color 1
[    4.399390] led_init_state to request GPIO pin 101 for LED 7 color 0
[    4.399404] led_init_state to request GPIO pin 100 for LED 7 color 1
[    4.399418] led_init_state to request GPIO pin 5 for LED 8 color 0
[    4.399432] led_init_state to request GPIO pin 0 for LED 8 color 1
[    5.907318] selinux: SELinux: Loaded policy from /odm/etc/selinux/precompiled_sepolicy
rk3568_r:/ #
此时我看设备上的灯已经亮了

注意

虽然这个解决方案可以有效地实现我们的目的,但是也有一些注意事项:

  • 不要在init.rc文件中添加过多的on property触发器,可能会影响init进程的性能和内存占用。最好只添加你需要的触发器,并且尽量简化触发器中的动作。
  • 正常情况下推荐是方案3 , 一般设备是不root的 , 同时也能避免有些sb驱动 不改driver权限的问题。
  • 验证发现如果打开SELINUX的话 普通app和adb shell 是压根没有权限写prop的。
  • 后面我跟了下代码流程发现这里是检测SELINUX权限的 会检测UID等信息, 所以我们屏蔽掉 这样普通app和adb shell都能执行写入prop。这种不是正规的搞法 适合不过GMS的源码 正常情况还是要去写上下文的。

总结

本文介绍了一种在Android 9.0及以上版本中解决app往/sys/class目录写不进数据的问题的方案,即利用init.rc文件中的write命令和on property触发器,来实现在app中正确往系统节点写数据的功能。本文也分析了这个方案的原理和注意事项,

希望对你有所帮助。如果你有任何问题或建议,欢迎留言讨论。谢谢

相关实践学习
使用CLup和iSCSI共享盘快速体验PolarDB for PostgtreSQL
在Clup云管控平台中快速体验创建与管理在iSCSI共享盘上的PolarDB for PostgtreSQL。
AnalyticDB PostgreSQL 企业智能数据中台:一站式管理数据服务资产
企业在数据仓库之上可构建丰富的数据服务用以支持数据应用及业务场景;ADB PG推出全新企业智能数据平台,用以帮助用户一站式的管理企业数据服务资产,包括创建, 管理,探索, 监控等; 助力企业在现有平台之上快速构建起数据服务资产体系
相关文章
|
11天前
|
Java Android开发 iOS开发
深入探讨移动操作系统的性能优化:安卓与iOS的对比分析
在现代移动设备中,操作系统的性能优化至关重要。本文从系统架构、内存管理、电池续航和应用程序运行效率等多个维度,深入探讨了安卓(Android)和iOS两大主流移动操作系统的优化策略及其实际效果,旨在为开发者和用户提供更清晰的了解和选择依据。
22 0
|
7天前
|
存储 监控 调度
Android系统服务:WMS、AMS相关知识
参考文献 Android窗口管理服务WindowManagerService计算Activity窗口大小的过程分析 Android窗口管理服务WindowManagerService显示Activity组件的启动窗口(Starting Window)的过程分析 Android窗口管理服务WindowManagerService对输入法窗口(Input Method Window)的管理分析 Android窗口管理服务WindowManagerService显示窗口动画的原理分析
|
8天前
|
安全 Android开发 iOS开发
探索Android与iOS开发的差异:平台特性与用户体验的对比分析
在移动应用开发的广阔天地中,Android和iOS两大阵营各据一方。本文将深入探讨这两个操作系统在开发环境、编程语言、用户界面设计及市场分布等方面的主要区别。通过比较分析,我们将揭示各自平台的特有优势,并讨论如何根据目标受众和业务需求选择适合的开发平台。
|
10天前
|
Java Linux Android开发
Android面试题之说说系统的启动流程(总结)
这篇文章概述了Android系统的启动流程,从Boot Rom到Zygote进程和SystemServer的启动。init进程作为用户级别的第一个进程,负责创建文件目录、初始化服务并启动Zygote。Zygote通过预加载资源和创建Socket服务,使用fork函数生成SystemServer进程。fork过程中,子进程继承父进程大部分信息但具有独立的进程ID。Zygote预加载资源以减少后续进程的启动时间,而SystemServer启动众多服务并最终开启Launcher应用。文中还讨论了为何从Zygote而非init或SystemServer fork新进程的原因。
19 2
|
17天前
|
JavaScript Java 测试技术
基于ssm+vue.js+uniapp小程序的安卓的微博客系统附带文章和源代码部署视频讲解等
基于ssm+vue.js+uniapp小程序的安卓的微博客系统附带文章和源代码部署视频讲解等
26 2
|
2天前
|
Java Linux API
微信API:探究Android平台下Hook技术的比较与应用场景分析
微信API:探究Android平台下Hook技术的比较与应用场景分析
|
2天前
|
安全 Android开发 iOS开发
探索安卓与iOS开发的差异:平台特性与用户体验的对比分析
移动应用开发的两大阵营——安卓与iOS,各自拥有独特的开发环境、用户群体和市场定位。本文将深入探讨这两个操作系统在应用开发过程中的主要差异,包括编程语言、开发工具、用户界面设计、性能优化、安全性考量以及发布流程等方面。通过比较分析,旨在为开发者提供跨平台开发的见解和策略,以优化应用性能和提升用户体验。
7 0
|
2天前
|
缓存 Java Linux
Android 匿名内存深入分析
Android 匿名内存深入分析
7 0
|
10天前
|
JavaScript Java 测试技术
基于SpringBoot+Vue+uniapp的安卓的微博客系统的详细设计和实现
基于SpringBoot+Vue+uniapp的安卓的微博客系统的详细设计和实现
9 0
|
11天前
|
搜索推荐 安全 Android开发
安卓与iOS操作系统的对比分析
在移动设备市场上,安卓和iOS操作系统一直是主要竞争对手。本文将从用户界面、应用生态系统、定制化程度和安全性等方面对安卓和iOS进行对比分析,并探讨两者在不同场景下的适用性。