Android应用无法修改input_suspend值,求充电停止实现方案
我正在开发一款可停止电池充电的Android应用,设备运行Android 12,应用compileSdk版本为32,且设备已Root,已执行setenforce 0关闭SELinux。
经测试,通过PC连接设备后分步执行以下命令可成功停止充电:
adb shell su echo 1 > /sys/class/power_supply/battery/input_suspend
但在应用中使用两种方式执行Root命令均无效:
第一种代码实现
package com.example.chargedisabler; import static android.content.ContentValues.TAG; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import java.io.IOException; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); try { Runtime.getRuntime().exec("su -c \"echo 1 > /sys/class/power_supply/battery/input_suspend\""); } catch (IOException e) { e.printStackTrace(); } } }
第二种代码实现
package com.example.chargedisabler; import static android.content.ContentValues.TAG; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import java.io.IOException; import java.io.OutputStream; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); try { Process p = Runtime.getRuntime().exec("su"); OutputStream OStrm = p.getOutputStream(); OStrm.write(("echo 1 > /sys/class/power_supply/battery/input_suspend\n").getBytes("UTF-8")); } catch (IOException e) { e.printStackTrace(); } } }
另外,直接执行adb shell su -c "echo 1 > /sys/class/power_supply/battery/input_suspend"会提示/system/bin/sh: can't create /sys/class/power_supply/battery/input_suspend: Permission denied,但可通过adb shell su -c "cat /sys/class/power_supply/battery/input_suspend"读取该文件值为"0"。
现询问如何在应用内修改该文件值,或是否有通用的设备充电停止方案?
1. 修复应用内Root命令执行问题
问题根源
分步执行su再写命令能成功,但su -c直接执行失败,是因为重定向操作>是由shell处理的,若命令参数引号处理不当,重定向会被当前非root的shell解析,而非root环境无写入权限。第二种代码未刷新输出流、未等待命令执行完成,也未处理错误输出,导致命令可能未真正执行。
修正后的代码实现
方式一:使用字符串数组传递su参数
用字符串数组传递命令,避免本地shell解析引号,同时等待执行并检查退出码:
import static android.content.ContentValues.TAG; import android.util.Log; import java.io.IOException; // 在Activity中执行 try { Process process = Runtime.getRuntime().exec(new String[]{"su", "-c", "echo 1 > /sys/class/power_supply/battery/input_suspend"}); int exitCode = process.waitFor(); if (exitCode != 0) { String error = new String(process.getErrorStream().readAllBytes()); Log.e(TAG, "命令执行失败,退出码:" + exitCode + ",错误信息:" + error); } else { Log.d(TAG, "充电已成功停止"); } } catch (IOException | InterruptedException e) { e.printStackTrace(); }
方式二:完善OutputStream写入逻辑
确保刷新输出流、关闭流,等待进程执行完成:
import static android.content.ContentValues.TAG; import android.util.Log; import java.io.IOException; import java.io.OutputStream; // 在Activity中执行 try { Process p = Runtime.getRuntime().exec("su"); OutputStream os = p.getOutputStream(); os.write(("echo 1 > /sys/class/power_supply/battery/input_suspend\n").getBytes("UTF-8")); os.flush(); os.close(); int exitCode = p.waitFor(); if (exitCode != 0) { String error = new String(p.getErrorStream().readAllBytes()); Log.e(TAG, "错误信息:" + error); } } catch (IOException | InterruptedException e) { e.printStackTrace(); }
2. 通用充电停止方案(适配更多设备)
不同厂商设备的充电控制sysfs路径存在差异,可尝试以下方案:
- 排查其他sysfs节点:通过
adb shell su -c ls /sys/class/power_supply/battery/查看所有节点,常见替代路径如/sys/class/power_supply/battery/charging_enabled(写入0停止充电)、/sys/class/power_supply/usb/charging_enabled(控制USB充电)。 - Magisk模块方案:编写简单的Magisk模块,修改系统充电相关服务或脚本,应用通过调用模块提供的接口控制充电,兼容性更强,避免直接操作sysfs的权限问题。
- 系统签名API(受限):若能获取系统签名,可调用
PowerManager的隐藏API(如setChargingEnabled(false)),但普通第三方应用无法直接使用。
内容的提问来源于stack exchange,提问作者TranslucentFoxHuman

