多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

KernelSU 模块 WebUI 开发指南:从 webroot 目录到 JavaScript API 的完整实践

KernelSU 模块 WebUI 开发指南:从 webroot 目录到 JavaScript API 的完整实践 KernelSU 模块 WebUI 开发指南从 webroot 目录到 JavaScript API 的完整实践【免费下载链接】KernelSUA Kernel based root solution for Android项目地址: https://gitcode.com/GitHub_Trending/ke/KernelSU导读KernelSU 的模块不仅能执行开机脚本、修改系统文件还能直接向用户展示图形化界面并与之交互。本指南以 KernelSU 模块的 WebUI 机制为核心讲解webroot目录的规范结构、kernelsuJavaScript 库的系统 API 用法shell 命令执行、全屏切换、Toast 提示等并结合仓库源码剖析 WebView 桥接、root shell 注入与 SELinux 上下文处理等底层原理。读完本文你将能够从零构建一个带交互界面的 KernelSU 模块并理解其安全边界与数据持久化策略。一、WebUI 是什么模块从命令行脚本走向图形界面传统上KernelSU 模块通过启动脚本service.sh/post-fs-data.sh和修改系统文件来工作用户与模块的交互往往局限在配置文件编辑。WebUI 打破了这一限制模块可以定义任意 Web 技术HTML CSS JavaScript编写的页面由 KernelSU 管理器Manager App通过 WebView 渲染展示并向页面暴露与系统交互的 API例如执行 shell 命令。这意味着模块作者可以提供可视化的配置面板替代手工编辑配置文件在界面中实时执行命令并展示输出如查看日志、切换功能开关通过 Toast、全屏等原生能力增强交互体验。在管理器侧模块列表页会为带 WebUI 的模块展示入口。入口的可视化图标可通过module.prop中的webuiIcon字段指定管理器解析该字段并用于 WebUI 快捷入口的展示相关逻辑位于 userspace/ksud/src/module.rs。二、webroot目录WebUI 的资源根目录2.1 目录结构与硬性要求所有 Web 资源文件必须放在模块根目录下的webroot子目录中且必须包含一个名为index.html的文件它是模块页面的入口。包含 Web 界面的最简模块结构如下❯ tree . . |-- module.prop -- webroot -- index.html如果页面包含 CSS 或 JavaScript同样需要放置在webroot目录内。要点与警告index.html是强制要求MUST缺少它模块将无法展示 WebUI安装模块时KernelSU 会自动为webroot目录设置权限和 SELinux 上下文。除非你明确知道自己在做什么否则不要自行修改该目录的权限否则可能导致 WebView 无法读取资源或触发 SELinux 拒绝在 ksud 中webroot目录名由常量MODULE_WEB_DIR定义userspace/ksud/src/defs.rs且扫描模块时会通过path.join(defs::MODULE_WEB_DIR).exists()检测模块是否带 WebUI并写入web标志位供管理器识别userspace/ksud/src/module.rs。2.2 管理器如何加载 webroot 资源从管理器源码可以确认资源的加载方式管理器通过WebViewAssetLoader将域名mui.kernelsu.org与模块的webroot目录绑定并使用带 root shell 的SuFilePathHandler读取文件——由于模块目录位于/data/adb/modules/moduleId/webroot受 SELinux 保护普通文件访问无法直接读取必须借助 root shell 与SuFile完成见 manager/app/src/main/java/me/weishu/kernelsu/ui/webui/WebViewHelper.kt 与 SuFilePathHandler.java。此外SuFilePathHandler还内置了两个特殊的内部资源路径/internal/insets.css动态返回系统窗口 insets状态栏/导航栏高度的 CSS 变量/internal/colors.css根据管理器的主题设置动态生成 Monet 动态取色 CSS。详见 SuFilePathHandler.java2.3 入口加载的可用性校验管理器在加载 WebUI 前会做多重校验模块必须存在、hasWebUi为真、模块处于启用状态、且无update/remove待处理标记任一不满足都会显示模块不可用错误见 WebViewHelper.kt。这提醒模块作者WebUI 只在模块正常启用时可用卸载或更新流程中的模块无法打开界面。三、JavaScript API通过kernelsu库调用系统能力如果只是展示静态内容WebUI 与普通网页并无区别其价值核心在于 KernelSU 提供的一系列系统 API。KernelSU 提供了发布在 npm 上的 JavaScript 库kernelsu可在页面代码中直接使用。3.1 安装在 Web 前端项目中安装yarn add kernelsu该库的完整类型声明与实现分别位于 js/index.d.ts 与 js/index.js。3.2 exec执行 shell 命令exec会在root shell中运行一条命令返回一个 Promise命令完成后解析出stdout与stderr输出import { exec } from kernelsu; const { errno, stdout } exec(getprop ro.product.model);带选项的完整示例指定工作目录与退出码判断import { exec } from kernelsu; const { errno, stdout, stderr } await exec(ls -l, { cwd: /tmp }); if (errno 0) { // success console.log(stdout); }参数说明对应 js/index.d.ts参数类型说明commandstring要执行的命令参数以空格分隔options.cwdstring子进程的工作目录options.envObject环境变量键值对返回值PromiseExecResults含errno退出码、stdout、stderr底层原理管理器侧的WebViewInterface.exec把cwd转换为cd cwd;前缀、把env逐项转换为export KEYvalue;前缀后拼接命令再通过withNewRootShell(true)创建 root shell 执行最后把退出码与输出通过javascript:协议回调到页面注册的全局回调函数见 WebViewInterface.kt。js/index.js中的exec使用唯一回调函数名 window 全局函数 Promise的模式封装了这一桥接js/index.js。3.3 spawn流式执行命令spawn以 root shell 启动一个新进程支持将命令行参数作为数组传入省略时默认为空数组并返回ChildProcess实例可监听流式输出与退出事件import { spawn } from kernelsu; const ls spawn(ls, [-lh, /data]); ls.stdout.on(data, (data) { console.log(stdout: ${data}); }); ls.stderr.on(data, (data) { console.log(stderr: ${data}); }); ls.on(exit, (code) { console.log(child process exited with code ${code}); });ChildProcess 事件与流成员类型说明stdoutReadable Stream子进程标准输出流stderrReadable Stream子进程标准错误流on(exit, code)事件子进程结束时触发code为退出码正常退出时on(error, err)事件进程无法 spawn 或无法终止时触发参数签名对应 js/index.d.tsspawn(command: string): ChildProcess; spawn(command: string, args: string[]): ChildProcess; spawn(command: string, options: SpawnOptions): ChildProcess; spawn(command: string, args: string[], options: SpawnOptions): ChildProcess;底层原理管理器侧通过CallbackList将 stdout/stderr 的每行数据以emit(data, ...)的形式实时推送到页面的ChildProcess对象上进程结束后再触发emit(exit, code)若退出码非 0 还会额外触发error事件见 WebViewInterface.kt。注意spawn的 stdout/stderr 输出以行为单位回调适合top、logcat这类持续输出的场景。3.4 fullScreen全屏切换请求 WebView 进入或退出全屏import { fullScreen } from kernelsu; fullScreen(true);管理器侧会隐藏/显示系统栏状态栏、导航栏并同步启用边缘到边缘edge-to-edge布局见 WebViewInterface.kt。3.5 enableEdgeToEdge边缘到边缘布局请求 WebView 将 padding 设置为 0 或安全绘制区域safeDrawing insetsimport { enableEdgeToEdge } from kernelsu; enableEdgeToEdge(true);该能力默认关闭有两种方式自动启用并在 CSS 中获得 insets 值在 CSS 中import https://mui.kernelsu.org/internal/insets.css;在 HTML 中link relstylesheet typetext/css href/internal/insets.css /引入后管理器会自动开启 insets 支持并动态下发状态栏/导航栏高度对应的 CSS 变量帮助页面在全面屏、手势导航等环境下正确避让系统 UI。3.6 toast原生提示显示一条 Toast 消息短时长import { toast } from kernelsu; toast(Hello, world!);3.7 moduleInfo获取模块信息获取当前模块的信息JSON 字符串包含模块 id、版本、module.prop中的自定义字段以及moduleDir模块目录路径import { moduleInfo } from kernelsu; // print moduleId in console console.log(moduleInfo());管理器侧实现会遍历已安装模块列表按当前 WebUI 所属模块的 id 匹配合并返回module.prop的全部键值并附带moduleDir见 WebViewInterface.kt。3.8 listPackages列出已安装应用返回包名数组可按类型过滤import { listPackages } from kernelsu; // list user packages const packages listPackages(user);type取值user用户应用、system系统应用、all全部。配套能力当listPackagesAPI 可用时可以使用ksu://icon/{packageName}协议获取应用图标img.src ksu://icon/ packageName;管理器通过WebViewClient.shouldInterceptRequest拦截ksu://icon/请求从应用缓存中加载图标并返回 PNG 响应见 WebViewHelper.kt。注意listPackages基于管理器的应用列表实现WebViewInterface.kt首次使用时需要管理器完成应用列表的加载。3.9 getPackagesInfo批量获取应用详情传入包名数组返回PackagesInfo对象数组import { getPackagesInfo } from kernelsu; const packages getPackagesInfo([com.android.settings, com.android.shell]);PackagesInfo 字段字段类型说明packageNamestring应用包名versionNamestring应用版本名versionCodenumber应用版本号appLabelstring应用显示名称isSystemboolean是否为系统应用uidnumber应用 UID对于不存在或无法访问的包名返回对象会包含error字段说明原因见 WebViewInterface.kt。3.10 exit退出 WebUI关闭当前 WebUI 页面import { exit } from kernelsu; exit();管理器侧通过state.requestExit()触发Close事件关闭页面并释放 root shell见 WebViewInterface.kt 与 WebUIState.kt。四、一个完整的 WebUI 模块示例综合以上 API一个带交互界面的模块可以这样组织my-module/ |-- module.prop # idmy_module, name..., webuiIcon... -- webroot |-- index.html |-- style.css -- app.jsindex.html引入kernelsu库通过 npm 打包后引入 bundle并在app.js中调用系统 APIimport { exec, moduleInfo, toast } from kernelsu; async function init() { // 读取设备型号 const { errno, stdout } await exec(getprop ro.product.model); document.getElementById(model).textContent stdout.trim(); // 显示模块信息 console.log(moduleInfo()); } document.getElementById(apply).addEventListener(click, async () { const { errno, stderr } await exec(setprop my.module.enabled 1); if (errno 0) { toast(已启用); } else { toast(执行失败: stderr); } }); init();对于这种简单的单页面场景官方推荐使用 parceljs 打包零配置、开箱即用一条yarn add kernelsu加parcel build即可产出可在 WebView 中加载的静态资源将产物输出到webroot目录即可。如果你是前端专家或有其他偏好也可以自由选择 Vite、webpack 等任意构建工具——KernelSU 对 Web 技术栈没有限制。五、注意事项与最佳实践5.1 数据持久化localStorage 的边界你可以像在普通网页中一样使用localStorage存储数据但要记住管理器应用被卸载时这些数据会随之消失WebView 数据随应用数据清除。如果需要长期持久化存储应自行将数据保存到模块的特定目录例如/data/adb/modules/moduleId/下的自有文件或通过exec写入用户自定义目录。5.2 安全与权限边界root shell 能力exec/spawn均在 root shell 中运行拥有系统最高权限。模块作者应最小化命令执行面避免把用户输入直接拼进命令防止命令注入并对命令输出做合理的转义处理SELinux 上下文webroot目录的权限与 SELinux 上下文由 KernelSU 在安装时自动设置不要手动修改否则可能破坏 WebView 的资源读取目录隔离管理器通过SuFilePathHandler只暴露webroot目录并校验请求路径不能越出该目录见 SuFilePathHandler.java但模块页面内部引用的资源仍应只放在webroot内。5.3 兼容性与调试管理器为 WebView 开启了 JavaScript 与 DOM Storage但allowFileAccess为 false页面应通过mui.kernelsu.org虚拟域名加载资源而不是依赖file://协议见 WebViewHelper.kt管理器的设置中可开启 Web 调试enable_web_debugging方便通过 Chrome DevTools 远程调试页面见 WebViewHelper.kt。六、延伸阅读模块开发总览模块目录结构、module.prop字段含webuiIcon图标与生命周期模块配置通过 WebUI 或 action 脚本管理模块配置项manage.*的用户偏好存储机制kernelsu库源码与类型声明js/index.js、js/index.d.ts管理器 WebUI 实现WebViewInterface.kt、WebViewHelper.kt、SuFilePathHandler.java。如果你觉得现有 API 无法满足需求或使用不便欢迎在 KernelSU 的 GitHub Issues 中提出建议推动 WebUI 能力持续演进。【免费下载链接】KernelSUA Kernel based root solution for Android项目地址: https://gitcode.com/GitHub_Trending/ke/KernelSU创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表