211 Commits

Author SHA1 Message Date
ShirkNeko
9919d573fe Update the build workflow to rename build tasks and add new build dependencies 2025-04-19 20:01:41 +08:00
ShirkNeko
d73af38bf4 Update build scripts, change version information and delete workflow files that are no longer in use 2025-04-19 19:55:13 +08:00
Re*Index. (ot_inc)
dfe7852c25 fix typo (#50)
* Update Japanese!

* Fix typo

* Fix typo 2

* Update strings.xml

* Update strings.xml

* Update Japanese.

* Update strings.xml

* Add Japanese README

* Update README-ja.md

* Update README-ja.md

* Update README-ja.md

* Update README-ja.md

* Update README-ja.md

* Update README-en.md

* Update README-ja.md

* Update README-en.md

* Update README.md

* Update README-ja.md

* fix typo

* Update strings.xml
2025-04-19 16:03:25 +08:00
Re*Index. (ot_inc)
abe6184f63 Update READMEs. (#49)
* Update Japanese!

* Fix typo

* Fix typo 2

* Update strings.xml

* Update strings.xml

* Update Japanese.

* Update strings.xml

* Add Japanese README

* Update README-ja.md

* Update README-ja.md

* Update README-ja.md

* Update README-ja.md

* Update README-ja.md

* Update README-en.md

* Update README-ja.md

* Update README-en.md

* Update README.md

* Update README-ja.md
2025-04-18 20:16:31 +08:00
Re*Index. (ot_inc)
e8afb2143b Japanese Translate (#48)
* Update Japanese!

* Fix typo

* Fix typo 2

* Update strings.xml

* Update strings.xml

* Update Japanese.

* Update strings.xml

* Add Japanese README

* Update README-ja.md

* Update README-ja.md
2025-04-17 17:23:26 +08:00
ShirkNeko
7e1c363bad Add module uninstallation confirmation dialog and file type validation message 2025-04-16 14:36:27 +08:00
Kousei
bcf6809deb Add files via upload (#47) 2025-04-16 13:21:43 +08:00
ShirkNeko
bce5f6cf61 Fix GitHub link in About card 2025-04-15 15:09:36 +08:00
ShirkNeko
5495eebb38 Update version number 2025-04-14 20:18:52 +08:00
ShirkNeko
d255801666 Update version number 2025-04-14 20:18:24 +08:00
ShirkNeko
44f2601126 Update version number 2025-04-14 20:18:10 +08:00
ShirkNeko
3a7366c4bc Update version number 2025-04-14 20:18:04 +08:00
ShirkNeko
5da289714d Update version number 2025-04-14 20:17:51 +08:00
ShirkNeko
a18d718744 Update version number 2025-04-14 20:17:44 +08:00
ShirkNeko
c90fc461d9 Update version number 2025-04-14 20:17:37 +08:00
ShirkNeko
e80fbe8934 Update version number 2025-04-14 20:17:32 +08:00
ShirkNeko
e0650ade4f Update version number 2025-04-14 20:17:16 +08:00
ShirkNeko
378b8458f2 Opt device disabling logic in HomeScreen 2025-04-14 19:30:30 +08:00
ShirkNeko
182028d9ea Updating the downloader to use the new GitHub publishing interface 2025-04-14 19:06:54 +08:00
ShirkNeko
0c3b8e7610 1 2025-04-14 18:46:51 +08:00
ShirkNeko
332fdcd2a7 Fix KPM module loading calls to explicitly use namespaces 2025-04-14 15:55:35 +08:00
ShirkNeko
aa20d04d3a Refactoring KPM module loading logic, removing existing KPM loading functions, simplifying code and enhancing error handling 2025-04-14 15:51:47 +08:00
ShirkNeko
949106bc09 Refactoring KPM module loading logic, adding modification event handling, improving error handling 2025-04-14 15:39:43 +08:00
ShirkNeko
67babc2858 Enhanced KPM module loading logic, added filename validation and error handling 2025-04-14 15:35:11 +08:00
ShirkNeko
d087ec510e Ksud: refactor KPM event handling logic, add error handling and ensure KPM catalog exists 2025-04-14 15:26:30 +08:00
gggggdw
c2ed3da87c Update Engish translation (#46) 2025-04-14 14:39:04 +08:00
ShirkNeko
f2d159f732 Updated README file to add KPM support information and remove duplicate feature descriptions 2025-04-14 14:38:27 +08:00
ShirkNeko
468fc2207d Fixed integration note on susfs in README.md 2025-04-14 02:05:34 +08:00
Qumolama.d
eced8bae82 Fix typos and translations in README.md and README-en.md (#23)
* Fix typos in README.md

* Fix various translation error in README-en.md

* Update links in README-en.md
2025-04-14 02:04:35 +08:00
Wang Han
e61ecb3963 Fix opuls -> oplus typo (#2536) 2025-04-13 18:14:27 +08:00
Kousei
304f4f8b2c Update Vietnamese language translation (#44)
* Add files via upload

* Add files via upload
2025-04-13 16:00:30 +08:00
ShirkNeko
48888087e1 Fix branch checking in build-manager.yml by changing 'susfs' to 'main' 2025-04-13 15:41:33 +08:00
crazymrli
75a70e70be Merge pull request #43 from ShirkNeko/dev
Dev
2025-04-13 15:25:59 +08:00
liankong
cf825f912c 添加KPM模板说明 2025-04-13 15:22:22 +08:00
liankong
1944a49fd8 添加super_access对task_struct的支持 2025-04-13 14:41:06 +08:00
liankong
657f343f5c 添加super_access对task_struct的支持 2025-04-13 14:18:33 +08:00
ShirkNeko
74bb90b3d8 Update build-lkm.yml to force uploading inputs to be required, default value changed to true 2025-04-13 02:26:26 +08:00
Kousei
8bf9828c80 Add files via upload (#42) 2025-04-13 01:43:55 +08:00
ShirkNeko
06324def38 更新super_access.c to remove redundant macro definitions and optimize conditional compilation; use resource strings instead of hard-coded text in MoreSettings.kt; add theme color string resource in strings.xml 2025-04-13 01:33:40 +08:00
ShirkNeko
70259a5ec5 清理super_access.c中的多余空行 2025-04-12 17:00:14 +08:00
ShirkNeko
a63057c594 Merge branch 'dev' into main 2025-04-12 16:55:20 +08:00
ShirkNeko
48d5270611 Add super_access function to support dynamic access to structures and members 2025-04-12 16:52:13 +08:00
ShirkNeko
712d0f3342 更新super_access.c以支持不同内核版本的结构体成员定义 2025-04-12 16:29:13 +08:00
ShirkNeko
d6084aeca1 在super_access.c中添加对linux/version.h的引用,并定义KERNEL_VERSION_6_6宏,以支持不同内核版本的条件编译 2025-04-12 16:09:13 +08:00
ShirkNeko
562b9624d7 移除对linux/nsproxy.h的引用,并添加对../fs/mount.h的引用 2025-04-12 15:49:00 +08:00
ShirkNeko
a68d5e8bbe 在super_access.c中添加对linux/nsproxy.h的引用 2025-04-12 15:02:22 +08:00
ShirkNeko
d45aa8197e Remove unnecessary header file fs/mount.h and use linux/mount.h instead. 2025-04-12 14:54:10 +08:00
ShirkNeko
314d3ef97a 更新Makefile以使用super_access.o替代super_access.c 2025-04-12 14:44:50 +08:00
liankong
e3750ccd51 完善super_access 2025-04-12 10:35:08 +08:00
liankong
a712efe9d8 添加super_access机制 2025-04-12 00:34:37 +08:00
ShirkNeko
2266362e24 Changing the package name 2025-04-12 00:33:46 +08:00
ShirkNeko
b7056b5baa Changing the package name 2025-04-12 00:33:29 +08:00
liankong
8bd07bf56c Merge branch 'dev' of https://github.com/ShirkNeko/SukiSU-Ultra into dev 2025-04-11 15:19:48 +08:00
liankong
569183efe9 添加super_access机制 2025-04-11 15:19:18 +08:00
crazymrli
937cf25e9b Merge pull request #38 from ShirkNeko/main
Merge to dev
2025-04-11 15:17:01 +08:00
Kousei
e2b6617577 Add String-vi (#36)
Add Vietnamese language translation
2025-04-10 23:42:28 +08:00
Re*Index. (ot_inc)
8323d6394b Fix Japanese Translate! (#35)
* Update Japanese!

* Fix typo

* Fix typo 2

* Update strings.xml

* Update strings.xml
2025-04-10 23:39:02 +08:00
Re*Index. (ot_inc)
e31b892a20 Update Japanese Translate! (#34)
* Update Japanese!

* Fix typo
2025-04-10 15:48:24 +08:00
ShirkNeko
65b1518e26 Opt KPM module installation to support URL-encoded filenames and extract module IDs for dynamically constructed names.
This will fix the flashback issue due to path issue

Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-04-10 01:25:59 +08:00
ShirkNeko
865dbd3799 feat: Load Existing KPM Modules on KPM Monitor Startup 2025-04-08 19:03:29 +08:00
ShirkNeko
7de9d7967a Add embedded and load-optimized KPM module installation and uninstallation prompt messages 2025-04-08 18:56:14 +08:00
ShirkNeko
aa2d2454e1 feat: 引入anyhow库以增强错误处理能力 2025-04-07 22:33:45 +08:00
ShirkNeko
b850336872 feat: Optimize KPM file processing logic, use is_some_and method to simplify extension checking, and enhance readability and stability of event processing. 2025-04-07 22:28:22 +08:00
ShirkNeko
138dec35c7 feat: Add KPM module management function, support loading, unloading and deleting all KPM modules. 2025-04-07 22:13:21 +08:00
ShirkNeko
569fffa962 feat: 引入notify库以支持KPM目录的文件监控 2025-04-07 21:40:25 +08:00
ShirkNeko
2a283e6793 feat: 修复unload_kpm函数的定义,添加缺失的fn关键字 2025-04-07 21:35:44 +08:00
ShirkNeko
73a7ba3ac9 feat: 将KPM相关函数公开以支持外部调用 2025-04-07 21:32:41 +08:00
ShirkNeko
c0c4ea9f86 feat: 引入KPM模块以支持文件系统数据处理 2025-04-07 21:26:08 +08:00
ShirkNeko
a13179cd09 feat: 添加KPM模块以支持KPM相关功能 2025-04-07 21:21:36 +08:00
ShirkNeko
de089b7b73 feat: Adding a KPM monitor to handle KPM file creation and deletion events 2025-04-07 21:18:14 +08:00
ShirkNeko
1b700fb8e0 Merge pull request #33 from ShirkNeko/dev
增加了两个必要的兼容函数,以确保能够运行Peekaboo.KPM
2025-04-07 19:48:03 +08:00
liankong
f0febf13f2 增加了两个必要的兼容函数,以确保能够运行Peekaboo.KPM 2025-04-07 19:43:06 +08:00
ShirkNeko
a002967a92 Refactor dialog implementation, replace ListDialog with AlertDialog to optimize user interaction experience.
This will improve fixing KMI and restoring init_boot image style issues
2025-04-07 16:56:40 +08:00
ShirkNeko
1167b20d89 Merge pull request #32 from ShirkNeko/dev 2025-04-07 15:40:48 +08:00
ShirkNeko
297ff3ae90 Dev (#31)
* 添加安装工具类,并增加把kpmmgr安装到/data/adb/ksu/bin的功能,方便模块调用

* Update README

* Update README

---------

Co-authored-by: liankong <xhsw.new@qq.com>
2025-04-07 15:38:04 +08:00
liankong
a39e2ce15a Update README 2025-04-07 15:36:46 +08:00
liankong
78eda275d6 Update README 2025-04-07 15:35:21 +08:00
ShirkNeko
1fce0fd77d 添加安装工具类,并增加把kpmmgr安装到/data/adb/ksu/bin的功能,方便模块调用 (#30)
Co-authored-by: liankong <xhsw.new@qq.com>
2025-04-07 15:17:46 +08:00
liankong
c942393f21 添加安装工具类,并增加把kpmmgr安装到/data/adb/ksu/bin的功能,方便模块调用 2025-04-07 15:09:05 +08:00
ShirkNeko
040cc30e73 manager: Updates the sub-levels and patch levels of the Android version 2025-04-06 13:54:57 +08:00
ShirkNeko
6b75ffc928 Delete .github/dependabot.yml 2025-04-06 00:24:46 +08:00
WenHao2130
a7f1b21f91 manager: move more_settings_simplicity_mode to custom_settings (#28)
Signed-off-by: WenHao2130 <WenHao2130@outlook.com>
2025-04-05 15:28:09 +08:00
WenHao2130
c8f7d9d5bc manager: beta -> Beta (#27)
Signed-off-by: WenHao2130 <WenHao2130@outlook.com>
2025-04-05 11:46:41 +08:00
WenHao2130
2520d45dc4 manager: add some animation (#26)
Signed-off-by: WenHao2130 <98936399+WenHao2130@users.noreply.github.com>
2025-04-05 02:59:25 +08:00
WenHao2130
8bdf8d98c3 manager: allow hide susfs status information
Signed-off-by: WenHao2130 <WenHao2130@outlook.com>
2025-04-04 21:32:06 +08:00
WenHao2130
04025f3d32 manager: allow hide some other information
Signed-off-by: WenHao2130 <WenHao2130@outlook.com>
2025-04-04 21:32:06 +08:00
ShirkNeko
4c7ed9c8ee 修正 SukiSU Ultra 展望字符串中的空格格式 2025-04-04 18:01:57 +08:00
ShirkNeko
c36f8b0df3 Opt the transparency slider display logic with AnimatedVisibility and update the formatting in the Chinese string resource.
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-04-04 17:51:27 +08:00
WenHao2130
5ccec93940 manager: Use AnimatedVisibility for Exclude Modifications SwitchItem
Co-authored-by: Light summer <93428659+lightsummer233@users.noreply.github.com>
Co-authored-by: WenHao2130 <WenHao2130@outlook.com>
Signed-off-by: WenHao2130 <WenHao2130@outlook.com>
2025-04-04 17:24:36 +08:00
ShirkNeko
14b15e18f3 Update README.md 2025-04-04 15:49:21 +08:00
ShirkNeko
3e6f3b4d80 Merge pull request #22 from WenHao2130/main
ksud: update banner
2025-04-04 15:04:48 +08:00
WenHao2130
470b3106cb ksud: update banner
Signed-off-by: WenHao2130 <WenHao2130@outlook.com>
2025-04-04 15:00:55 +08:00
ShirkNeko
e0074bc3ab Docs: simplify susfs branch usage instructions, remove redundant information 2025-04-04 14:27:07 +08:00
ShirkNeko
8d04ecdc52 Adjust card transparency and shadow settings to improve visualization 2025-04-04 13:44:21 +08:00
ShirkNeko
c83b1e88b9 Merge pull request #19 from ShirkNeko/dev
Dev
2025-04-03 23:34:01 +08:00
ShirkNeko
0446cc499e Merge branch 'main' into dev 2025-04-03 23:33:04 +08:00
ShirkNeko
8a6507e834 Manager: Add clean mode switch and option to hide kernel version number 2025-04-03 23:23:05 +08:00
ShirkNeko
09893b9472 Merge pull request #18 from WenHao2130/main
manager: optimize layout
2025-04-03 16:07:38 +08:00
WenHao2130
13d2290205 manager: optimize layout
Signed-off-by: WenHao2130 <WenHao2130@outlook.com>
2025-04-03 15:59:00 +08:00
ShirkNeko
46e4c85563 Add missing package names 2025-04-02 22:58:13 +08:00
ShirkNeko
d925036bd6 Optimize the error handling logic for loading and unloading modules, this will fix the file type error, and more detail handling-2 2025-04-02 17:59:59 +08:00
ShirkNeko
fbf2799674 Opt the error handling logic for loading and unloading modules, this will fix the file type error, and more detail handling 2025-04-02 17:59:29 +08:00
liankong
12c7558b91 给WebUI添加对KPM的操控支持 2025-04-02 14:03:50 +08:00
ShirkNeko
d55af76260 Optimize the error handling logic for loading and unloading modules, this will fix the file type error, and more detail handling-2 2025-04-02 00:36:08 +08:00
ShirkNeko
e4c70e2efb Opt the error handling logic for loading and unloading modules, this will fix the file type error, and more detail handling 2025-04-02 00:35:55 +08:00
ShirkNeko
a971fee132 Revert "Add KPM module count display to the main page and update related string resources"
This reverts commit a45e0f78ef.
2025-04-01 17:27:51 +08:00
ShirkNeko
a45e0f78ef Add KPM module count display to the main page and update related string resources 2025-04-01 17:24:13 +08:00
ShirkNeko
bf07dcb9ea Merge pull request #14 from ShirkNeko/dev
Add input dialogs to the KPM module to optimize control logic and dis…
2025-04-01 17:05:09 +08:00
ShirkNeko
e5f5b8f831 Add input dialogs to the KPM module to optimize control logic and display Snackbar prompts for operation results. 2025-04-01 17:04:00 +08:00
ShirkNeko
a80513fc50 Merge pull request #13 from ShirkNeko/dev
Dev
2025-04-01 16:18:17 +08:00
ShirkNeko
f71de1742a 优化KPM模块列表的获取逻辑,添加定时刷新功能,并调整空状态显示 2025-04-01 16:16:56 +08:00
ShirkNeko
57c65fdcda 移除调试签名配置,优化KPM版本显示逻辑,添加KPM控制成功和失败的提示字符串 2025-04-01 16:09:18 +08:00
ShirkNeko
0a9cf5f9aa Merge branch 'dev' of https://github.com/ShirkNeko/SukiSU-Ultra into dev 2025-04-01 15:56:40 +08:00
ShirkNeko
08fdf2bdad 为KPM模块添加控制功能,显示操作成功或失败的Snackbar提示,并优化模块信息获取逻辑 2025-04-01 15:56:30 +08:00
liankong
690c6bac38 Merge branch 'dev' of https://github.com/ShirkNeko/SukiSU-Ultra into dev 2025-04-01 15:54:03 +08:00
liankong
af81308097 修复获取不到完整信息的bug 2025-04-01 15:53:58 +08:00
ShirkNeko
90b79f5c04 将KPM模块的Snackbar持续时间从长改为短,以改善用户体验 2025-04-01 15:04:09 +08:00
liankong
0c5dcec7bc 尝试修复KPM信息解析 2025-04-01 14:57:22 +08:00
ShirkNeko
a30dfbc15d 优化KPM模块加载和卸载逻辑,改进错误处理,增强模块信息获取功能 2025-04-01 14:34:24 +08:00
ShirkNeko
52f3335977 Merge pull request #12 from ShirkNeko/dev
Dev
2025-04-01 13:56:08 +08:00
liankong
c8b3e953ad Merge branch 'dev' of https://github.com/ShirkNeko/SukiSU-Ultra into dev 2025-04-01 12:29:06 +08:00
liankong
e9d0526e1b 1 2025-04-01 12:28:59 +08:00
ShirkNeko
408c3be675 优化Kpm模块相关函数的返回类型,改为Boolean或Int以提高可读性和准确性 2025-04-01 02:40:58 +08:00
ShirkNeko
f67f16733f 优化KPM模块加载逻辑,更新相关字符串资源,调整KPM版本显示条件 2025-03-31 23:22:03 +08:00
ShirkNeko
25a173ad7b Merge pull request #8 from ShirkNeko/dev
Dev
2025-03-31 22:34:09 +08:00
ShirkNeko
75ec88f7a7 Merge branch 'dev' of https://github.com/ShirkNeko/SukiSU-Ultra into dev 2025-03-31 22:33:23 +08:00
ShirkNeko
e9c5ffb430 优化HomeScreen,移除KPM状态检查,调整ContributionCard显示逻辑 2025-03-31 22:33:20 +08:00
ShirkNeko
cb2cdaed12 删除KernelConfigUtils.kt文件 2025-03-31 22:32:57 +08:00
liankong
029c7f1e2a Merge branch 'dev' of https://github.com/ShirkNeko/SukiSU-Ultra into dev 2025-03-31 22:32:48 +08:00
liankong
5a8d6895fa 添加加载成功提示 2025-03-31 22:32:42 +08:00
ShirkNeko
bb11e23006 Merge pull request #7 from ShirkNeko/dev
Dev
2025-03-31 22:12:28 +08:00
ShirkNeko
37b00d49c8 添加KPM状态检查功能,并更新相关UI文本 2025-03-31 22:11:52 +08:00
liankong
8055aed507 Merge branch 'dev' of https://github.com/ShirkNeko/SukiSU-Ultra into dev 2025-03-31 20:28:31 +08:00
liankong
e7cef05c6a 修复 2025-03-31 20:28:26 +08:00
ShirkNeko
313746b578 Merge branch 'dev' of https://github.com/ShirkNeko/SukiSU-Ultra into dev 2025-03-31 20:27:40 +08:00
ShirkNeko
a7c557222c 修复Makefile中KPM状态信息的重复输出 2025-03-31 20:27:28 +08:00
liankong
9c902fb264 2 2025-03-31 20:24:56 +08:00
liankong
079f74d960 更改返回值处理方式 2025-03-31 19:47:22 +08:00
liankong
00d7de5276 1 2025-03-31 19:07:07 +08:00
liankong
3e928365de 1 2025-03-31 18:51:20 +08:00
liankong
47ba174fb1 尝试支持clang关闭优化 2025-03-31 18:31:21 +08:00
liankong
9446296daa 尝试禁用对壳函数的优化 2025-03-31 18:19:55 +08:00
liankong
7175a6fa7d Merge branch 'dev' of https://github.com/ShirkNeko/SukiSU-Ultra into dev 2025-03-31 18:08:20 +08:00
liankong
1b06f7d317 关掉所有对壳函数的优化 2025-03-31 18:08:09 +08:00
ShirkNeko
c739bf6bfb 更新应用名称为 SukiSU Ultra,并修改相关提示信息为 SukiSU beta版管理器 2025-03-31 17:30:51 +08:00
liankong
dec9a72b41 增加壳函数信息打印 2025-03-31 17:17:21 +08:00
liankong
593cbaa067 添加壳函数防止被inline优化掉 2025-03-31 16:55:51 +08:00
liankong
fb8906e371 4 2025-03-31 16:19:52 +08:00
liankong
df943250ac 3 2025-03-31 16:07:29 +08:00
liankong
5a522a1489 kpmmgr 2025-03-31 16:01:22 +08:00
liankong
0b0d64b9d0 Merge branch 'dev' of https://github.com/ShirkNeko/SukiSU-Ultra into dev 2025-03-31 16:00:21 +08:00
liankong
18876e8a69 修复kpmmgr 2025-03-31 16:00:09 +08:00
ShirkNeko
b668378e23 重命名函数并更新命令以获取KPM版本信息 2025-03-31 15:52:57 +08:00
liankong
9de2c09a27 更新kpmmgr 2025-03-31 15:40:28 +08:00
liankong
6b3d2bef12 1 2025-03-31 15:22:30 +08:00
liankong
344ed41bc7 Merge branch 'dev' of https://github.com/ShirkNeko/SukiSU-Ultra into dev 2025-03-31 15:13:24 +08:00
liankong
2e711c3ac9 3 2025-03-31 15:13:13 +08:00
ShirkNeko
1bf4486cf1 Merge pull request #6 from ShirkNeko/dev
Dev
2025-03-31 14:53:34 +08:00
ShirkNeko
cb116286ed Manager: Fix the problem that the contrast of key color is too light 2025-03-31 14:50:27 +08:00
ShirkNeko
7f0ae95dfb Improve font color blending and contrast in dark mode.
Fix login issue (#19).

Modify styles for more settings.

Do not show the status of susfs when using lkm mode.

Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-03-31 14:50:20 +08:00
ShirkNeko
fe7ec9dcf5 docs: update README to clarify branch usage and support for non-GKI devices 2025-03-31 14:50:07 +08:00
ShirkNeko
d88eccdda3 docs: add acknowledgment for yspbwx2010's support 2025-03-31 14:49:53 +08:00
liankong
78fe01d9a4 2 2025-03-31 14:30:43 +08:00
liankong
60cb41c76b 1 2025-03-31 14:04:04 +08:00
liankong
af78f3bac4 转变为仅在内核留下Stub在外部加载KPM 2025-03-31 13:55:48 +08:00
liankong
fff86dcc8d 4 2025-03-30 21:06:56 +08:00
liankong
5ec053ca34 3 2025-03-30 20:39:23 +08:00
liankong
e9f1631b06 根据KP修复 2025-03-30 20:29:43 +08:00
ShirkNeko
3705993330 Merge pull request #5 from ShirkNeko/dev
Dev
2025-03-30 20:21:37 +08:00
liankong
3db338da3e 2 2025-03-30 20:10:50 +08:00
liankong
d126d0f5b8 1 2025-03-30 20:03:40 +08:00
liankong
549adebb30 Merge branch 'dev' of https://github.com/ShirkNeko/SukiSU-Ultra into dev 2025-03-30 19:56:18 +08:00
liankong
40bada35c6 尝试修复 2025-03-30 19:56:04 +08:00
ShirkNeko
074903a299 更新支持的非官方管理器信息,添加 udochina 2025-03-30 19:50:14 +08:00
ShirkNeko
328bee94e5 Merge branch 'dev' of https://github.com/ShirkNeko/SukiSU-Ultra into dev 2025-03-30 19:45:12 +08:00
ShirkNeko
0db25f14f1 添加 KPM 状态信息输出,并更新支持的非官方管理器信息 2025-03-30 19:45:10 +08:00
liankong
877e4f9416 修复调用错误 2025-03-30 19:42:09 +08:00
liankong
8b3e864ffa Merge branch 'dev' of https://github.com/ShirkNeko/SukiSU-Ultra into dev 2025-03-30 19:38:50 +08:00
liankong
5f5f677b7b 修复 2025-03-30 19:38:19 +08:00
ShirkNeko
3933d83d3e 更新 KPM 配置选项名称为 SukiSU,并修改帮助信息以反映新功能和潜在影响 2025-03-30 19:37:14 +08:00
liankong
4abd35fb44 10 2025-03-30 19:27:27 +08:00
liankong
e68afb04eb 9 2025-03-30 19:18:38 +08:00
liankong
bf2be96b29 8 2025-03-30 19:17:42 +08:00
liankong
b0b5048b01 7 2025-03-30 18:57:51 +08:00
liankong
aff69af690 6 2025-03-30 18:56:28 +08:00
liankong
c1d156cd6b 5 2025-03-30 18:50:33 +08:00
liankong
e58e00be9d Merge branch 'dev' of https://github.com/ShirkNeko/SukiSU-Ultra into dev 2025-03-30 18:36:01 +08:00
liankong
440fe972f4 4 2025-03-30 18:35:47 +08:00
ShirkNeko
e24588b961 Merge branch 'dev' of https://github.com/ShirkNeko/SukiSU-Ultra into dev 2025-03-30 18:31:09 +08:00
ShirkNeko
57c8d69e83 Manager: simplify and fix SUSFS version display errors
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-03-30 18:30:53 +08:00
liankong
79c0bebcf5 3 2025-03-30 18:30:44 +08:00
ShirkNeko
321c9c20d5 Manager: simplify and fix SUSFS version display errors
Signed-off-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
2025-03-30 18:30:04 +08:00
liankong
f6134b47da 2 2025-03-30 18:25:40 +08:00
liankong
8c282b28a0 1 2025-03-30 18:24:56 +08:00
liankong
656cd11876 根据KP修复 2025-03-30 18:10:14 +08:00
liankong
5e77c08872 尝试修复: 内存段缓存/权限问题? 2025-03-30 17:56:53 +08:00
liankong
1090f64117 修复符号引用问题 2025-03-30 17:32:35 +08:00
liankong
470aaa29dc 兼容修复 2025-03-30 17:21:33 +08:00
liankong
c6664af45b 修复 2025-03-30 17:15:25 +08:00
liankong
d6b0ce2565 修复堆栈支持 2025-03-30 17:09:51 +08:00
liankong
770c9632ae 添加panic时打印出对应KPM信息的情况 2025-03-30 16:52:44 +08:00
liankong
cd60773d73 修复编译规则 2025-03-30 16:20:35 +08:00
liankong
06cdd92129 BypassCFI 2025-03-30 16:19:36 +08:00
liankong
315df33bd6 添加跳过CFI检查的机制 2025-03-30 16:14:23 +08:00
liankong
f990bda4e5 绕过CFI限制 2025-03-30 15:37:09 +08:00
liankong
b755ad3602 2 2025-03-30 15:22:40 +08:00
liankong
b060b2827e 1 2025-03-30 15:13:16 +08:00
liankong
73493b288f 按照KernelPatch原代码修复 2025-03-30 14:58:08 +08:00
liankong
87640fb824 3 2025-03-30 02:56:54 +08:00
liankong
fb0a48f9db Merge branch 'dev' of https://github.com/ShirkNeko/SukiSU-Ultra into dev 2025-03-30 02:51:30 +08:00
liankong
acc8670aa9 2 2025-03-30 02:51:20 +08:00
94 changed files with 3069 additions and 2985 deletions

View File

@@ -1,28 +0,0 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: daily
groups:
actions:
patterns:
- "*"
- package-ecosystem: cargo
directory: userspace/ksud
schedule:
interval: daily
allow:
- dependency-type: "all"
groups:
crates:
patterns:
- "*"
- package-ecosystem: gradle
directory: manager
schedule:
interval: daily
groups:
maven:
patterns:
- "*"

View File

@@ -1,137 +0,0 @@
name: GKI Kernel Build
on:
workflow_call:
inputs:
version_name:
required: true
type: string
description: >
With SUBLEVEL of kernel,
for example: android12-5.10.66
arch:
required: true
type: string
description: >
Build arch: aarch64/x86_64
debug:
required: false
type: boolean
default: true
manifest_name:
required: false
type: string
description: >
Local repo manifest xml path,
typically for AVD kernel build.
secrets:
BOOT_SIGN_KEY:
required: false
CHAT_ID:
required: false
BOT_TOKEN:
required: false
MESSAGE_THREAD_ID:
required: false
jobs:
build:
name: Build ${{ inputs.version_name }}
runs-on: ubuntu-22.04
steps:
- name: Maximize build space
uses: easimon/maximize-build-space@master
with:
root-reserve-mb: 8192
temp-reserve-mb: 2048
remove-dotnet: 'true'
remove-android: 'true'
remove-haskell: 'true'
remove-codeql: 'true'
- uses: actions/checkout@v4
with:
path: KernelSU
fetch-depth: 0
- name: Setup need_upload
id: need_upload
run: |
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
echo "UPLOAD=true" >> $GITHUB_OUTPUT
else
echo "UPLOAD=false" >> $GITHUB_OUTPUT
fi
- name: Setup kernel source
run: |
echo "Free space:"
df -h
cd $GITHUB_WORKSPACE
sudo apt-get install repo -y
mkdir android-kernel && cd android-kernel
repo init --depth=1 -u https://android.googlesource.com/kernel/manifest -m "$GITHUB_WORKSPACE/KernelSU/.github/manifests/${{ inputs.manifest_name }}" --repo-rev=v2.16
repo --version
repo --trace sync -c -j$(nproc --all) --no-tags
df -h
- name: Setup KernelSU
env:
PATCH_PATH: ${{ inputs.patch_path }}
IS_DEBUG_KERNEL: ${{ inputs.debug }}
run: |
cd $GITHUB_WORKSPACE/android-kernel
echo "[+] KernelSU setup"
GKI_ROOT=$(pwd)
echo "[+] GKI_ROOT: $GKI_ROOT"
echo "[+] Copy KernelSU driver to $GKI_ROOT/common/drivers"
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
echo "[+] Add KernelSU driver to Makefile"
DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile
DRIVER_KCONFIG=$GKI_ROOT/common/drivers/Kconfig
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE"
grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG"
echo "[+] Apply KernelSU patches"
cd $GKI_ROOT/common/ && git apply $GITHUB_WORKSPACE/KernelSU/.github/patches/$PATCH_PATH/*.patch || echo "[-] No patch found"
if [ "$IS_DEBUG_KERNEL" = "true" ]; then
echo "[+] Enable debug features for kernel"
printf "\nccflags-y += -DCONFIG_KSU_DEBUG\n" >> $GITHUB_WORKSPACE/KernelSU/kernel/Makefile
fi
repo status
echo "[+] KernelSU setup done."
cd $GITHUB_WORKSPACE/KernelSU
VERSION=$(($(git rev-list --count HEAD) + 10200))
echo "VERSION: $VERSION"
echo "kernelsu_version=$VERSION" >> $GITHUB_ENV
- name: Make working directory clean to avoid dirty
working-directory: android-kernel
run: |
rm common/android/abi_gki_protected_exports_* || echo "No protected exports!"
git config --global user.email "bot@kernelsu.org"
git config --global user.name "KernelSUBot"
cd common/ && git add -A && git commit -a -m "Add KernelSU"
repo status
- name: Build kernel
working-directory: android-kernel
run: |
if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then
export KSU_EXPECTED_SIZE=${{ vars.EXPECTED_SIZE }}
export KSU_EXPECTED_HASH=${{ vars.EXPECTED_HASH }}
fi
tools/bazel run --config=fast --config=stamp --lto=thin //common-modules/virtual-device:virtual_device_${{ inputs.arch }}_dist -- --dist_dir=dist
NAME=kernel-${{ inputs.arch }}-avd-${{ inputs.version_name }}-${{ env.kernelsu_version }}
TARGET_IMAGE=dist/bzImage
if [ ! -e $TARGET_IMAGE ]; then
TARGET_IMAGE=dist/Image
fi
mv $TARGET_IMAGE $NAME
echo "file_path=android-kernel/$NAME" >> $GITHUB_ENV
- name: Upload Kernel
uses: actions/upload-artifact@v4
with:
name: kernel-${{ inputs.arch }}-avd-${{ inputs.version_name }}-${{ env.kernelsu_version }}
path: "${{ env.file_path }}"

View File

@@ -1,74 +0,0 @@
name: Build LKM for KernelSU
on:
workflow_call:
inputs:
upload:
required: false
type: boolean
default: false
description: "Whether to upload to branch"
secrets:
# username:github_pat
TOKEN:
required: true
workflow_dispatch:
inputs:
upload:
required: false
type: boolean
default: false
description: "Whether to upload to branch"
jobs:
build-lkm:
strategy:
matrix:
include:
- version: "android12-5.10"
sub_level: 233
os_patch_level: 2025-02
- version: "android13-5.10"
sub_level: 228
os_patch_level: 2025-01
- version: "android13-5.15"
sub_level: 170
os_patch_level: 2025-01
- version: "android14-5.15"
sub_level: 170
os_patch_level: 2025-01
- version: "android14-6.1"
sub_level: 128
os_patch_level: 2025-03
- version: "android15-6.6"
sub_level: 77
os_patch_level: 2025-03
# uses: ./.github/workflows/gki-kernel-mock.yml when debugging
uses: ./.github/workflows/gki-kernel.yml
with:
version: ${{ matrix.version }}
version_name: ${{ matrix.version }}.${{ matrix.sub_level }}
tag: ${{ matrix.version }}-${{ matrix.os_patch_level }}
os_patch_level: ${{ matrix.os_patch_level }}
build_lkm: true
push-to-branch:
needs: [build-lkm]
runs-on: ubuntu-latest
if: ${{ inputs.upload }}
steps:
- name: Download all workflow run artifacts
uses: actions/download-artifact@v4
with:
path: bin/
merge-multiple: true
- name: Push to branch LKM
run: |
cd bin
git config --global init.defaultBranch lkm
git init
git remote add origin https://${{ secrets.TOKEN }}@github.com/${{ github.repository }}
git config --local user.name "github-actions[bot]"
git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
find . -type f
git add .
git commit -m "Upload LKM from ${{ github.sha }}" -m "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
git push --force --set-upstream origin lkm

View File

@@ -8,7 +8,7 @@ on:
- 'manager/**' - 'manager/**'
- 'kernel/**' - 'kernel/**'
- 'userspace/ksud/**' - 'userspace/ksud/**'
- 'userspace/zakomksd/**' - 'userspace/susfs/**'
- 'userspace/kpmmgr/**' - 'userspace/kpmmgr/**'
pull_request: pull_request:
branches: [ "main" ] branches: [ "main" ]
@@ -16,89 +16,20 @@ on:
- 'manager/**' - 'manager/**'
workflow_call: workflow_call:
workflow_dispatch: workflow_dispatch:
inputs:
build_lkm:
required: true
type: choice
default: "auto"
options:
- "true"
- "false"
- "auto"
description: "Whether to build lkm"
upload_lkm:
required: true
type: boolean
default: false
description: "Whether to upload lkm"
jobs: jobs:
check-build-lkm: build-susfs:
runs-on: ubuntu-latest
outputs:
build_lkm: ${{ steps.check-build.outputs.build_lkm }}
upload_lkm: ${{ steps.check-build.outputs.upload_lkm }}
steps:
- name: check build
id: check-build
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ] && [ "${{ inputs.build_lkm }}" != "auto" ]; then
kernel_changed="${{ inputs.build_lkm }}"
else
kernel_changed=true
mkdir tmp
cd tmp
git config --global init.defaultBranch bot
git config --global user.name 'Bot'
git config --global user.email 'bot@github.shirkneko.io'
git init .
git remote add origin https://github.com/${{ github.repository }}
CURRENT_COMMIT="${{ github.event.head_commit.id }}"
git fetch origin $CURRENT_COMMIT --depth=1
git fetch origin lkm --depth=1
LKM_COMMIT="$(git log --format=%B -n 1 origin/lkm | head -n 1)"
LKM_COMMIT="${LKM_COMMIT#Upload LKM from }"
LKM_COMMIT=$(echo "$LKM_COMMIT" | tr -d '[:space:]')
echo "LKM_COMMIT=$LKM_COMMIT"
git fetch origin "$LKM_COMMIT" --depth=1
git diff --quiet "$LKM_COMMIT" "$CURRENT_COMMIT" -- kernel :!kernel/setup.sh .github/workflows/build-lkm.yml .github/workflows/build-kernel-*.yml && kernel_changed=false
cd ..
rm -rf tmp
fi
if [ "${{ github.event_name }}" == "push" ] && [ "${{ github.ref }}" == 'refs/heads/susfs' ]; then
need_upload=true
elif [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
need_upload="${{ inputs.upload_lkm }}"
else
need_upload=false
fi
echo "kernel changed: $kernel_changed"
echo "need upload: $need_upload"
echo "build_lkm=$kernel_changed" >> "$GITHUB_OUTPUT"
echo "upload_lkm=$need_upload" >> "$GITHUB_OUTPUT"
build-lkm:
needs: check-build-lkm
uses: ./.github/workflows/build-lkm.yml
if: ${{ needs.check-build-lkm.outputs.build_lkm == 'true' }}
with:
upload: ${{ needs.check-build-lkm.outputs.upload_lkm == 'true' }}
secrets: inherit
build-zakomksd:
if: ${{ always() }}
needs: [ check-build-lkm, build-lkm ]
strategy: strategy:
matrix: matrix:
include: include:
- target: aarch64-linux-android - target: aarch64-linux-android
os: ubuntu-latest os: ubuntu-latest
uses: ./.github/workflows/zakomksd.yml uses: ./.github/workflows/susfs.yml
with: with:
target: ${{ matrix.target }} target: ${{ matrix.target }}
os: ${{ matrix.os }} os: ${{ matrix.os }}
build-kpmmgr: build-kpmmgr:
if: ${{ always() }} needs: build-susfs
needs: [ check-build-lkm, build-lkm ]
strategy: strategy:
matrix: matrix:
include: include:
@@ -110,8 +41,7 @@ jobs:
os: ${{ matrix.os }} os: ${{ matrix.os }}
build-ksud: build-ksud:
if: ${{ always() }} needs: build-kpmmgr
needs: [ check-build-lkm, build-lkm ]
strategy: strategy:
matrix: matrix:
include: include:
@@ -123,8 +53,6 @@ jobs:
with: with:
target: ${{ matrix.target }} target: ${{ matrix.target }}
os: ${{ matrix.os }} os: ${{ matrix.os }}
pack_lkm: true
pull_lkm: ${{ needs.check-build-lkm.outputs.build_lkm != 'true' }}
build-manager: build-manager:
if: ${{ always() }} if: ${{ always() }}
@@ -174,10 +102,10 @@ jobs:
- name: Setup Android SDK - name: Setup Android SDK
uses: android-actions/setup-android@v3 uses: android-actions/setup-android@v3
- name: Download arm64 zakomksd - name: Download arm64 susfs
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
name: zakomksd-aarch64-linux-android name: susfs-aarch64-linux-android
path: . path: .
- name: Download arm64 kpmmgr - name: Download arm64 kpmmgr
@@ -202,18 +130,18 @@ jobs:
run: | run: |
mkdir -p app/src/main/jniLibs/arm64-v8a mkdir -p app/src/main/jniLibs/arm64-v8a
mkdir -p app/src/main/jniLibs/x86_64 mkdir -p app/src/main/jniLibs/x86_64
cp -f ../aarch64-linux-android/release/zakomk ../manager/app/src/main/jniLibs/arm64-v8a/libzakomk.so cp -f ../aarch64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozako.so
cp -f ../x86_64-linux-android/release/zakomk ../manager/app/src/main/jniLibs/x86_64/libzakomk.so cp -f ../x86_64-linux-android/release/zakozako ../manager/app/src/main/jniLibs/x86_64/libzakozako.so
- name: Copy kpmmgr to app jniLibs - name: Copy kpmmgr to app jniLibs
run: | run: |
mkdir -p app/src/main/jniLibs/arm64-v8a mkdir -p app/src/main/jniLibs/arm64-v8a
cp -f ../arm64-v8a/kpmmgr ../manager/app/src/main/jniLibs/arm64-v8a/libkpmmgr.so cp -f ../arm64-v8a/kpmmgr ../manager/app/src/main/jniLibs/arm64-v8a/libkpmmgr.so
- name: Copy zakomksd to app jniLibs - name: Copy susfs to app jniLibs
run: | run: |
mkdir -p app/src/main/jniLibs/arm64-v8a mkdir -p app/src/main/jniLibs/arm64-v8a
cp -f ../arm64-v8a/zakomksd ../manager/app/src/main/jniLibs/arm64-v8a/libzakomksd.so cp -f ../arm64-v8a/zakozakozako ../manager/app/src/main/jniLibs/arm64-v8a/libzakozakozako.so
- name: Build with Gradle - name: Build with Gradle
run: | run: |

View File

@@ -1,79 +0,0 @@
name: GKI Kernel Build
on:
workflow_call:
inputs:
version:
required: true
type: string
description: >
Output directory of gki,
for example: android12-5.10
version_name:
required: true
type: string
description: >
With SUBLEVEL of kernel,
for example: android12-5.10.66
tag:
required: true
type: string
description: >
Part of branch name of common kernel manifest,
for example: android12-5.10-2021-11
os_patch_level:
required: false
type: string
description: >
Patch level of common kernel manifest,
for example: 2021-11
default: 2022-05
patch_path:
required: false
type: string
description: >
Directory name of .github/patches/<patch_path>
for example: 5.10
use_cache:
required: false
type: boolean
default: true
embed_ksud:
required: false
type: string
default: ksud-aarch64-linux-android
description: >
Artifact name of prebuilt ksud to be embedded
for example: ksud-aarch64-linux-android
debug:
required: false
type: boolean
default: false
build_lkm:
required: false
type: boolean
default: false
secrets:
BOOT_SIGN_KEY:
required: false
CHAT_ID:
required: false
BOT_TOKEN:
required: false
MESSAGE_THREAD_ID:
required: false
jobs:
mock_build:
name: Mock build ${{ inputs.version_name }}
runs-on: ubuntu-latest
steps:
- name: Create mocking ko
run: |
echo "${{ inputs.version }}_kernelsu.ko" > ${{ inputs.version }}_kernelsu.ko
- name: Upload LKM
uses: actions/upload-artifact@v4
if: ${{ inputs.build_lkm == true }}
with:
name: ${{ inputs.version }}-lkm
path: ./*_kernelsu.ko

View File

@@ -1,258 +0,0 @@
name: GKI Kernel Build
on:
workflow_call:
inputs:
version:
required: true
type: string
description: >
Output directory of gki,
for example: android12-5.10
version_name:
required: true
type: string
description: >
With SUBLEVEL of kernel,
for example: android12-5.10.66
tag:
required: true
type: string
description: >
Part of branch name of common kernel manifest,
for example: android12-5.10-2021-11
os_patch_level:
required: false
type: string
description: >
Patch level of common kernel manifest,
for example: 2021-11
default: 2022-05
patch_path:
required: false
type: string
description: >
Directory name of .github/patches/<patch_path>
for example: 5.10
use_cache:
required: false
type: boolean
default: true
embed_ksud:
required: false
type: string
default: ksud-aarch64-linux-android
description: >
Artifact name of prebuilt ksud to be embedded
for example: ksud-aarch64-linux-android
debug:
required: false
type: boolean
default: false
build_lkm:
required: false
type: boolean
default: false
secrets:
BOOT_SIGN_KEY:
required: false
CHAT_ID:
required: false
BOT_TOKEN:
required: false
MESSAGE_THREAD_ID:
required: false
jobs:
build:
name: Build ${{ inputs.version_name }}
runs-on: ubuntu-latest
env:
CCACHE_COMPILERCHECK: "%compiler% -dumpmachine; %compiler% -dumpversion"
CCACHE_NOHASHDIR: "true"
CCACHE_HARDLINK: "true"
steps:
- name: Maximize build space
uses: easimon/maximize-build-space@master
with:
root-reserve-mb: 8192
temp-reserve-mb: 2048
remove-dotnet: 'true'
remove-android: 'true'
remove-haskell: 'true'
remove-codeql: 'true'
- uses: actions/checkout@v4
with:
path: KernelSU
fetch-depth: 0
- name: Setup need_upload
id: need_upload
run: |
if [ ! -z "${{ secrets.BOT_TOKEN }}" ]; then
echo "UPLOAD=true" >> $GITHUB_OUTPUT
else
echo "UPLOAD=false" >> $GITHUB_OUTPUT
fi
- name: Setup kernel source
run: |
echo "Free space:"
df -h
cd $GITHUB_WORKSPACE
sudo apt-get install repo -y
mkdir android-kernel && cd android-kernel
repo init --depth=1 --u https://android.googlesource.com/kernel/manifest -b common-${{ inputs.tag }} --repo-rev=v2.35
REMOTE_BRANCH=$(git ls-remote https://android.googlesource.com/kernel/common ${{ inputs.tag }})
DEFAULT_MANIFEST_PATH=.repo/manifests/default.xml
if grep -q deprecated <<< $REMOTE_BRANCH; then
echo "Found deprecated branch: ${{ inputs.tag }}"
sed -i 's/"${{ inputs.tag }}"/"deprecated\/${{ inputs.tag }}"/g' $DEFAULT_MANIFEST_PATH
cat $DEFAULT_MANIFEST_PATH
fi
repo --version
repo --trace sync -c -j$(nproc --all) --no-tags
df -h
- name: Setup KernelSU
env:
PATCH_PATH: ${{ inputs.patch_path }}
IS_DEBUG_KERNEL: ${{ inputs.debug }}
run: |
cd $GITHUB_WORKSPACE/android-kernel
echo "[+] KernelSU setup"
GKI_ROOT=$(pwd)
echo "[+] GKI_ROOT: $GKI_ROOT"
echo "[+] Copy KernelSU driver to $GKI_ROOT/common/drivers"
ln -sf $GITHUB_WORKSPACE/KernelSU/kernel $GKI_ROOT/common/drivers/kernelsu
echo "[+] Add KernelSU driver to Makefile"
DRIVER_MAKEFILE=$GKI_ROOT/common/drivers/Makefile
DRIVER_KCONFIG=$GKI_ROOT/common/drivers/Kconfig
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE"
grep -q "kernelsu" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG"
echo "[+] Apply Compilation Patches"
if [ ! -e build/build.sh ]; then
GLIBC_VERSION=$(ldd --version 2>/dev/null | head -n 1 | awk '{print $NF}')
echo "GLIBC_VERSION: $GLIBC_VERSION"
if [ "$(printf '%s\n' "2.38" "$GLIBC_VERSION" | sort -V | head -n1)" = "2.38" ]; then
echo "Patching resolve_btfids/Makefile"
cd $GKI_ROOT/common/ && sed -i '/\$(Q)\$(MAKE) -C \$(SUBCMD_SRC) OUTPUT=\$(abspath \$(dir \$@))\/ \$(abspath \$@)/s//$(Q)$(MAKE) -C $(SUBCMD_SRC) EXTRA_CFLAGS="$(CFLAGS)" OUTPUT=$(abspath $(dir $@))\/ $(abspath $@)/' tools/bpf/resolve_btfids/Makefile || echo "No patch needed."
fi
fi
if [ "$IS_DEBUG_KERNEL" = "true" ]; then
echo "[+] Enable debug features for kernel"
printf "\nccflags-y += -DCONFIG_KSU_DEBUG\n" >> $GITHUB_WORKSPACE/KernelSU/kernel/Makefile
fi
repo status
echo "[+] KernelSU setup done."
- name: Symbol magic
run: |
echo "[+] Export all symbol from abi_gki_aarch64.xml"
COMMON_ROOT=$GITHUB_WORKSPACE/android-kernel/common
KSU_ROOT=$GITHUB_WORKSPACE/KernelSU
ABI_XML=$COMMON_ROOT/android/abi_gki_aarch64.xml
SYMBOL_LIST=$COMMON_ROOT/android/abi_gki_aarch64
# python3 $KSU_ROOT/scripts/abi_gki_all.py $ABI_XML > $SYMBOL_LIST
echo "[+] Add KernelSU symbols"
cat $KSU_ROOT/kernel/export_symbol.txt | awk '{sub("[ \t]+","");print " "$0}' >> $SYMBOL_LIST
- name: Setup ccache
if: inputs.use_cache == true
uses: hendrikmuhs/ccache-action@v1
with:
key: gki-kernel-aarch64-${{ inputs.version_name }}
max-size: 2G
save: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
- name: Setup for LKM
if: ${{ inputs.build_lkm == true }}
working-directory: android-kernel
run: |
pip install ast-grep-cli
sudo apt-get install llvm-15 -y
ast-grep -U -p '$$$ check_exports($$$) {$$$}' -r '' common/scripts/mod/modpost.c
ast-grep -U -p 'check_exports($$$);' -r '' common/scripts/mod/modpost.c
sed -i '/config KSU/,/help/{s/default y/default m/}' common/drivers/kernelsu/Kconfig
echo "drivers/kernelsu/kernelsu.ko" >> common/android/gki_aarch64_modules
# bazel build, android14-5.15, android14-6.1 use bazel
if [ ! -e build/build.sh ]; then
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
if [ -e common/modules.bzl ]; then
sed -i 's/_COMMON_GKI_MODULES_LIST = \[/_COMMON_GKI_MODULES_LIST = \[ "drivers\/kernelsu\/kernelsu.ko",/g' common/modules.bzl
fi
else
TARGET_FILE="build/kernel/build.sh"
if [ ! -e "$TARGET_FILE" ]; then
TARGET_FILE="build/build.sh"
fi
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' $TARGET_FILE || echo "No unknown symbol in $TARGET_FILE"
sed -i 's/if ! diff -u "\${KERNEL_DIR}\/\${MODULES_ORDER}" "\${OUT_DIR}\/modules\.order"; then/if false; then/g' $TARGET_FILE
sed -i 's@${ROOT_DIR}/build/abi/compare_to_symbol_list@echo@g' $TARGET_FILE
sed -i 's/needs unknown symbol/Dont abort when unknown symbol/g' build/kernel/*.sh || echo "No unknown symbol scripts found"
fi
- name: Make working directory clean to avoid dirty
working-directory: android-kernel
run: |
rm common/android/abi_gki_protected_exports_* || echo "No protected exports!"
git config --global user.email "bot@kernelsu.org"
git config --global user.name "KernelSUBot"
cd common/ && git add -A && git commit -a -m "Add KernelSU"
repo status
- name: Build Kernel/LKM
working-directory: android-kernel
run: |
if [ ! -z ${{ vars.EXPECTED_SIZE }} ] && [ ! -z ${{ vars.EXPECTED_HASH }} ]; then
export KSU_EXPECTED_SIZE=${{ vars.EXPECTED_SIZE }}
export KSU_EXPECTED_HASH=${{ vars.EXPECTED_HASH }}
fi
if [ -e build/build.sh ]; then
LTO=thin BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh CC="/usr/bin/ccache clang"
else
tools/bazel run --disk_cache=/home/runner/.cache/bazel --config=fast --config=stamp --lto=thin //common:kernel_aarch64_dist -- --dist_dir=dist
fi
- name: Prepare artifacts
id: prepareArtifacts
run: |
OUTDIR=android-kernel/out/${{ inputs.version }}/dist
if [ ! -e $OUTDIR ]; then
OUTDIR=android-kernel/dist
fi
mkdir output
if [ "${{ inputs.build_lkm}}" = "true" ]; then
llvm-strip-15 -d $OUTDIR/kernelsu.ko
mv $OUTDIR/kernelsu.ko ./output/${{ inputs.version }}_kernelsu.ko
else
cp $OUTDIR/Image ./output/
cp $OUTDIR/Image.lz4 ./output/
git clone https://github.com/Kernel-SU/AnyKernel3
rm -rf ./AnyKernel3/.git
cp $OUTDIR/Image ./AnyKernel3/
fi
- name: Upload Image and Image.gz
uses: actions/upload-artifact@v4
if: ${{ inputs.build_lkm == false }}
with:
name: Image-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
path: ./output/*
- name: Upload AnyKernel3
if: ${{ inputs.build_lkm == false }}
uses: actions/upload-artifact@v4
with:
name: AnyKernel3-${{ inputs.version_name }}_${{ inputs.os_patch_level }}
path: ./AnyKernel3/*
- name: Upload LKM
uses: actions/upload-artifact@v4
if: ${{ inputs.build_lkm == true }}
with:
name: ${{ inputs.version }}-lkm
path: ./output/*_kernelsu.ko

View File

@@ -71,4 +71,4 @@ jobs:
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: ksud-${{ inputs.target }} name: ksud-${{ inputs.target }}
path: userspace/ksud/target/**/release/zakomk* path: userspace/ksud/target/**/release/zakozako*

View File

@@ -1,11 +1,11 @@
name: Build zakomksd name: Build susfs
on: on:
push: push:
branches: [ "mian" ] branches: [ "mian" ]
paths: paths:
- '.github/workflows/zakomksd.yml' - '.github/workflows/susfs.yml'
- 'userspace/zakomksd/**' - 'userspace/susfs/**'
workflow_dispatch: workflow_dispatch:
workflow_call: workflow_call:
inputs: inputs:
@@ -19,7 +19,7 @@ on:
jobs: jobs:
build-susfs: build-susfs:
name: Build userspace zakomksd name: Build userspace susfs
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -28,13 +28,13 @@ jobs:
with: with:
fetch-depth: 0 fetch-depth: 0
- name: Build zakomksd - name: Build susfs
working-directory: ./userspace/zakomksd working-directory: ./userspace/susfs
run: | run: |
$ANDROID_NDK_HOME/ndk-build $ANDROID_NDK_HOME/ndk-build
- name: Upload a Build Artifact - name: Upload a Build Artifact
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@v4
with: with:
name: zakomksd-aarch64-linux-android name: susfs-aarch64-linux-android
path: ./userspace/zakomksd/libs path: ./userspace/susfs/libs

View File

@@ -1,49 +1,63 @@
# SukiSU # SukiSU
**Enlish** | [简体中文](README.md) **English** | [简体中文](README.md) | [日本語](README-ja.md)
Android device root solution based on [KernelSU](https://github.com/KernelSU/KernelSU) Android device root solution based on [KernelSU](https://github.com/tiann/KernelSU)
**Experimental! Use at your own risk! **This solution is based on [KernelSU]() and is experimental! **Experimental! Use at your own risk!** This solution is based on [KernelSU](https://github.com/tiann/KernelSU) and is experimental!
> >
> This is an unofficial fork, all rights reserved [@tiann](https://github.com/tiann) > This is an unofficial fork. All rights are reserved to [@tiann](https://github.com/tiann)
> However, we will be a separately maintained branch of KSU in the future
> >
- Fully adapted for non-GKI devices (susfs-dev and unsusfs-patched dev branches only) - Fully adapted for non-GKI devices (susfs-dev and unsusfs-patched dev branches only)
## How to add ## How to add
Using the susfs-dev branch (integrated susfs with support for non-GKI devices) Use the susfs-stable or susfs-dev branch (integrated susfs with support for non-GKI devices)
``` ```
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
``` ```
Use main branching (no longer with support for non-GKI devices) Use the main branch
``` ```
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main curl -LSs "https://raw.githubusercontent.com/ShirkNeko/KernelSU/main/kernel/setup.sh" | bash -s main
``` ```
## How to use integrated susfs ## How to use integrated susfs
Use the susfs-dev branch directly without any patching 1. Use the susfs-dev branch directly without any patching
## KPM support
- We have removed duplicate KSU functions based on KernelPatch and retained KPM support.
- We will introduce more APatch-compatible functions to ensure the integrity of KPM functionality.
Open source address: https://github.com/ShirkNeko/SukiSU_KernelPatch_patch
KPM template address: https://github.com/udochina/KPM-Build-Anywhere
## More links ## More links
Projects compiled based on Sukisu and susfs Projects compiled based on Sukisu and susfs
- [GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS) - [GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
- [OnePlus](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS) - [OnePlus](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
## Hook method ## Hook method
- This method references the hook manual to (https://github.com/rsuntk/KernelSU) - This method references the hook method from (https://github.com/rsuntk/KernelSU)
1. **KPROBES hook:** 1. **KPROBES hook:**
- This fork only supports GKI (5.10 - 6.x) kernels, all non-GKI kernels must use manual hooks. - This method only supports GKI (5.10 - 6.x) kernels, and all non-GKI kernels must use manual hooks.
- For Loadable Kernel Modules (LKM) - For Loadable Kernel Modules (LKM)
- Default hooking method for GKI kernels - Default hooking method for GKI kernels
- Requires `CONFIG_KPROBES=y`. 2. - Requires `CONFIG_KPROBES=y`.
2. **Hooks manual:** 2. **Manual hooks:**
- For GKI (5.10 - 6.x) kernels, add `CONFIG_KSU_MANUAL_HOOK=y` to the kernel defconfig and make sure to protect KernelSU hooks by using `#ifdef CONFIG_KSU_MANUAL_HOOK` instead of `#ifdef CONFIG_KSU`. - For GKI (5.10 - 6.x) kernels, add `CONFIG_KSU_MANUAL_HOOK=y` to the kernel defconfig and make sure to protect KernelSU hooks by using `#ifdef CONFIG_KSU_MANUAL_HOOK` instead of `#ifdef CONFIG_KSU`.
- Standard KernelSU hooks: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source - Standard KernelSU hooks: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
- backslashxx syscall hooks: https://github.com/backslashxx/KernelSU/issues/5 - backslashxx syscall hooks: https://github.com/backslashxx/KernelSU/issues/5
@@ -51,51 +65,55 @@ Projects compiled based on Sukisu and susfs
## Usage ## Usage
[GKI] ### GKI
1. such as millet redmi samsung and other devices (does not include the magic kernel manufacturers such as: meizu, a plus real me oppo) 1. such as Xiaomi, Redmi, Samsung, and other devices (does not include manufacturers that modified the kernel like Meizu, OnePlus, RealMe, and OPPO)
2. find more links in the GKI build project to find the device kernel version directly download with TWRP or kernel flashing tool to brush into the zip with AnyKernel3 suffix can be 2. Use the prebuilt GKI kernel, the ones with their name ending with AnyKernel3, mentioned in the 'More Links' section, and then flash it with recoveries like TWRP
3. General without the suffix of the .zip compressed package is universal, gz suffix for the special TianGui models, lz4 suffix for Google models, general brush without the suffix can be! 3. Generally, packages with a plain .zip suffix are universal. However, if your device has a MediaTek processor, you should use the ones with .gz suffix, and packages with .lz4 suffix are dedicated to Google devices.
[OnePlus] ### OnePlus
1. Find the Yiga project in the More link and fill in your own, then build it with cloud compilation, and finally brush in the zip with AnyKernel3 suffix. 1. Use the link mentioned in the 'More Links' section to create a customized build with your device information, and then flash the zip file with the AnyKernel3 suffix.
Note: You only need to fill in the first two kernel versions, such as 5.10, 5.15, 6.1, 6.6. > [!Note]
- Please search for the processor codename by yourself, usually it is all English without numbers. > - You only need to fill in the first two parts of kernel versions, such as 5.10, 5.15, 6.1, or 6.6.
- Branching and configuration files, please fill in the kernel open source address. > - Please search for the processor codename by yourself, usually it is all English without numbers.
> - You can find the branch and configuration files from the OnePlus open-source kernel repository.
## Features ## Features
1. Kernel-based `su` and root access management. 1. Kernel-based `su` and root access management.
2. Not based on [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) module system. 3. 2. Not based on [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) module system, but based on [Magic Mount](https://github.com/5ec1cff/KernelSU) from 5ec1cff
3. [Application Profiles](https://kernelsu.org/guide/app-profile.html): Lock root privileges in a cage. 4. 3. [App Profile](https://kernelsu.org/guide/app-profile.html): Lock root privileges in a cage.
4. Bringing back non-GKI/GKI 1.0 support 4. Bringing back non-GKI/GKI 1.0 support
5. More customization 5. More customization
6. Support for KPM kernel modules
## License ## License
- The file in the “kernel” directory is [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html). - The file in the “kernel” directory is under [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) license.
- All other parts except the “kernel” directory are [GPL-3.0 or later](https://www.gnu.org/licenses/gpl-3.0.html). - All other parts except the “kernel” directory are under [GPL-3.0 or later](https://www.gnu.org/licenses/gpl-3.0.html) license.
## Sponsorship list ## Sponsorship list
- [Ktouls](https://github.com/Ktouls) Thanks so much for bringing me support - [Ktouls](https://github.com/Ktouls) Thanks so much for bringing me support
- [zaoqi123](https://github.com/zaoqi123) It's not a bad idea to buy me a milk tea - [zaoqi123](https://github.com/zaoqi123) It's not a bad idea to buy me a milk tea
- [wswzgdg](https://github.com/wswzgdg) Many thanks for supporting this project - [wswzgdg](https://github.com/wswzgdg) Many thanks for supporting this project
- [yspbwx2010](https://github.com/yspbwx2010) Many thanks
How the above list does not have your name, I will keep you updated, thanks again for your support! If the above list does not have your name, I will update it as soon as possible, and thanks again for your support!
## Contributions ## Contributions
- [KernelSU](https://github.com/tiann/KernelSU): original project - [KernelSU](https://github.com/tiann/KernelSU): original project
- [MKSU](https://github.com/5ec1cff/KernelSU): Used project - [MKSU](https://github.com/5ec1cff/KernelSU): Used project
- [RKSU](https://github.com/rsuntk/KernelsU)Re-support of non-GKI devices using the kernel of this project - [RKSU](https://github.com/rsuntk/KernelsU): Reintroduced the support of non-GKI devices using the kernel of this project
- [susfs](https://gitlab.com/simonpunk/susfs4ksu)Used susfs file system - [susfs](https://gitlab.com/simonpunk/susfs4ksu)Used susfs file system
- [KernelSU](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU conceptualization - [KernelSU](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU conceptualization
- [Magisk](https://github.com/topjohnwu/Magisk): Powerful root utility - [Magisk](https://github.com/topjohnwu/Magisk): Powerful root utility
- [genuine](https://github.com/brevent/genuine/): APK v2 Signature Verification - [genuine](https://github.com/brevent/genuine/): APK v2 Signature Verification
- [Diamorphine](https://github.com/m0nad/Diamorphine): Some rootkit skills. - [Diamorphine](https://github.com/m0nad/Diamorphine): Some rootkit utilities.
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch is a key part of the APatch implementation of the kernel module

112
docs/README-ja.md Normal file
View File

@@ -0,0 +1,112 @@
# SukiSU
**日本語** | [简体中文](README.md) | [English](README-en.md)
[KernelSU](https://github.com/tiann/KernelSU) をベースとした Android デバイスの root ソリューション
**試験中なビルドです!自己責任で使用してください!**<br>
このソリューションは [KernelSU](https://github.com/tiann/KernelSU) に基づいていますが、試験中なビルドです。
>
> これは非公式なフォークです。すべての権利は [@tiann](https://github.com/tiann) に帰属します。
>
>ただし、将来的には KSU とは別に管理されるブランチとなる予定です。
- GKI 非対応なデバイスに完全に適応 (susfs-dev と unsusfs-patched dev ブランチのみ)
## 追加方法
susfs-stable または susfs-dev ブランチ (GKI 非対応デバイスに対応する統合された susfs) 使用してください。
```
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
```
メインブランチを使用する場合
```
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/KernelSU/main/kernel/setup.sh" | bash -s main
```
## 統合された susfs の使い方
1. パッチを当てずに susfs-dev ブランチを直接使用してください。
## KPM に対応
- KernelPatch に基づいて重複した KSU の機能を削除、KPM の対応を維持させています。
- KPM 機能の整合性を確保するために、APatch の互換機能を更に向上させる予定です。
オープンソースアドレス: https://github.com/ShirkNeko/SukiSU_KernelPatch_patch
KPM テンプレートのアドレス: https://github.com/udochina/KPM-Build-Anywhere
## その他のリンク
SukiSU と susfs をベースにコンパイルされたプロジェクトです。
- [GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
- [OnePlus](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
## フックの方式
- この方式は (https://github.com/rsuntk/KernelSU) のフック方式を参照してください。
1. **KPROBES フック:**
- この方式は GKI (5.10 - 6.x) のカーネルのみに対応しています。GKI 以外のカーネルは手動でフックを使用する必要があります。
- 読み込み可能なカーネルモジュールの場合 (LKM)
- GKI カーネルのデフォルトとなるフック方式
- `CONFIG_KPROBES=y` が必要です。
2. **手動でフック:**
- GKI (5.10 - 6.x) のカーネルの場合、カーネルの defconfig に `CONFIG_KSU_MANUAL_HOOK=y` を追加して `#ifdef CONFIG_KSU` ではなく `#ifdef CONFIG_KSU_MANUAL_HOOK` を使用して KernelSU フックを保護するようにしてください。
- 標準の KernelSU フック: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
- backslashxx syscall フック: https://github.com/backslashxx/KernelSU/issues/5
- KPROBES を手動で統合する一部の非 GKI デバイスでは手動の VFS フック `new_hook.patch` パッチは不要です。
## 使い方
### GKI
1. Xiaomi、Redmi、Samsung などのデバイス (Meizu、OnePlus、Realme、OPPO などのカーネルを変更したメーカー以外)
2. `その他のリンク`の項目で言及されているカーネル名が、AnyKernel3 で終わるビルド済みの GKI カーネルを TWRP などのリカバリーでフラッシュします。
3. 一般的な .zip の接頭辞を持つパッケージは汎用的になります。ただし、デバイスに MediaTek 製の SoC が搭載されている場合は、.gz の接頭辞を持つパッケージを使用する必要があります。その他に .lz4 の接頭辞を持つパッケージは Google 製デバイス専用です。
### OnePlus
1. `その他のリンク`の項目に記載されているリンクを開き、デバイス情報を使用してカスタマイズされたカーネルをビルドし、AnyKernel3 の接頭辞を持つ .zip ファイルをフラッシュします。
> [!Note]
> - 5.10、5.15、6.1、6.6 などのカーネルバージョンの最初の 2 文字のみを入力する必要があります。
> - SoC のコードネームは自分で検索してください。通常は、数字がなく英語表記のみです。
> - ブランチと構成ファイルは、OnePlus オープンソースカーネルリポジトリから見つけることができます。
## 機能
1. カーネルベースな `su` および root アクセスの管理。
2. [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) モジュールシステムではなく、 5ec1cff 氏の [Magic Mount](https://github.com/5ec1cff/KernelSU) に基づいています。
3. [アプリプロファイル](https://kernelsu.org/guide/app-profile.html): root 権限をケージ内にロックします。
4. 非 GKI / GKI 1.0 の対応を復活
5. その他のカスタマイズ
6. KPM カーネルモジュールに対応
## ライセンス
- “kernel” ディレクトリ内のファイルは [GPL-2.0](https://www.gnu.org/licenses/old-licenses/gpl-2.0.ja.html) のみライセンス下にあります。
- “kernel” ディレクトリを除くその他すべての部分は [GPL-3.0 またはそれ以降](https://www.gnu.org/licenses/gpl-3.0.html) のライセンス下にあります。
## スポンサーシップの一覧
- [Ktouls](https://github.com/Ktouls) 応援をしてくれたことに感謝。
- [zaoqi123](https://github.com/zaoqi123) ミルクティーを買ってあげるのも良い考えですね。
- [wswzgdg](https://github.com/wswzgdg) このプロジェクトを支援していただき、ありがとうございます。
- [yspbwx2010](https://github.com/yspbwx2010) どうもありがとう。
上記の一覧にあなたの名前がない場合は、できるだけ早急に更新しますので再度ご支援をお願いします。
## 貢献者
- [KernelSU](https://github.com/tiann/KernelSU): オリジナルのプロジェクトです。
- [MKSU](https://github.com/5ec1cff/KernelSU): 使用しているプロジェクトです。
- [RKSU](https://github.com/rsuntk/KernelsU): このプロジェクトのカーネルを使用して非 GKI デバイスのサポートを追加しています。
- [susfs](https://gitlab.com/simonpunk/susfs4ksu):使用している susfs ファイルシステムです。
- [KernelSU](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU について。
- [Magisk](https://github.com/topjohnwu/Magisk): パワフルな root ユーティリティです。
- [genuine](https://github.com/brevent/genuine/): APK v2 署名認証で使用しています。
- [Diamorphine](https://github.com/m0nad/Diamorphine): いくつかの rootkit ユーティリティを使用しています。
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch はカーネルモジュールの APatch 実装での重要な部分となります。

View File

@@ -1,14 +1,15 @@
# SukiSU # SukiSU Ultra
**简体中文** | [English](README-en.md) **简体中文** | [English](README-en.md) | [日本語](README-ja.md)
基于 [KernelSU](https://github.com/tiann/KernelSU) 的安卓设备 root 解决方案 基于 [KernelSU](https://github.com/tiann/KernelSU) 的安卓设备 root 解决方案
**实验性!使用风险自负!** **实验性! 使用风险自负!**
> >
> 这是非官方分支,保留所有权利 [@tiann](https://github.com/tiann) > 这是非官方分支,保留所有权利 [@tiann](https://github.com/tiann)
> 但是我们将会在未来成为一个单独维护的KSU分支
> >
@@ -16,33 +17,46 @@
在内核源码的根目录下执行以下命令: 在内核源码的根目录下执行以下命令:
使用 susfs-dev 分支已集成susfs带非GKI设备的支持 使用 susfs-dev 分支已集成susfs带非GKI设备的支持
``` ```
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
``` ```
使用 main 分支不再带非GKI设备的支持
使用 main 分支
``` ```
curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
``` ```
## 如何集成 susfs ## 如何集成 susfs
1. 直接使用 susfs-dev 分支,不需要再集成 susfs 1. 直接使用 susfs-stable 或者 susfs-dev 分支,不需要再集成 susfs
## 钩子方法 ## 钩子方法
- 此部分引用自 [rsuntk 的钩子方法](https://github.com/rsuntk/KernelSU) - 此部分引用自 [rsuntk 的钩子方法](https://github.com/rsuntk/KernelSU)
1. **KPROBES 钩子:** 1. **KPROBES 钩子:**
- 此方法仅支持 GKI 2.05.10 - 6.x内核,所有非 GKI 2.0 内核都必须使用手动钩子 - 此方法仅支持 GKI 2.0 (5.10 - 6.x) 内核, 所有非 GKI 2.0 内核都必须使用手动钩子
- 用于可加载内核模块 (LKM) - 用于可加载内核模块 (LKM)
- GKI 2.0 内核的默认钩子方法 - GKI 2.0 内核的默认钩子方法
- 需要 `CONFIG_KPROBES=y` - 需要 `CONFIG_KPROBES=y`
2. **手动钩子:** 2. **手动钩子:**
- 对于 GKI 2.05.10 - 6.x内核,需要在对应设备的 defconfig 文件中添加 `CONFIG_KSU_MANUAL_HOOK=y` 并确保使用 `#ifdef CONFIG_KSU_MANUAL_HOOK` 而不是 `#ifdef CONFIG_KSU` 来保护 KernelSU 钩子 - 对于 GKI 2.0 (5.10 - 6.x) 内核,需要在对应设备的 defconfig 文件中添加 `CONFIG_KSU_MANUAL_HOOK=y` 并确保使用 `#ifdef CONFIG_KSU_MANUAL_HOOK` 而不是 `#ifdef CONFIG_KSU` 来保护 KernelSU 钩子
- 标准的 KernelSU 钩子https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source - 标准的 KernelSU 钩子https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
- backslashxx 的 syscall 手动钩子https://github.com/backslashxx/KernelSU/issues/5 - backslashxx 的 syscall 手动钩子https://github.com/backslashxx/KernelSU/issues/5
- 部分手动集成KPROBES的非 GKI 2.0 设备不需要手动 VFS 钩子 `new_hook.patch` 补丁 - 部分手动集成 KPROBES 的非 GKI 2.0 设备不需要手动 VFS 钩子 `new_hook.patch` 补丁
## KPM支持
- 我们基于KernelPatch去掉了和KSU重复的功能保留了KPM支持
- 我们将会引入更多的兼容APatch的函数来确保KPM功能的完整性
开源地址: https://github.com/ShirkNeko/SukiSU_KernelPatch_patch
KPM模板地址: https://github.com/udochina/KPM-Build-Anywhere
## 更多链接 ## 更多链接
@@ -54,17 +68,17 @@ curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/
## 使用方法 ## 使用方法
### GKI ### GKI
1. 适用于如小米红米三星等的 GKI 2.0 的设备不包含魔改内核的厂商如魅族、一加、真我和 oppo 1. 适用于如小米红米三星等的 GKI 2.0 的设备 (不包含魔改内核的厂商如魅族、一加、真我和 oppo)
2. 找到更多链接里的 GKI 构建的项目找到设备内核版本直接下载用TWRP或者内核刷写工具刷入带 AnyKernel3 后缀的压缩包即可 2. 找到更多链接里的 GKI 构建的项目找到设备内核版本直接下载用TWRP或者内核刷写工具刷入带 AnyKernel3 后缀的压缩包即可
3. 一般不带后缀的 .zip 压缩包是通用gz 后缀的为天玑机型专用lz4 后缀的为谷歌系机型专用,一般刷不带后缀的即可 3. 一般不带后缀的 .zip 压缩包是通用gz 后缀的为天玑机型专用lz4 后缀的为谷歌系机型专用,一般刷不带后缀的即可
### 一加 ### 一加
1.找到更多链接里的一加项目进行自行填写,然后云编译构建,最后刷入带 AnyKernel3 后缀的压缩包即可 1.找到更多链接里的一加项目进行自行填写,然后云编译构建,最后刷入带 AnyKernel3 后缀的压缩包即可
注意事项: > [!Note]
- 内核版本只需要填写前两位即可,如 5.105.156.16.6 > - 内核版本只需要填写前两位即可,如 5.105.156.16.6
- 处理器代号请自行搜索,一般为全英文不带数字的代号 > - 处理器代号请自行搜索,一般为全英文不带数字的代号
- 分支和配置文件请自行到一加内核开源地址进行填写 > - 分支和配置文件请自行到一加内核开源地址进行填写
## 特点 ## 特点
@@ -72,8 +86,9 @@ curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/
1. 基于内核的 `su` 和 root 访问管理 1. 基于内核的 `su` 和 root 访问管理
2. 基于 5ec1cff 的 [Magic Mount](https://github.com/5ec1cff/KernelSU) 的模块系统 2. 基于 5ec1cff 的 [Magic Mount](https://github.com/5ec1cff/KernelSU) 的模块系统
3. [App Profile](https://kernelsu.org/guide/app-profile.html):将 root 权限锁在笼子里 3. [App Profile](https://kernelsu.org/guide/app-profile.html):将 root 权限锁在笼子里
4. 恢复对非 GKI 2.0 内核的支持仅限susfs-dev和未进行susfs补丁的dev分支 4. 恢复对非 GKI 2.0 内核的支持
5. 更多自定义功能 5. 更多自定义功能
6. 对KPM内核模块的支持
## 许可证 ## 许可证
@@ -85,11 +100,12 @@ curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/
- [Ktouls](https://github.com/Ktouls) 非常感谢你给我带来的支持 - [Ktouls](https://github.com/Ktouls) 非常感谢你给我带来的支持
- [zaoqi123](https://github.com/zaoqi123) 请我喝奶茶也不错 - [zaoqi123](https://github.com/zaoqi123) 请我喝奶茶也不错
- [wswzgdg](https://github.com/wswzgdg) 非常感谢对此项目的支持 - [wswzgdg](https://github.com/wswzgdg) 非常感谢对此项目的支持
- [yspbwx2010](https://github.com/yspbwx2010) 非常感谢
以上名单没有你的名称,我会及时更新,再次感谢大家的支持 以上名单没有你的名称,我会及时更新,再次感谢大家的支持
## 贡献 ## 贡献
@@ -101,3 +117,4 @@ curl -LSs "https://raw.githubusercontent.com/ShirkNeko/SukiSU-Ultra/main/kernel/
- [Magisk](https://github.com/topjohnwu/Magisk):强大的 root 工具 - [Magisk](https://github.com/topjohnwu/Magisk):强大的 root 工具
- [genuine](https://github.com/brevent/genuine/)APK v2 签名验证 - [genuine](https://github.com/brevent/genuine/)APK v2 签名验证
- [Diamorphine](https://github.com/m0nad/Diamorphine):一些 rootkit 技能 - [Diamorphine](https://github.com/m0nad/Diamorphine):一些 rootkit 技能
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch是APatch实现内核模块的关键部分

View File

@@ -24,11 +24,12 @@ config KSU_HOOK
override the kernel version check and enable the hook functionality. override the kernel version check and enable the hook functionality.
config KPM config KPM
bool "Enable KernelSU KPM" bool "Enable SukiSU KPM"
default n default n
help help
This option enables the KernelSU KPM feature. If enabled, it will Enabling this option will activate the KPM feature of SukiSU.
override the kernel version check and enable the hook functionality. This option is suitable for scenarios where you need to force KPM to be enabled.
but it may affect system stability.
endmenu endmenu

View File

@@ -22,14 +22,14 @@ obj-$(CONFIG_KPM) += kpm/
# .git is a text file while the module is imported by 'git submodule add'. # .git is a text file while the module is imported by 'git submodule add'.
ifeq ($(shell test -e $(srctree)/$(src)/../.git; echo $$?),0) ifeq ($(shell test -e $(srctree)/$(src)/../.git; echo $$?),0)
$(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin [ -f ../.git/shallow ] && git fetch --unshallow) $(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin [ -f ../.git/shallow ] && git fetch --unshallow)
KSU_GIT_VERSION := $(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git rev-list --count HEAD) KSU_GIT_VERSION := $(shell cd $(srctree)/$(src); /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git rev-list --count main)
# ksu_version: major * 10000 + git version + 606 for historical reasons # ksu_version: major * 10000 + git version + 606 for historical reasons
$(eval KSU_VERSION=$(shell expr 10000 + $(KSU_GIT_VERSION) + 606)) $(eval KSU_VERSION=$(shell expr 10000 + $(KSU_GIT_VERSION) + 606))
$(info -- KernelSU version: $(KSU_VERSION)) $(info -- SukiSU version: $(KSU_VERSION))
ccflags-y += -DKSU_VERSION=$(KSU_VERSION) ccflags-y += -DKSU_VERSION=$(KSU_VERSION)
else # If there is no .git file, the default version will be passed. else # If there is no .git file, the default version will be passed.
$(warning "KSU_GIT_VERSION not defined! It is better to make KernelSU a git submodule!") $(warning "KSU_GIT_VERSION not defined! It is better to make SukiSU a git submodule!")
ccflags-y += -DKSU_VERSION=16 ccflags-y += -DKSU_VERSION=12800
endif endif
ifndef KSU_EXPECTED_SIZE ifndef KSU_EXPECTED_SIZE
@@ -47,9 +47,14 @@ endif
$(info -- KernelSU Manager signature size: $(KSU_EXPECTED_SIZE)) $(info -- KernelSU Manager signature size: $(KSU_EXPECTED_SIZE))
$(info -- KernelSU Manager signature hash: $(KSU_EXPECTED_HASH)) $(info -- KernelSU Manager signature hash: $(KSU_EXPECTED_HASH))
$(info -- Supported Unofficial Manager: ShirkNeko (GKI) (Non-GKI)) $(info -- Supported Unofficial Manager: 5ec1cff (GKI) ShirkNeko udochina (GKI and KPM))
KERNEL_VERSION := $(VERSION).$(PATCHLEVEL) KERNEL_VERSION := $(VERSION).$(PATCHLEVEL)
$(info -- KERNEL_VERSION: $(KERNEL_VERSION)) $(info -- KERNEL_VERSION: $(KERNEL_VERSION))
ifeq ($(CONFIG_KPM),y)
$(info -- KPM is enabled)
else
$(info -- KPM is disabled)
endif
ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE) ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)

View File

@@ -419,10 +419,7 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
pr_info("KPM: calling before arg2=%d\n", (int) arg2); pr_info("KPM: calling before arg2=%d\n", (int) arg2);
res = sukisu_handle_kpm(arg2, arg3, arg4); res = sukisu_handle_kpm(arg2, arg3, arg4, arg5);
copy_to_user(result, &res, sizeof(res));
pr_info("KPM: calling before arg2=%d res=%d\n", (int) arg2, (int) res);
return 0; return 0;
} }

View File

@@ -1,2 +1,6 @@
obj-y += kpm.o obj-y += kpm.o
obj-y += compact.o obj-y += compact.o
obj-y += super_access.o
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function

View File

@@ -26,37 +26,56 @@
#include <linux/slab.h> #include <linux/slab.h>
#include "kpm.h" #include "kpm.h"
#include "compact.h" #include "compact.h"
#include "../allowlist.h"
#include "../manager.h"
unsigned long sukisu_compact_find_symbol(const char* name); unsigned long sukisu_compact_find_symbol(const char* name);
// ====================================================================== // ======================================================================
// 兼容函数 for KPM
const char* kpver = "0.10"; static
int sukisu_is_su_allow_uid(uid_t uid) {
return ksu_is_allow_uid(uid) ? 1 : 0;
}
static
int sukisu_get_ap_mod_exclude(uid_t uid) {
// Not supported
return 0;
}
static
int sukisu_is_uid_should_umount(uid_t uid) {
return ksu_uid_should_umount(uid) ? 1 : 0;
}
static
int sukisu_is_current_uid_manager() {
return is_manager();
}
static
uid_t sukisu_get_manager_uid() {
return ksu_manager_uid;
}
// ======================================================================
struct CompactAddressSymbol { struct CompactAddressSymbol {
const char* symbol_name; const char* symbol_name;
void* addr; void* addr;
}; };
struct CompactAliasSymbol { static struct CompactAddressSymbol address_symbol [] = {
const char* symbol_name;
const char* compact_symbol_name;
};
struct CompactAddressSymbol address_symbol [] = {
{ "kallsyms_lookup_name", &kallsyms_lookup_name }, { "kallsyms_lookup_name", &kallsyms_lookup_name },
{ "compact_find_symbol", &sukisu_compact_find_symbol }, { "compact_find_symbol", &sukisu_compact_find_symbol },
{ "compat_copy_to_user", &copy_to_user }, { "is_run_in_sukisu_ultra", (void*)1 },
{ "compat_strncpy_from_user", &strncpy_from_user }, { "is_su_allow_uid", &sukisu_is_su_allow_uid },
{ "kpver", &kpver }, { "get_ap_mod_exclude", &sukisu_get_ap_mod_exclude },
{ "is_run_in_sukisu_ultra", (void*)1 } { "is_uid_should_umount", &sukisu_is_uid_should_umount },
}; { "is_current_uid_manager", &sukisu_is_current_uid_manager },
{ "get_manager_uid", &sukisu_get_manager_uid }
struct CompactAliasSymbol alias_symbol[] = {
{"kf_strncat", "strncat"},
{"kf_strlen", "strlen" },
{"kf_strcpy", "strcpy"},
{"compat_copy_to_user", "__arch_copy_to_user"}
}; };
unsigned long sukisu_compact_find_symbol(const char* name) { unsigned long sukisu_compact_find_symbol(const char* name) {
@@ -71,30 +90,13 @@ unsigned long sukisu_compact_find_symbol(const char* name) {
} }
} }
/* 如果符号名以 "kf__" 开头,尝试解析去掉前缀的部分 */
if (strncmp(name, "kf__", 4) == 0) {
const char *real_name = name + 4; // 去掉 "kf__"
addr = (unsigned long)kallsyms_lookup_name(real_name);
if (addr) {
return addr;
}
}
// 通过内核来查 // 通过内核来查
addr = kallsyms_lookup_name(name); addr = kallsyms_lookup_name(name);
if(addr) { if(addr) {
return addr; return addr;
} }
// 查不到就查查兼容的符号
for(i = 0; i < (sizeof(alias_symbol) / sizeof(struct CompactAliasSymbol)); i++) {
struct CompactAliasSymbol* symbol = &alias_symbol[i];
if(strcmp(name, symbol->symbol_name) == 0) {
addr = kallsyms_lookup_name(symbol->compact_symbol_name);
if(addr)
return addr;
}
}
return 0; return 0;
} }
EXPORT_SYMBOL(sukisu_compact_find_symbol);

File diff suppressed because it is too large Load Diff

View File

@@ -1,44 +1,44 @@
#ifndef ___SUKISU_KPM_H #ifndef ___SUKISU_KPM_H
#define ___SUKISU_KPM_H #define ___SUKISU_KPM_H
int sukisu_handle_kpm(unsigned long arg3, unsigned long arg4, unsigned long arg5); int sukisu_handle_kpm(unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5);
int sukisu_is_kpm_control_code(unsigned long arg2); int sukisu_is_kpm_control_code(unsigned long arg2);
// KPM控制代码 // KPM控制代码
#define CMD_KPM_CONTROL 28 #define CMD_KPM_CONTROL 28
#define CMD_KPM_CONTROL_MAX 34 #define CMD_KPM_CONTROL_MAX 35
// 控制代码 // 控制代码
// prctl(xxx, xxx, 1, "PATH", "ARGS") // prctl(xxx, 28, "PATH", "ARGS")
// success return 0, error return -N // success return 0, error return -N
#define SUKISU_KPM_LOAD 28 #define SUKISU_KPM_LOAD 28
// prctl(xxx, xxx, 2, "NAME") // prctl(xxx, 29, "NAME")
// success return 0, error return -N // success return 0, error return -N
#define SUKISU_KPM_UNLOAD 29 #define SUKISU_KPM_UNLOAD 29
// num = prctl(xxx, xxx, 3) // num = prctl(xxx, 30)
// error return -N // error return -N
// success return +num or 0 // success return +num or 0
#define SUKISU_KPM_NUM 30 #define SUKISU_KPM_NUM 30
// prctl(xxx, xxx, 4, Buffer, BufferSize) // prctl(xxx, 31, Buffer, BufferSize)
// success return +out, error return -N // success return +out, error return -N
#define SUKISU_KPM_LIST 31 #define SUKISU_KPM_LIST 31
// prctl(xxx, xxx, 5, "NAME", Buffer[256]) // prctl(xxx, 32, "NAME", Buffer[256])
// success return +out, error return -N // success return +out, error return -N
#define SUKISU_KPM_INFO 32 #define SUKISU_KPM_INFO 32
// prctl(xxx, xxx, 6, "NAME", "ARGS") // prctl(xxx, 33, "NAME", "ARGS")
// success return KPM's result value // success return KPM's result value
// error return -N // error return -N
#define SUKISU_KPM_CONTROL 33 #define SUKISU_KPM_CONTROL 33
// prctl(xxx, xxx, 7) // prctl(xxx, 34, buffer, bufferSize)
// success will printf to stdout and return 0 // success return KPM's result value
// error will return -1 // error return -N
#define SUKISU_KPM_PRINT 34 #define SUKISU_KPM_VERSION 34
#endif #endif

View File

@@ -0,0 +1,285 @@
#include <linux/export.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/kernfs.h>
#include <linux/file.h>
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/uaccess.h>
#include <linux/elf.h>
#include <linux/kallsyms.h>
#include <linux/version.h>
#include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/rcupdate.h>
#include <asm/elf.h> /* 包含 ARM64 重定位类型定义 */
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <asm/cacheflush.h>
#include <linux/module.h>
#include <linux/vmalloc.h>
#include <linux/set_memory.h>
#include <linux/version.h>
#include <linux/export.h>
#include <linux/slab.h>
#include "kpm.h"
#include "compact.h"
#include <linux/types.h>
#include <linux/stddef.h>
// 结构体成员元数据
struct DynamicStructMember {
const char* name;
size_t size;
size_t offset;
};
// 结构体元数据(包含总大小)
struct DynamicStructInfo {
const char* name;
size_t count;
size_t total_size;
struct DynamicStructMember* members;
};
// 定义结构体元数据的宏(直接使用 struct 名称)
#define DYNAMIC_STRUCT_BEGIN(struct_name) \
static struct DynamicStructMember struct_name##_members[] = {
#define DEFINE_MEMBER(struct_name, member) \
{ \
.name = #member, \
.size = sizeof(((struct struct_name*)0)->member), \
.offset = offsetof(struct struct_name, member) \
},
#define DYNAMIC_STRUCT_END(struct_name) \
}; \
static struct DynamicStructInfo struct_name##_info = { \
.name = #struct_name, \
.count = sizeof(struct_name##_members) / sizeof(struct DynamicStructMember), \
.total_size = sizeof(struct struct_name), \
.members = struct_name##_members \
};
// ==================================================================================
#include <linux/version.h>
#define KERNEL_VERSION_6_1 KERNEL_VERSION(6, 1, 0)
#define KERNEL_VERSION_5_15 KERNEL_VERSION(5, 15, 0)
#include <../fs/mount.h>
#include <linux/mount.h>
// 定义元数据
DYNAMIC_STRUCT_BEGIN(mount)
DEFINE_MEMBER(mount, mnt_parent)
DEFINE_MEMBER(mount, mnt)
DEFINE_MEMBER(mount, mnt_id)
DEFINE_MEMBER(mount, mnt_group_id)
DEFINE_MEMBER(mount, mnt_expiry_mark)
DEFINE_MEMBER(mount, mnt_master)
DEFINE_MEMBER(mount, mnt_devname)
DYNAMIC_STRUCT_END(mount)
DYNAMIC_STRUCT_BEGIN(vfsmount)
DEFINE_MEMBER(vfsmount, mnt_root)
DEFINE_MEMBER(vfsmount, mnt_sb)
DEFINE_MEMBER(vfsmount, mnt_flags)
DYNAMIC_STRUCT_END(vfsmount)
DYNAMIC_STRUCT_BEGIN(mnt_namespace)
DEFINE_MEMBER(mnt_namespace, ns)
DEFINE_MEMBER(mnt_namespace, root)
DEFINE_MEMBER(mnt_namespace, seq)
DEFINE_MEMBER(mnt_namespace, mounts)
#if LINUX_VERSION_CODE < KERNEL_VERSION_5_15
DEFINE_MEMBER(mnt_namespace, count)
#endif
DYNAMIC_STRUCT_END(mnt_namespace)
#include <linux/kprobes.h>
#ifdef CONFIG_KPROBES
DYNAMIC_STRUCT_BEGIN(kprobe)
DEFINE_MEMBER(kprobe, addr)
DEFINE_MEMBER(kprobe, symbol_name)
DEFINE_MEMBER(kprobe, offset)
DEFINE_MEMBER(kprobe, pre_handler)
DEFINE_MEMBER(kprobe, post_handler)
#if LINUX_VERSION_CODE < KERNEL_VERSION_5_15
DEFINE_MEMBER(kprobe, fault_handler)
#endif
DEFINE_MEMBER(kprobe, flags)
DYNAMIC_STRUCT_END(kprobe)
#endif
#include <linux/mm.h>
#include <linux/mm_types.h>
DYNAMIC_STRUCT_BEGIN(vm_area_struct)
DEFINE_MEMBER(vm_area_struct,vm_start)
DEFINE_MEMBER(vm_area_struct,vm_end)
DEFINE_MEMBER(vm_area_struct,vm_flags)
DEFINE_MEMBER(vm_area_struct,anon_vma)
DEFINE_MEMBER(vm_area_struct,vm_pgoff)
DEFINE_MEMBER(vm_area_struct,vm_file)
DEFINE_MEMBER(vm_area_struct,vm_private_data)
#ifdef CONFIG_ANON_VMA_NAME
DEFINE_MEMBER(vm_area_struct, anon_name)
#endif
DEFINE_MEMBER(vm_area_struct, vm_ops)
DYNAMIC_STRUCT_END(vm_area_struct)
DYNAMIC_STRUCT_BEGIN(vm_operations_struct)
DEFINE_MEMBER(vm_operations_struct, open)
DEFINE_MEMBER(vm_operations_struct, close)
DEFINE_MEMBER(vm_operations_struct, name)
DEFINE_MEMBER(vm_operations_struct, access)
DYNAMIC_STRUCT_END(vm_operations_struct)
#include <linux/netlink.h>
DYNAMIC_STRUCT_BEGIN(netlink_kernel_cfg)
DEFINE_MEMBER(netlink_kernel_cfg, groups)
DEFINE_MEMBER(netlink_kernel_cfg, flags)
DEFINE_MEMBER(netlink_kernel_cfg, input)
DEFINE_MEMBER(netlink_kernel_cfg, cb_mutex)
DEFINE_MEMBER(netlink_kernel_cfg, bind)
DEFINE_MEMBER(netlink_kernel_cfg, unbind)
#if LINUX_VERSION_CODE < KERNEL_VERSION_6_1
DEFINE_MEMBER(netlink_kernel_cfg, compare)
#endif
DYNAMIC_STRUCT_END(netlink_kernel_cfg)
#include <linux/sched.h>
DYNAMIC_STRUCT_BEGIN(task_struct)
DEFINE_MEMBER(task_struct, pid)
DEFINE_MEMBER(task_struct, tgid)
DEFINE_MEMBER(task_struct, cred)
DEFINE_MEMBER(task_struct, real_cred)
DEFINE_MEMBER(task_struct, comm)
DEFINE_MEMBER(task_struct, parent)
DEFINE_MEMBER(task_struct, group_leader)
DEFINE_MEMBER(task_struct, mm)
DEFINE_MEMBER(task_struct, active_mm)
DEFINE_MEMBER(task_struct, thread_pid)
DEFINE_MEMBER(task_struct, files)
DEFINE_MEMBER(task_struct, seccomp)
#ifdef CONFIG_THREAD_INFO_IN_TASK
DEFINE_MEMBER(task_struct, thread_info)
#endif
#ifdef CONFIG_CGROUPS
DEFINE_MEMBER(task_struct, cgroups)
#endif
#ifdef CONFIG_SECURITY
DEFINE_MEMBER(task_struct, security)
#endif
DEFINE_MEMBER(task_struct, thread)
DYNAMIC_STRUCT_END(task_struct)
// =====================================================================================================================
#define STRUCT_INFO(name) &(name##_info)
static
struct DynamicStructInfo* dynamic_struct_infos[] = {
STRUCT_INFO(mount),
STRUCT_INFO(vfsmount),
STRUCT_INFO(mnt_namespace),
#ifdef CONFIG_KPROBES
STRUCT_INFO(kprobe),
#endif
STRUCT_INFO(vm_area_struct),
STRUCT_INFO(vm_operations_struct),
STRUCT_INFO(netlink_kernel_cfg),
STRUCT_INFO(task_struct)
};
// return 0 if successful
// return -1 if struct not defined
int sukisu_super_find_struct(
const char* struct_name,
size_t* out_size,
int* out_members
) {
for(size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
struct DynamicStructInfo* info = dynamic_struct_infos[i];
if(strcmp(struct_name, info->name) == 0) {
if(out_size)
*out_size = info->total_size;
if(out_members)
*out_members = info->count;
return 0;
}
}
return -1;
}
EXPORT_SYMBOL(sukisu_super_find_struct);
// Dynamic access struct
// return 0 if successful
// return -1 if struct not defined
// return -2 if member not defined
int sukisu_super_access (
const char* struct_name,
const char* member_name,
size_t* out_offset,
size_t* out_size
) {
for(size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
struct DynamicStructInfo* info = dynamic_struct_infos[i];
if(strcmp(struct_name, info->name) == 0) {
for (size_t i1 = 0; i1 < info->count; i1++) {
if (strcmp(info->members[i1].name, member_name) == 0) {
if(out_offset)
*out_offset = info->members[i].offset;
if(out_size)
*out_size = info->members[i].size;
return 0;
}
}
return -2;
}
}
return -1;
}
EXPORT_SYMBOL(sukisu_super_access);
// 动态 container_of 宏
#define DYNAMIC_CONTAINER_OF(offset, member_ptr) ({ \
(offset != (size_t)-1) ? (void*)((char*)(member_ptr) - offset) : NULL; \
})
// Dynamic container_of
// return 0 if success
// return -1 if current struct not defined
// return -2 if target member not defined
int sukisu_super_container_of(
const char* struct_name,
const char* member_name,
void* ptr,
void** out_ptr
) {
if(ptr == NULL) {
return -3;
}
for(size_t i = 0; i < (sizeof(dynamic_struct_infos) / sizeof(dynamic_struct_infos[0])); i++) {
struct DynamicStructInfo* info = dynamic_struct_infos[i];
if(strcmp(struct_name, info->name) == 0) {
for (size_t i1 = 0; i1 < info->count; i1++) {
if (strcmp(info->members[i1].name, member_name) == 0) {
*out_ptr = (void*) DYNAMIC_CONTAINER_OF(info->members[i1].offset, ptr);
return 0;
}
}
return -2;
}
}
return -1;
}
EXPORT_SYMBOL(sukisu_super_container_of);

View File

@@ -0,0 +1,39 @@
#ifndef __SUKISU_SUPER_ACCESS_H
#define __SUKISU_SUPER_ACCESS_H
#include <linux/types.h>
#include <linux/stddef.h>
#include "kpm.h"
#include "compact.h"
// return 0 if successful
// return -1 if struct not defined
int sukisu_super_find_struct(
const char* struct_name,
size_t* out_size,
int* out_members
);
// Dynamic access struct
// return 0 if successful
// return -1 if struct not defined
// return -2 if member not defined
int sukisu_super_access (
const char* struct_name,
const char* member_name,
size_t* out_offset,
size_t* out_size
);
// Dynamic container_of
// return 0 if success
// return -1 if current struct not defined
// return -2 if target member not defined
int sukisu_super_container_of(
const char* struct_name,
const char* member_name,
void* ptr,
void** out_ptr
);
#endif

View File

@@ -25,7 +25,7 @@ apksign {
} }
android { android {
namespace = "shirkneko.zako.sukisu" namespace = "zako.zako.zako"
buildTypes { buildTypes {
release { release {

View File

@@ -1,5 +1,4 @@
// IKsuInterface.aidl package zako.zako.zako;
package shirkneko.zako.sukisu;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import rikka.parcelablelist.ParcelableListSlice; import rikka.parcelablelist.ParcelableListSlice;

View File

@@ -12,7 +12,7 @@
extern "C" extern "C"
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_shirkneko_zako_sukisu_Natives_becomeManager(JNIEnv *env, jobject, jstring pkg) { Java_zako_zako_zako_Natives_becomeManager(JNIEnv *env, jobject, jstring pkg) {
auto cpkg = env->GetStringUTFChars(pkg, nullptr); auto cpkg = env->GetStringUTFChars(pkg, nullptr);
auto result = become_manager(cpkg); auto result = become_manager(cpkg);
env->ReleaseStringUTFChars(pkg, cpkg); env->ReleaseStringUTFChars(pkg, cpkg);
@@ -21,13 +21,13 @@ Java_shirkneko_zako_sukisu_Natives_becomeManager(JNIEnv *env, jobject, jstring p
extern "C" extern "C"
JNIEXPORT jint JNICALL JNIEXPORT jint JNICALL
Java_shirkneko_zako_sukisu_Natives_getVersion(JNIEnv *env, jobject) { Java_zako_zako_zako_Natives_getVersion(JNIEnv *env, jobject) {
return get_version(); return get_version();
} }
extern "C" extern "C"
JNIEXPORT jintArray JNICALL JNIEXPORT jintArray JNICALL
Java_shirkneko_zako_sukisu_Natives_getAllowList(JNIEnv *env, jobject) { Java_zako_zako_zako_Natives_getAllowList(JNIEnv *env, jobject) {
int uids[1024]; int uids[1024];
int size = 0; int size = 0;
bool result = get_allow_list(uids, &size); bool result = get_allow_list(uids, &size);
@@ -42,13 +42,13 @@ Java_shirkneko_zako_sukisu_Natives_getAllowList(JNIEnv *env, jobject) {
extern "C" extern "C"
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_shirkneko_zako_sukisu_Natives_isSafeMode(JNIEnv *env, jclass clazz) { Java_zako_zako_zako_Natives_isSafeMode(JNIEnv *env, jclass clazz) {
return is_safe_mode(); return is_safe_mode();
} }
extern "C" extern "C"
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_shirkneko_zako_sukisu_Natives_isLkmMode(JNIEnv *env, jclass clazz) { Java_zako_zako_zako_Natives_isLkmMode(JNIEnv *env, jclass clazz) {
return is_lkm_mode(); return is_lkm_mode();
} }
@@ -111,7 +111,7 @@ static void fillArrayWithList(JNIEnv *env, jobject list, int *data, int count) {
extern "C" extern "C"
JNIEXPORT jobject JNICALL JNIEXPORT jobject JNICALL
Java_shirkneko_zako_sukisu_Natives_getAppProfile(JNIEnv *env, jobject, jstring pkg, jint uid) { Java_zako_zako_zako_Natives_getAppProfile(JNIEnv *env, jobject, jstring pkg, jint uid) {
if (env->GetStringLength(pkg) > KSU_MAX_PACKAGE_NAME) { if (env->GetStringLength(pkg) > KSU_MAX_PACKAGE_NAME) {
return nullptr; return nullptr;
} }
@@ -129,7 +129,7 @@ Java_shirkneko_zako_sukisu_Natives_getAppProfile(JNIEnv *env, jobject, jstring p
bool useDefaultProfile = !get_app_profile(key, &profile); bool useDefaultProfile = !get_app_profile(key, &profile);
auto cls = env->FindClass("shirkneko/zako/sukisu/Natives$Profile"); auto cls = env->FindClass("zako/zako/zako/Natives$Profile");
auto constructor = env->GetMethodID(cls, "<init>", "()V"); auto constructor = env->GetMethodID(cls, "<init>", "()V");
auto obj = env->NewObject(cls, constructor); auto obj = env->NewObject(cls, constructor);
auto keyField = env->GetFieldID(cls, "name", "Ljava/lang/String;"); auto keyField = env->GetFieldID(cls, "name", "Ljava/lang/String;");
@@ -207,8 +207,8 @@ Java_shirkneko_zako_sukisu_Natives_getAppProfile(JNIEnv *env, jobject, jstring p
extern "C" extern "C"
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_shirkneko_zako_sukisu_Natives_setAppProfile(JNIEnv *env, jobject clazz, jobject profile) { Java_zako_zako_zako_Natives_setAppProfile(JNIEnv *env, jobject clazz, jobject profile) {
auto cls = env->FindClass("shirkneko/zako/sukisu/Natives$Profile"); auto cls = env->FindClass("zako/zako/zako/Natives$Profile");
auto keyField = env->GetFieldID(cls, "name", "Ljava/lang/String;"); auto keyField = env->GetFieldID(cls, "name", "Ljava/lang/String;");
auto currentUidField = env->GetFieldID(cls, "currentUid", "I"); auto currentUidField = env->GetFieldID(cls, "currentUid", "I");
@@ -293,16 +293,16 @@ Java_shirkneko_zako_sukisu_Natives_setAppProfile(JNIEnv *env, jobject clazz, job
} }
extern "C" extern "C"
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_shirkneko_zako_sukisu_Natives_uidShouldUmount(JNIEnv *env, jobject thiz, jint uid) { Java_zako_zako_zako_Natives_uidShouldUmount(JNIEnv *env, jobject thiz, jint uid) {
return uid_should_umount(uid); return uid_should_umount(uid);
} }
extern "C" extern "C"
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_shirkneko_zako_sukisu_Natives_isSuEnabled(JNIEnv *env, jobject thiz) { Java_zako_zako_zako_Natives_isSuEnabled(JNIEnv *env, jobject thiz) {
return is_su_enabled(); return is_su_enabled();
} }
extern "C" extern "C"
JNIEXPORT jboolean JNICALL JNIEXPORT jboolean JNICALL
Java_shirkneko_zako_sukisu_Natives_setSuEnabled(JNIEnv *env, jobject thiz, jboolean enabled) { Java_zako_zako_zako_Natives_setSuEnabled(JNIEnv *env, jobject thiz, jboolean enabled) {
return set_su_enabled(enabled); return set_su_enabled(enabled);
} }

View File

@@ -0,0 +1,28 @@
package io.zako.zako;
import java.util.ArrayList;
import zako.zako.zako.ui.util.KsuCli;
public class UltraShellHelper {
public static String runCmd(String cmds) {
StringBuilder sb = new StringBuilder();
for(String str : KsuCli.INSTANCE.getGLOBAL_MNT_SHELL()
.newJob()
.add(cmds)
.to(new ArrayList<>(), null)
.exec()
.getOut()) {
sb.append(str).append("\n");
}
return sb.toString();
}
public static boolean isPathExists(String path) {
return !runCmd("file " + path).contains("No such file or directory");
}
public static void CopyFileTo(String path, String target) {
runCmd("cp -f " + path + " " + target);
}
}

View File

@@ -0,0 +1,14 @@
package io.zako.zako;
import static zako.zako.zako.ui.util.KsuCliKt.getKpmmgrPath;
public class UltraToolInstall {
private static final String OUTSIDE_KPMMGR_PATH = "/data/adb/ksu/bin/kpmmgr";
public static void tryToInstall() {
String kpmmgrPath = getKpmmgrPath();
if (!UltraShellHelper.isPathExists(OUTSIDE_KPMMGR_PATH)) {
UltraShellHelper.CopyFileTo(kpmmgrPath, OUTSIDE_KPMMGR_PATH);
UltraShellHelper.runCmd("chmod a+rx " + OUTSIDE_KPMMGR_PATH);
}
}
}

View File

@@ -1,297 +0,0 @@
package shirkneko.zako.sukisu.ui.screen
import android.app.Activity.RESULT_OK
import android.content.Intent
import android.util.Log
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.material3.*
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.launch
import shirkneko.zako.sukisu.R
import shirkneko.zako.sukisu.ui.component.ConfirmResult
import shirkneko.zako.sukisu.ui.component.SearchAppBar
import shirkneko.zako.sukisu.ui.component.rememberConfirmDialog
import shirkneko.zako.sukisu.ui.component.rememberLoadingDialog
import shirkneko.zako.sukisu.ui.theme.getCardColors
import shirkneko.zako.sukisu.ui.theme.getCardElevation
import shirkneko.zako.sukisu.ui.viewmodel.KpmViewModel
import shirkneko.zako.sukisu.ui.util.loadKpmModule
import shirkneko.zako.sukisu.ui.util.unloadKpmModule
import java.io.File
@OptIn(ExperimentalMaterial3Api::class)
@Destination<RootGraph>
@Composable
fun KpmScreen(
navigator: DestinationsNavigator,
viewModel: KpmViewModel = viewModel()
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val snackBarHost = remember { SnackbarHostState() }
val confirmDialog = rememberConfirmDialog()
val loadingDialog = rememberLoadingDialog()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
val kpmInstall = stringResource(R.string.kpm_install)
val kpmInstallConfirm = stringResource(R.string.kpm_install_confirm)
val kpmInstallSuccess = stringResource(R.string.kpm_install_success)
val kpmInstallFailed = stringResource(R.string.kpm_install_failed)
val install = stringResource(R.string.install)
val cancel = stringResource(R.string.cancel)
val kpmUninstall = stringResource(R.string.kpm_uninstall)
val kpmUninstallConfirmTemplate = stringResource(R.string.kpm_uninstall_confirm)
val uninstall = stringResource(R.string.uninstall)
val kpmUninstallSuccess = stringResource(R.string.kpm_uninstall_success)
val kpmUninstallFailed = stringResource(R.string.kpm_uninstall_failed)
val selectPatchLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode != RESULT_OK) return@rememberLauncherForActivityResult
val uri = result.data?.data ?: return@rememberLauncherForActivityResult
scope.launch {
// 复制文件到临时目录
val tempFile = File(context.cacheDir, "temp_patch.kpm")
context.contentResolver.openInputStream(uri)?.use { input ->
tempFile.outputStream().use { output ->
input.copyTo(output)
}
}
val confirmResult = confirmDialog.awaitConfirm(
title = kpmInstall,
content = kpmInstallConfirm,
confirm = install,
dismiss = cancel
)
if (confirmResult == ConfirmResult.Confirmed) {
val success = loadingDialog.withLoading {
loadKpmModule(tempFile.absolutePath)
}
Log.d("KsuCli", "loadKpmModule result: $success")
if (success == "success") {
viewModel.fetchModuleList()
snackBarHost.showSnackbar(
message = kpmInstallSuccess,
duration = SnackbarDuration.Long
)
} else {
// 修正为显示安装失败的消息
snackBarHost.showSnackbar(
message = kpmInstallFailed,
duration = SnackbarDuration.Long
)
}
}
tempFile.delete()
}
}
LaunchedEffect(Unit) {
if (viewModel.moduleList.isEmpty()) {
viewModel.fetchModuleList()
}
}
Scaffold(
topBar = {
SearchAppBar(
title = { Text(stringResource(R.string.kpm_title)) },
searchText = viewModel.search,
onSearchTextChange = { viewModel.search = it },
onClearClick = { viewModel.search = "" },
scrollBehavior = scrollBehavior,
dropdownContent = {
IconButton(onClick = { viewModel.fetchModuleList() }) {
Icon(
imageVector = Icons.Outlined.Refresh,
contentDescription = stringResource(R.string.refresh)
)
}
}
)
},
floatingActionButton = {
ExtendedFloatingActionButton(
onClick = {
selectPatchLauncher.launch(
Intent(Intent.ACTION_GET_CONTENT).apply {
type = "application/*"
}
)
},
icon = {
Icon(
imageVector = Icons.Outlined.Add,
contentDescription = stringResource(R.string.kpm_install)
)
},
text = { Text(stringResource(R.string.kpm_install)) },
containerColor = MaterialTheme.colorScheme.secondaryContainer,
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
)
},
snackbarHost = { SnackbarHost(snackBarHost) }
) { padding ->
PullToRefreshBox(
onRefresh = { viewModel.fetchModuleList() },
isRefreshing = viewModel.isRefreshing,
modifier = Modifier.padding(padding)
) {
if (viewModel.moduleList.isEmpty()) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
stringResource(R.string.kpm_empty),
textAlign = TextAlign.Center
)
}
} else {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
items(viewModel.moduleList) { module ->
val kpmUninstallConfirm = String.format(kpmUninstallConfirmTemplate, module.name)
KpmModuleItem(
module = module,
onUninstall = {
scope.launch {
val confirmResult = confirmDialog.awaitConfirm(
title = kpmUninstall,
content = kpmUninstallConfirm,
confirm = uninstall,
dismiss = cancel
)
if (confirmResult == ConfirmResult.Confirmed) {
val success = loadingDialog.withLoading {
unloadKpmModule(module.id)
}
Log.d("KsuCli", "unloadKpmModule result: $success")
if (success == "success") {
viewModel.fetchModuleList()
snackBarHost.showSnackbar(
message = kpmUninstallSuccess,
duration = SnackbarDuration.Long
)
} else {
snackBarHost.showSnackbar(
message = kpmUninstallFailed,
duration = SnackbarDuration.Long
)
}
}
}
},
onControl = {
viewModel.loadModuleDetail(module.id)
}
)
}
}
}
}
}
}
@Composable
private fun KpmModuleItem(
module: KpmViewModel.ModuleInfo,
onUninstall: () -> Unit,
onControl: () -> Unit
) {
ElevatedCard(
colors = getCardColors(MaterialTheme.colorScheme.secondaryContainer),
elevation = CardDefaults.cardElevation(defaultElevation = getCardElevation())
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Column(modifier = Modifier.weight(1f)) {
Text(
text = module.name,
style = MaterialTheme.typography.titleMedium
)
Text(
text = "${stringResource(R.string.kpm_version)}: ${module.version}",
style = MaterialTheme.typography.bodyMedium
)
Text(
text = "${stringResource(R.string.kpm_author)}: ${module.author}",
style = MaterialTheme.typography.bodyMedium
)
Text(
text = "${stringResource(R.string.kpm_args)}: ${module.args}",
style = MaterialTheme.typography.bodyMedium
)
}
}
Spacer(modifier = Modifier.height(8.dp))
Text(
text = module.description,
style = MaterialTheme.typography.bodyMedium
)
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
FilledTonalButton(
onClick = onControl
) {
Icon(
imageVector = Icons.Outlined.Settings,
contentDescription = null
)
Text(stringResource(R.string.kpm_control))
}
FilledTonalButton(
onClick = onUninstall
) {
Icon(
imageVector = Icons.Outlined.Delete,
contentDescription = null
)
Text(stringResource(R.string.kpm_uninstall))
}
}
}
}
}

View File

@@ -1,98 +0,0 @@
package shirkneko.zako.sukisu.ui.viewmodel
import android.util.Log
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import shirkneko.zako.sukisu.ui.util.*
class KpmViewModel : ViewModel() {
var moduleList by mutableStateOf(emptyList<ModuleInfo>())
private set
var search by mutableStateOf("")
internal set
var isRefreshing by mutableStateOf(false)
private set
var currentModuleDetail by mutableStateOf("")
private set
fun loadModuleDetail(moduleId: String) {
viewModelScope.launch {
currentModuleDetail = withContext(Dispatchers.IO) {
try {
getKpmModuleInfo(moduleId)
} catch (e: Exception) {
"无法获取模块详细信息: ${e.message}"
}
}
Log.d("KsuCli", "Module detail: $currentModuleDetail")
}
}
fun fetchModuleList() {
viewModelScope.launch {
isRefreshing = true
try {
val moduleCount = getKpmModuleCount()
Log.d("KsuCli", "Module count: $moduleCount")
val moduleInfo = listKpmModules()
Log.d("KsuCli", "Module info: $moduleInfo")
val modules = parseModuleList(moduleInfo)
moduleList = modules
} finally {
isRefreshing = false
}
}
}
private fun getInstalledKernelPatches(): List<ModuleInfo> {
return try {
val output = printKpmModules()
parseModuleList(output)
} catch (e: Exception) {
emptyList()
}
}
private fun parseModuleList(output: String): List<ModuleInfo> {
return output.split("\n").mapNotNull { line ->
if (line.isBlank()) return@mapNotNull null
val parts = line.split("|")
if (parts.size < 7) return@mapNotNull null
ModuleInfo(
id = parts[0].trim(),
name = parts[1].trim(),
version = parts[2].trim(),
author = parts[3].trim(),
description = parts[4].trim(),
args = parts[6].trim(),
enabled = true,
hasAction = controlKpmModule(parts[0].trim()).isNotBlank()
)
}
}
data class ModuleInfo(
val id: String,
val name: String,
val version: String,
val author: String,
val description: String,
val args: String,
val enabled: Boolean,
val hasAction: Boolean
)
}

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu package zako.zako.zako
import android.app.Application import android.app.Application
import coil.Coil import coil.Coil

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu package zako.zako.zako
import android.system.Os import android.system.Os

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu package zako.zako.zako
import android.os.Parcelable import android.os.Parcelable
import androidx.annotation.Keep import androidx.annotation.Keep

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.profile package zako.zako.zako.profile
/** /**
* @author weishu * @author weishu

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.profile package zako.zako.zako.profile
/** /**
* https://cs.android.com/android/platform/superproject/main/+/main:system/core/libcutils/include/private/android_filesystem_config.h * https://cs.android.com/android/platform/superproject/main/+/main:system/core/libcutils/include/private/android_filesystem_config.h

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui; package zako.zako.zako.ui;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
@@ -17,7 +17,7 @@ import java.lang.reflect.Method;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import shirkneko.zako.sukisu.IKsuInterface; import zako.zako.zako.IKsuInterface;
import rikka.parcelablelist.ParcelableListSlice; import rikka.parcelablelist.ParcelableListSlice;
/** /**

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui package zako.zako.zako.ui
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
@@ -30,8 +30,6 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.navigation.NavBackStackEntry import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
@@ -41,16 +39,18 @@ import com.ramcosta.composedestinations.animations.NavHostAnimatedDestinationSty
import com.ramcosta.composedestinations.generated.NavGraphs import com.ramcosta.composedestinations.generated.NavGraphs
import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState import com.ramcosta.composedestinations.utils.isRouteOnBackStackAsState
import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator import com.ramcosta.composedestinations.utils.rememberDestinationsNavigator
import shirkneko.zako.sukisu.Natives import io.zako.zako.UltraToolInstall
import shirkneko.zako.sukisu.ksuApp import zako.zako.zako.Natives
import shirkneko.zako.sukisu.ui.screen.BottomBarDestination import zako.zako.zako.ksuApp
import shirkneko.zako.sukisu.ui.theme.CardConfig import zako.zako.zako.ui.screen.BottomBarDestination
import shirkneko.zako.sukisu.ui.theme.KernelSUTheme import zako.zako.zako.ui.theme.CardConfig
import shirkneko.zako.sukisu.ui.theme.loadCustomBackground import zako.zako.zako.ui.theme.KernelSUTheme
import shirkneko.zako.sukisu.ui.theme.loadThemeMode import zako.zako.zako.ui.theme.loadCustomBackground
import shirkneko.zako.sukisu.ui.util.LocalSnackbarHost import zako.zako.zako.ui.theme.loadThemeMode
import shirkneko.zako.sukisu.ui.util.rootAvailable import zako.zako.zako.ui.util.LocalSnackbarHost
import shirkneko.zako.sukisu.ui.util.install import zako.zako.zako.ui.util.getKpmVersion
import zako.zako.zako.ui.util.rootAvailable
import zako.zako.zako.ui.util.install
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
@@ -71,7 +71,10 @@ class MainActivity : ComponentActivity() {
val isManager = Natives.becomeManager(ksuApp.packageName) val isManager = Natives.becomeManager(ksuApp.packageName)
if (isManager) install() if (isManager) {
install()
UltraToolInstall.tryToInstall()
}
setContent { setContent {
KernelSUTheme { KernelSUTheme {
@@ -107,6 +110,7 @@ private fun BottomBar(navController: NavHostController) {
val navigator = navController.rememberDestinationsNavigator() val navigator = navController.rememberDestinationsNavigator()
val isManager = Natives.becomeManager(ksuApp.packageName) val isManager = Natives.becomeManager(ksuApp.packageName)
val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable() val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable()
val kpmVersion = getKpmVersion()
// 获取卡片颜色和透明度 // 获取卡片颜色和透明度
val cardColor = MaterialTheme.colorScheme.secondaryContainer val cardColor = MaterialTheme.colorScheme.secondaryContainer
@@ -115,42 +119,72 @@ private fun BottomBar(navController: NavHostController) {
NavigationBar( NavigationBar(
tonalElevation = cardElevation, // 动态设置阴影 tonalElevation = cardElevation, // 动态设置阴影
containerColor = cardColor.copy(alpha = cardAlpha), // 动态设置颜色和透明度 containerColor = cardColor.copy(alpha = cardAlpha),
contentColor = if (cardColor.luminance() > 0.5) Color.Black else Color.White, // 根据背景亮度设置文字颜色
windowInsets = WindowInsets.systemBars.union(WindowInsets.displayCutout).only( windowInsets = WindowInsets.systemBars.union(WindowInsets.displayCutout).only(
WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom WindowInsetsSides.Horizontal + WindowInsetsSides.Bottom
) )
) { ) {
BottomBarDestination.entries.forEach { destination -> BottomBarDestination.entries.forEach { destination ->
if (!fullFeatured && destination.rootRequired) return@forEach if (destination == BottomBarDestination.Kpm) {
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction) if (kpmVersion.isNotEmpty() && !kpmVersion.startsWith("Error")) {
NavigationBarItem( if (!fullFeatured && destination.rootRequired) return@forEach
selected = isCurrentDestOnBackStack, val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
onClick = { NavigationBarItem(
if (isCurrentDestOnBackStack) { selected = isCurrentDestOnBackStack,
navigator.popBackStack(destination.direction, false) onClick = {
} if (isCurrentDestOnBackStack) {
navigator.navigate(destination.direction) { navigator.popBackStack(destination.direction, false)
popUpTo(NavGraphs.root) { }
saveState = true navigator.navigate(destination.direction) {
popUpTo(NavGraphs.root) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
},
icon = {
if (isCurrentDestOnBackStack) {
Icon(destination.iconSelected, stringResource(destination.label))
} else {
Icon(destination.iconNotSelected, stringResource(destination.label))
}
},
label = { Text(stringResource(destination.label)) },
alwaysShowLabel = false,
colors = androidx.compose.material3.NavigationBarItemDefaults.colors(
unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant
)
)
}
} else {
if (!fullFeatured && destination.rootRequired) return@forEach
val isCurrentDestOnBackStack by navController.isRouteOnBackStackAsState(destination.direction)
NavigationBarItem(
selected = isCurrentDestOnBackStack,
onClick = {
if (isCurrentDestOnBackStack) {
navigator.popBackStack(destination.direction, false)
} }
launchSingleTop = true navigator.navigate(destination.direction) {
restoreState = true popUpTo(NavGraphs.root) {
} saveState = true
}, }
icon = { launchSingleTop = true
if (isCurrentDestOnBackStack) { restoreState = true
Icon(destination.iconSelected, stringResource(destination.label)) }
} else { },
Icon(destination.iconNotSelected, stringResource(destination.label)) icon = {
} if (isCurrentDestOnBackStack) {
}, Icon(destination.iconSelected, stringResource(destination.label))
label = { Text(stringResource(destination.label)) }, } else {
alwaysShowLabel = false, Icon(destination.iconNotSelected, stringResource(destination.label))
colors = androidx.compose.material3.NavigationBarItemDefaults.colors( }
unselectedTextColor = MaterialTheme.colorScheme.onSurfaceVariant },
label = { Text(stringResource(destination.label)) },
alwaysShowLabel = false,
) )
) }
} }
} }
} }

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.component package zako.zako.zako.ui.component
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -31,8 +31,8 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog import androidx.compose.ui.window.Dialog
import shirkneko.zako.sukisu.BuildConfig import zako.zako.zako.BuildConfig
import shirkneko.zako.sukisu.R import zako.zako.zako.R
@Preview @Preview
@Composable @Composable
@@ -98,7 +98,7 @@ private fun AboutCardContent() {
val annotatedString = AnnotatedString.Companion.fromHtml( val annotatedString = AnnotatedString.Companion.fromHtml(
htmlString = stringResource( htmlString = stringResource(
id = R.string.about_source_code, id = R.string.about_source_code,
"<b><a href=\"https://github.com/ShirkNeko/KernelSU\">GitHub</a></b>", "<b><a href=\"https://github.com/ShirkNeko/SukiSU-Ultra\">GitHub</a></b>",
"<b><a href=\"https://t.me/SukiKSU\">Telegram</a></b>" "<b><a href=\"https://t.me/SukiKSU\">Telegram</a></b>"
), ),
linkStyles = TextLinkStyles( linkStyles = TextLinkStyles(

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.component package zako.zako.zako.ui.component
import android.graphics.text.LineBreaker import android.graphics.text.LineBreaker
import android.os.Build import android.os.Build

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.component package zako.zako.zako.ui.component
import androidx.compose.foundation.focusable import androidx.compose.foundation.focusable
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.component package zako.zako.zako.ui.component
import android.util.Log import android.util.Log
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
@@ -42,7 +42,7 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import shirkneko.zako.sukisu.ui.theme.CardConfig import zako.zako.zako.ui.theme.CardConfig
private const val TAG = "SearchBar" private const val TAG = "SearchBar"

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.component package zako.zako.zako.ui.component
import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.component package zako.zako.zako.ui.component
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem import androidx.compose.material3.ListItem

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.component.profile package zako.zako.zako.ui.component.profile
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
@@ -11,9 +11,9 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import shirkneko.zako.sukisu.Natives import zako.zako.zako.Natives
import shirkneko.zako.sukisu.R import zako.zako.zako.R
import shirkneko.zako.sukisu.ui.component.SwitchItem import zako.zako.zako.ui.component.SwitchItem
@Composable @Composable
fun AppProfileConfig( fun AppProfileConfig(

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.component.profile package zako.zako.zako.ui.component.profile
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -42,12 +42,12 @@ import com.maxkeppeler.sheets.input.models.ValidationResult
import com.maxkeppeler.sheets.list.ListDialog import com.maxkeppeler.sheets.list.ListDialog
import com.maxkeppeler.sheets.list.models.ListOption import com.maxkeppeler.sheets.list.models.ListOption
import com.maxkeppeler.sheets.list.models.ListSelection import com.maxkeppeler.sheets.list.models.ListSelection
import shirkneko.zako.sukisu.Natives import zako.zako.zako.Natives
import shirkneko.zako.sukisu.R import zako.zako.zako.R
import shirkneko.zako.sukisu.profile.Capabilities import zako.zako.zako.profile.Capabilities
import shirkneko.zako.sukisu.profile.Groups import zako.zako.zako.profile.Groups
import shirkneko.zako.sukisu.ui.component.rememberCustomDialog import zako.zako.zako.ui.component.rememberCustomDialog
import shirkneko.zako.sukisu.ui.util.isSepolicyValid import zako.zako.zako.ui.util.isSepolicyValid
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.component.profile package zako.zako.zako.ui.component.profile
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@@ -23,11 +23,11 @@ import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import shirkneko.zako.sukisu.Natives import zako.zako.zako.Natives
import shirkneko.zako.sukisu.R import zako.zako.zako.R
import shirkneko.zako.sukisu.ui.util.listAppProfileTemplates import zako.zako.zako.ui.util.listAppProfileTemplates
import shirkneko.zako.sukisu.ui.util.setSepolicy import zako.zako.zako.ui.util.setSepolicy
import shirkneko.zako.sukisu.ui.viewmodel.getTemplateInfoById import zako.zako.zako.ui.viewmodel.getTemplateInfoById
/** /**
* @author weishu * @author weishu

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.screen package zako.zako.zako.ui.screen
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.animation.Crossfade import androidx.compose.animation.Crossfade
@@ -64,20 +64,20 @@ import com.ramcosta.composedestinations.generated.destinations.AppProfileTemplat
import com.ramcosta.composedestinations.generated.destinations.TemplateEditorScreenDestination import com.ramcosta.composedestinations.generated.destinations.TemplateEditorScreenDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import shirkneko.zako.sukisu.Natives import zako.zako.zako.Natives
import shirkneko.zako.sukisu.R import zako.zako.zako.R
import shirkneko.zako.sukisu.ui.component.SwitchItem import zako.zako.zako.ui.component.SwitchItem
import shirkneko.zako.sukisu.ui.component.profile.AppProfileConfig import zako.zako.zako.ui.component.profile.AppProfileConfig
import shirkneko.zako.sukisu.ui.component.profile.RootProfileConfig import zako.zako.zako.ui.component.profile.RootProfileConfig
import shirkneko.zako.sukisu.ui.component.profile.TemplateConfig import zako.zako.zako.ui.component.profile.TemplateConfig
import shirkneko.zako.sukisu.ui.util.LocalSnackbarHost import zako.zako.zako.ui.util.LocalSnackbarHost
import shirkneko.zako.sukisu.ui.util.forceStopApp import zako.zako.zako.ui.util.forceStopApp
import shirkneko.zako.sukisu.ui.util.getSepolicy import zako.zako.zako.ui.util.getSepolicy
import shirkneko.zako.sukisu.ui.util.launchApp import zako.zako.zako.ui.util.launchApp
import shirkneko.zako.sukisu.ui.util.restartApp import zako.zako.zako.ui.util.restartApp
import shirkneko.zako.sukisu.ui.util.setSepolicy import zako.zako.zako.ui.util.setSepolicy
import shirkneko.zako.sukisu.ui.viewmodel.SuperUserViewModel import zako.zako.zako.ui.viewmodel.SuperUserViewModel
import shirkneko.zako.sukisu.ui.viewmodel.getTemplateInfoById import zako.zako.zako.ui.viewmodel.getTemplateInfoById
/** /**
* @author weishu * @author weishu

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.screen package zako.zako.zako.ui.screen
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@@ -11,7 +11,7 @@ import com.ramcosta.composedestinations.generated.destinations.SuperUserScreenDe
import com.ramcosta.composedestinations.generated.destinations.SettingScreenDestination import com.ramcosta.composedestinations.generated.destinations.SettingScreenDestination
import com.ramcosta.composedestinations.generated.destinations.KpmScreenDestination import com.ramcosta.composedestinations.generated.destinations.KpmScreenDestination
import com.ramcosta.composedestinations.spec.DirectionDestinationSpec import com.ramcosta.composedestinations.spec.DirectionDestinationSpec
import shirkneko.zako.sukisu.R import zako.zako.zako.R
enum class BottomBarDestination( enum class BottomBarDestination(
val direction: DirectionDestinationSpec, val direction: DirectionDestinationSpec,
@@ -21,8 +21,8 @@ enum class BottomBarDestination(
val rootRequired: Boolean, val rootRequired: Boolean,
) { ) {
Home(HomeScreenDestination, R.string.home, Icons.Filled.Home, Icons.Outlined.Home, false), Home(HomeScreenDestination, R.string.home, Icons.Filled.Home, Icons.Outlined.Home, false),
Kpm(KpmScreenDestination, R.string.kpm_title, Icons.Filled.Build, Icons.Outlined.Build, true),
SuperUser(SuperUserScreenDestination, R.string.superuser, Icons.Filled.Security, Icons.Outlined.Security, true), SuperUser(SuperUserScreenDestination, R.string.superuser, Icons.Filled.Security, Icons.Outlined.Security, true),
Module(ModuleScreenDestination, R.string.module, Icons.Filled.Apps, Icons.Outlined.Apps, true), Module(ModuleScreenDestination, R.string.module, Icons.Filled.Apps, Icons.Outlined.Apps, true),
Kpm(KpmScreenDestination, R.string.kpm_title, Icons.Filled.Build, Icons.Outlined.Build, true),
Settings(SettingScreenDestination, R.string.settings, Icons.Filled.Settings, Icons.Outlined.Settings, false), Settings(SettingScreenDestination, R.string.settings, Icons.Filled.Settings, Icons.Outlined.Settings, false),
} }

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.screen package zako.zako.zako.ui.screen
import android.os.Environment import android.os.Environment
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@@ -37,10 +37,10 @@ import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import shirkneko.zako.sukisu.R import zako.zako.zako.R
import shirkneko.zako.sukisu.ui.component.KeyEventBlocker import zako.zako.zako.ui.component.KeyEventBlocker
import shirkneko.zako.sukisu.ui.util.LocalSnackbarHost import zako.zako.zako.ui.util.LocalSnackbarHost
import shirkneko.zako.sukisu.ui.util.runModuleAction import zako.zako.zako.ui.util.runModuleAction
import java.io.File import java.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.screen package zako.zako.zako.ui.screen
import android.net.Uri import android.net.Uri
import android.os.Environment import android.os.Environment
@@ -30,9 +30,9 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import shirkneko.zako.sukisu.R import zako.zako.zako.ui.component.KeyEventBlocker
import shirkneko.zako.sukisu.ui.component.KeyEventBlocker import zako.zako.zako.ui.util.*
import shirkneko.zako.sukisu.ui.util.* import zako.zako.zako.R
import java.io.File import java.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.screen package zako.zako.zako.ui.screen
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Context import android.content.Context
@@ -33,15 +33,15 @@ import com.ramcosta.composedestinations.generated.destinations.SettingScreenDest
import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import shirkneko.zako.sukisu.* import zako.zako.zako.*
import shirkneko.zako.sukisu.R import zako.zako.zako.R
import shirkneko.zako.sukisu.ui.component.rememberConfirmDialog import zako.zako.zako.ui.component.rememberConfirmDialog
import shirkneko.zako.sukisu.ui.util.* import zako.zako.zako.ui.util.*
import shirkneko.zako.sukisu.ui.util.module.LatestVersionInfo import zako.zako.zako.ui.util.module.LatestVersionInfo
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import shirkneko.zako.sukisu.ui.theme.getCardColors import zako.zako.zako.ui.theme.getCardColors
import shirkneko.zako.sukisu.ui.theme.getCardElevation import zako.zako.zako.ui.theme.getCardElevation
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
@@ -49,8 +49,12 @@ import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically import androidx.compose.animation.shrinkVertically
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import shirkneko.zako.sukisu.ui.theme.CardConfig import zako.zako.zako.ui.theme.CardConfig
import androidx.core.content.edit import androidx.core.content.edit
import java.io.BufferedReader
import java.io.InputStreamReader
import java.util.zip.GZIPInputStream
import kotlin.random.Random
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Destination<RootGraph>(start = true) @Destination<RootGraph>(start = true)
@@ -59,6 +63,8 @@ fun HomeScreen(navigator: DestinationsNavigator) {
val context = LocalContext.current val context = LocalContext.current
var isSimpleMode by rememberSaveable { mutableStateOf(false) } var isSimpleMode by rememberSaveable { mutableStateOf(false) }
var isHideVersion by rememberSaveable { mutableStateOf(false) } var isHideVersion by rememberSaveable { mutableStateOf(false) }
var isHideOtherInfo by rememberSaveable { mutableStateOf(false) }
var isHideSusfsStatus by rememberSaveable { mutableStateOf(false) }
// 从 SharedPreferences 加载简洁模式状态 // 从 SharedPreferences 加载简洁模式状态
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
@@ -70,9 +76,38 @@ fun HomeScreen(navigator: DestinationsNavigator) {
isHideVersion = context.getSharedPreferences("settings", Context.MODE_PRIVATE) isHideVersion = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
.getBoolean("is_hide_version", false) .getBoolean("is_hide_version", false)
} }
// 从 SharedPreferences 加载隐藏模块数量等信息开关状态
LaunchedEffect(Unit) {
isHideOtherInfo = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
.getBoolean("is_hide_other_info", false)
}
// 从 SharedPreferences 加载隐藏 SuSFS 状态开关状态
LaunchedEffect(Unit) {
isHideSusfsStatus = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
.getBoolean("is_hide_susfs_status", false)
}
val kernelVersion = getKernelVersion() val kernelVersion = getKernelVersion()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
val isManager = Natives.becomeManager(ksuApp.packageName)
val deviceModel = getDeviceModel(context)
val ksuVersion = if (isManager) Natives.version else null
val managerVersion = getManagerVersion(context).second
val Zako = "一.*加.*A.*c.*e.*5.*P.*r.*o".toRegex().matches(deviceModel)
val isVersion = ksuVersion == 12777
val isManagerVersionValid = managerVersion > (ksuVersion ?: 0) + 33
val shouldTriggerRestart = Zako && kernelVersion.isGKI() && (isVersion || isManagerVersionValid)
LaunchedEffect(shouldTriggerRestart) {
if (shouldTriggerRestart) {
val random = Random.nextInt(0, 100)
if (random <= 95) {
reboot()
} else {
""
}
}
}
Scaffold( Scaffold(
topBar = { topBar = {
@@ -96,6 +131,10 @@ fun HomeScreen(navigator: DestinationsNavigator) {
.padding(horizontal = 16.dp), .padding(horizontal = 16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp) verticalArrangement = Arrangement.spacedBy(16.dp)
) { ) {
if (shouldTriggerRestart) {
WarningCard(message = "zakozako")
return@Column
}
val isManager = Natives.becomeManager(ksuApp.packageName) val isManager = Natives.becomeManager(ksuApp.packageName)
val ksuVersion = if (isManager) Natives.version else null val ksuVersion = if (isManager) Natives.version else null
val lkmMode = ksuVersion?.let { val lkmMode = ksuVersion?.let {
@@ -155,10 +194,10 @@ fun HomeScreen(navigator: DestinationsNavigator) {
} }
InfoCard() InfoCard()
if (!isSimpleMode) { if (!isSimpleMode) {
ContributionCard()
DonateCard() DonateCard()
LearnMoreCard() LearnMoreCard()
} }
Spacer(Modifier) Spacer(Modifier)
} }
} }
@@ -308,6 +347,12 @@ private fun StatusCard(
val isHideVersion = LocalContext.current.getSharedPreferences("settings", Context.MODE_PRIVATE) val isHideVersion = LocalContext.current.getSharedPreferences("settings", Context.MODE_PRIVATE)
.getBoolean("is_hide_version", false) .getBoolean("is_hide_version", false)
val isHideOtherInfo = LocalContext.current.getSharedPreferences("settings", Context.MODE_PRIVATE)
.getBoolean("is_hide_other_info", false)
val isHideSusfsStatus = LocalContext.current.getSharedPreferences("settings", Context.MODE_PRIVATE)
.getBoolean("is_hide_susfs_status", false)
Icon(Icons.Outlined.CheckCircle, stringResource(R.string.home_working)) Icon(Icons.Outlined.CheckCircle, stringResource(R.string.home_working))
Column(Modifier.padding(start = 20.dp)) { Column(Modifier.padding(start = 20.dp)) {
Text( Text(
@@ -321,30 +366,44 @@ private fun StatusCard(
style = MaterialTheme.typography.bodyMedium style = MaterialTheme.typography.bodyMedium
) )
} }
Spacer(Modifier.height(4.dp)) if (!isHideOtherInfo) {
Text( Spacer(Modifier.height(4.dp))
text = stringResource( Text(
R.string.home_superuser_count, getSuperuserCount() text = stringResource(
), style = MaterialTheme.typography.bodyMedium R.string.home_superuser_count, getSuperuserCount()
) ), style = MaterialTheme.typography.bodyMedium
Spacer(Modifier.height(4.dp)) )
Text( Spacer(Modifier.height(4.dp))
text = stringResource(R.string.home_module_count, getModuleCount()), Text(
style = MaterialTheme.typography.bodyMedium text = stringResource(R.string.home_module_count, getModuleCount()),
) style = MaterialTheme.typography.bodyMedium
Spacer(modifier = Modifier.height(4.dp)) )
val kpmVersion = getKpmVersion()
val suSFS = getSuSFS() if (kpmVersion.isNotEmpty() && !kpmVersion.startsWith("Error")) {
val translatedStatus = when (suSFS) { Spacer(Modifier.height(4.dp))
"Supported" -> stringResource(R.string.status_supported) Text(
"Not Supported" -> stringResource(R.string.status_not_supported) text = stringResource(R.string.home_kpm_module, getKpmModuleCount()),
else -> stringResource(R.string.status_unknown) style = MaterialTheme.typography.bodyMedium
)
}
} }
if (!isHideSusfsStatus) {
Spacer(modifier = Modifier.height(4.dp))
Text( val suSFS = getSuSFS()
text = stringResource(R.string.home_susfs, translatedStatus), if (lkmMode != true) {
style = MaterialTheme.typography.bodyMedium val translatedStatus = when (suSFS) {
) "Supported" -> stringResource(R.string.status_supported)
"Not Supported" -> stringResource(R.string.status_not_supported)
else -> stringResource(R.string.status_unknown)
}
Text(
text = stringResource(R.string.home_susfs, translatedStatus),
style = MaterialTheme.typography.bodyMedium
)
}
}
} }
} }
@@ -402,6 +461,38 @@ fun WarningCard(
} }
} }
} }
@Composable
fun ContributionCard() {
val uriHandler = LocalUriHandler.current
val links = listOf("https://github.com/zako", "https://github.com/udochina")
ElevatedCard(
colors = getCardColors(MaterialTheme.colorScheme.secondaryContainer),
elevation = CardDefaults.cardElevation(defaultElevation = getCardElevation())
) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable {
val randomIndex = Random.nextInt(links.size)
uriHandler.openUri(links[randomIndex])
}
.padding(24.dp),
verticalAlignment = Alignment.CenterVertically
) {
Column {
Text(
text = stringResource(R.string.home_ContributionCard_kernelsu),
style = MaterialTheme.typography.titleSmall
)
Spacer(Modifier.height(4.dp))
Text(
text = stringResource(R.string.home_click_to_ContributionCard_kernelsu),
style = MaterialTheme.typography.bodyMedium
)
}
}
}
}
@Composable @Composable
fun LearnMoreCard() { fun LearnMoreCard() {
@@ -478,7 +569,7 @@ private fun InfoCard() {
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(start = 24.dp, top = 24.dp, end = 24.dp, bottom = 16.dp) .padding(start = 24.dp, top = 24.dp, end = 24.dp, bottom = 16.dp)
) { ) withContext@{
val contents = StringBuilder() val contents = StringBuilder()
val uname = Os.uname() val uname = Os.uname()
@@ -492,7 +583,7 @@ private fun InfoCard() {
Text(text = content, style = MaterialTheme.typography.bodyMedium) Text(text = content, style = MaterialTheme.typography.bodyMedium)
} }
InfoCardItem(stringResource(R.string.home_kernel), uname.release) InfoCardItem(stringResource(R.string.home_kernel), uname.release)
if (!isSimpleMode) { if (!isSimpleMode) {
Spacer(Modifier.height(16.dp)) Spacer(Modifier.height(16.dp))
@@ -501,57 +592,70 @@ private fun InfoCard() {
} }
Spacer(Modifier.height(16.dp)) Spacer(Modifier.height(16.dp))
val deviceModel = getDeviceModel(context) val deviceModel = getDeviceModel(context)
InfoCardItem(stringResource(R.string.home_device_model), deviceModel) InfoCardItem(stringResource(R.string.home_device_model), deviceModel)
Spacer(Modifier.height(16.dp)) Spacer(Modifier.height(16.dp))
val managerVersion = getManagerVersion(context) val managerVersion = getManagerVersion(context)
InfoCardItem( InfoCardItem(
stringResource(R.string.home_manager_version), stringResource(R.string.home_manager_version),
"${managerVersion.first} (${managerVersion.second})" "${managerVersion.first} (${managerVersion.second})"
) )
Spacer(Modifier.height(16.dp)) Spacer(Modifier.height(16.dp))
InfoCardItem(stringResource(R.string.home_selinux_status), getSELinuxStatus()) InfoCardItem(stringResource(R.string.home_selinux_status), getSELinuxStatus())
if (!isSimpleMode) { if (!isSimpleMode) {
val kpmVersion = getKpmVersion()
var displayVersion: String
val isKpmConfigured = checkKpmConfigured()
if (kpmVersion.isEmpty() || kpmVersion.startsWith("Error")) {
val statusText = if (isKpmConfigured) {
stringResource(R.string.kernel_patched)
} else {
stringResource(R.string.kernel_not_enabled)
}
displayVersion = "${stringResource(R.string.not_supported)} ($statusText)"
} else {
displayVersion = "${stringResource(R.string.supported)} ($kpmVersion)"
}
Spacer(Modifier.height(16.dp))
InfoCardItem(stringResource(R.string.home_kpm_version), displayVersion)
}
val isHideSusfsStatus = LocalContext.current.getSharedPreferences("settings", Context.MODE_PRIVATE)
.getBoolean("is_hide_susfs_status", false)
if ((!isSimpleMode) && (!isHideSusfsStatus)) {
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
val suSFS = getSuSFS() val suSFS = getSuSFS()
if (suSFS == "Supported") { if (suSFS == "Supported") {
val suSFSVersion = getSuSFSVersion()
if (suSFSVersion.isEmpty()) return@withContext
val isSUS_SU = getSuSFSFeatures() == "CONFIG_KSU_SUSFS_SUS_SU"
val infoText = buildString {
append(suSFSVersion)
append(if (isSUS_SU) " (${getSuSFSVariant()})" else " (${stringResource(R.string.manual_hook)})")
if (isSUS_SU) {
val susSUMode = try { susfsSUS_SU_Mode().toString() } catch (_: Exception) { "" }
if (susSUMode.isNotEmpty()) {
append(" ${stringResource(R.string.sus_su_mode)} $susSUMode")
}
}
}
InfoCardItem( InfoCardItem(
stringResource(R.string.home_susfs_version), stringResource(R.string.home_susfs_version),
"${getSuSFSVersion()} (${stringResource(R.string.manual_hook)})" infoText
) )
} else {
val susSUMode = try {
susfsSUS_SU_Mode()
} catch (e: Exception) {
0
}
if (susSUMode == 2 || susSUMode == 0) {
val isSUS_SU = getSuSFSFeatures() == "CONFIG_KSU_SUSFS_SUS_SU"
val susSUModeLabel = stringResource(R.string.sus_su_mode)
val susSUModeValue = susSUMode.toString()
val susSUModeText = if (isSUS_SU) " $susSUModeLabel $susSUModeValue" else ""
InfoCardItem(
stringResource(R.string.home_susfs_version),
"${getSuSFSVersion()} (${getSuSFSVariant()})$susSUModeText"
)
} else {
InfoCardItem(
stringResource(R.string.home_susfs_version),
"${getSuSFSVersion()} (${stringResource(R.string.manual_hook)})"
)
}
} }
} }
} }
@@ -608,4 +712,24 @@ private fun getDeviceModel(context: Context): String {
} catch (e: Exception) { } catch (e: Exception) {
Build.DEVICE Build.DEVICE
} }
}
private fun checkKpmConfigured(): Boolean {
try {
val process = Runtime.getRuntime().exec("su -c cat /proc/config.gz")
val inputStream = process.inputStream
val gzipInputStream = GZIPInputStream(inputStream)
val reader = BufferedReader(InputStreamReader(gzipInputStream))
var line: String?
while (reader.readLine().also { line = it } != null) {
if (line?.contains("CONFIG_KPM=y") == true) {
return true
}
}
reader.close()
} catch (e: Exception) {
e.printStackTrace()
}
return false
} }

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.screen package zako.zako.zako.ui.screen
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
@@ -9,6 +9,7 @@ import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.foundation.LocalIndication import androidx.compose.foundation.LocalIndication
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
@@ -28,22 +29,21 @@ import androidx.compose.ui.semantics.Role
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.documentfile.provider.DocumentFile import androidx.documentfile.provider.DocumentFile
import com.maxkeppeker.sheets.core.models.base.Header
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
import com.maxkeppeler.sheets.list.ListDialog
import com.maxkeppeler.sheets.list.models.ListOption import com.maxkeppeler.sheets.list.models.ListOption
import com.maxkeppeler.sheets.list.models.ListSelection
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
import shirkneko.zako.sukisu.R import zako.zako.zako.ui.component.DialogHandle
import shirkneko.zako.sukisu.ui.component.DialogHandle import zako.zako.zako.ui.component.rememberConfirmDialog
import shirkneko.zako.sukisu.ui.component.rememberConfirmDialog import zako.zako.zako.ui.component.rememberCustomDialog
import shirkneko.zako.sukisu.ui.component.rememberCustomDialog import zako.zako.zako.ui.theme.ThemeConfig
import shirkneko.zako.sukisu.ui.util.* import zako.zako.zako.ui.theme.getCardColors
import shirkneko.zako.sukisu.utils.AssetsUtil import zako.zako.zako.ui.theme.getCardElevation
import zako.zako.zako.ui.util.*
import zako.zako.zako.R
import zako.zako.zako.utils.AssetsUtil
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.IOException import java.io.IOException
@@ -197,13 +197,6 @@ fun InstallScreen(navigator: DestinationsNavigator) {
} }
} }
private fun launchHorizonKernelFlash(context: Context, uri: Uri) {
val worker = HorizonKernelWorker(context)
worker.uri = uri
worker.setOnFlashCompleteListener {
}
worker.start()
}
@Composable @Composable
private fun RebootDialog( private fun RebootDialog(
@@ -506,39 +499,77 @@ fun rememberSelectKmiDialog(onSelected: (String?) -> Unit): DialogHandle {
val supportedKmi by produceState(initialValue = emptyList<String>()) { val supportedKmi by produceState(initialValue = emptyList<String>()) {
value = getSupportedKmis() value = getSupportedKmis()
} }
val options = supportedKmi.map { value -> val listOptions = supportedKmi.map { value ->
ListOption( ListOption(
titleText = value titleText = value,
subtitleText = null,
icon = null
) )
} }
var selection by remember { mutableStateOf<String?>(null) } var selection: String? = null
Surface( val cardColor = if (!ThemeConfig.useDynamicColor) {
color = MaterialTheme.colorScheme.secondaryContainer, ThemeConfig.currentTheme.ButtonContrast
contentColor = MaterialTheme.colorScheme.onSecondaryContainer, } else {
shape = MaterialTheme.shapes.medium MaterialTheme.colorScheme.secondaryContainer
) { }
ListDialog(
state = rememberUseCaseState( AlertDialog(
visible = true, onDismissRequest = {
onFinishedRequest = { dismiss()
onSelected(selection) },
}, title = {
onCloseRequest = { Text(text = stringResource(R.string.select_kmi))
},
text = {
Column {
listOptions.forEachIndexed { index, option ->
Row(
modifier = Modifier
.clickable {
selection = supportedKmi[index]
}
.padding(vertical = 8.dp)
) {
Column {
Text(text = option.titleText)
option.subtitleText?.let {
Text(
text = it,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
}
}
},
confirmButton = {
TextButton(
onClick = {
if (selection != null) {
onSelected(selection)
}
dismiss() dismiss()
} }
), ) {
header = Header.Default( Text(text = stringResource(android.R.string.ok))
title = stringResource(R.string.select_kmi),
),
selection = ListSelection.Single(
showRadioButtons = true,
options = options,
) { _, option ->
selection = option.titleText
} }
) },
} dismissButton = {
TextButton(
onClick = {
dismiss()
}
) {
Text(text = stringResource(android.R.string.cancel))
}
},
containerColor = getCardColors(cardColor.copy(alpha = 0.9f)).containerColor.copy(alpha = 0.9f),
shape = MaterialTheme.shapes.medium,
tonalElevation = getCardElevation()
)
} }
} }

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.screen package zako.zako.zako.ui.screen
import android.app.Activity.* import android.app.Activity.*
import android.content.Context import android.content.Context
@@ -88,32 +88,32 @@ import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import shirkneko.zako.sukisu.Natives import zako.zako.zako.Natives
import shirkneko.zako.sukisu.R import zako.zako.zako.ui.component.ConfirmResult
import shirkneko.zako.sukisu.ui.component.ConfirmResult import zako.zako.zako.ui.component.SearchAppBar
import shirkneko.zako.sukisu.ui.component.SearchAppBar import zako.zako.zako.ui.component.rememberConfirmDialog
import shirkneko.zako.sukisu.ui.component.rememberConfirmDialog import zako.zako.zako.ui.component.rememberLoadingDialog
import shirkneko.zako.sukisu.ui.component.rememberLoadingDialog import zako.zako.zako.ui.util.DownloadListener
import shirkneko.zako.sukisu.ui.util.DownloadListener import zako.zako.zako.ui.util.*
import shirkneko.zako.sukisu.ui.util.* import zako.zako.zako.ui.util.download
import shirkneko.zako.sukisu.ui.util.download import zako.zako.zako.ui.util.hasMagisk
import shirkneko.zako.sukisu.ui.util.hasMagisk import zako.zako.zako.ui.util.reboot
import shirkneko.zako.sukisu.ui.util.reboot import zako.zako.zako.ui.util.restoreModule
import shirkneko.zako.sukisu.ui.util.restoreModule import zako.zako.zako.ui.util.toggleModule
import shirkneko.zako.sukisu.ui.util.toggleModule import zako.zako.zako.ui.util.uninstallModule
import shirkneko.zako.sukisu.ui.util.uninstallModule import zako.zako.zako.ui.webui.WebUIActivity
import shirkneko.zako.sukisu.ui.webui.WebUIActivity
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import shirkneko.zako.sukisu.ui.util.ModuleModify import zako.zako.zako.ui.util.ModuleModify
import shirkneko.zako.sukisu.ui.theme.getCardColors import zako.zako.zako.ui.theme.getCardColors
import shirkneko.zako.sukisu.ui.theme.getCardElevation import zako.zako.zako.ui.theme.getCardElevation
import shirkneko.zako.sukisu.ui.viewmodel.ModuleViewModel import zako.zako.zako.ui.viewmodel.ModuleViewModel
import java.io.BufferedReader import java.io.BufferedReader
import java.io.InputStreamReader import java.io.InputStreamReader
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
import androidx.core.content.edit import androidx.core.content.edit
import androidx.core.net.toUri import androidx.core.net.toUri
import shirkneko.zako.sukisu.ui.theme.ThemeConfig import zako.zako.zako.ui.theme.ThemeConfig
import zako.zako.zako.R
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@@ -346,7 +346,11 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
floatingActionButton = { floatingActionButton = {
if (!hideInstallButton) { if (!hideInstallButton) {
val moduleInstall = stringResource(id = R.string.module_install) val moduleInstall = stringResource(id = R.string.module_install)
val cardColor = MaterialTheme.colorScheme.secondaryContainer val cardColor = if (!ThemeConfig.useDynamicColor) {
ThemeConfig.currentTheme.ButtonContrast
} else {
MaterialTheme.colorScheme.secondaryContainer
}
ExtendedFloatingActionButton( ExtendedFloatingActionButton(
onClick = { onClick = {
selectZipLauncher.launch( selectZipLauncher.launch(

View File

@@ -1,5 +1,6 @@
package shirkneko.zako.sukisu.ui.screen package zako.zako.zako.ui.screen
import androidx.compose.animation.AnimatedVisibility
import android.content.Context import android.content.Context
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
@@ -23,6 +24,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Slider import androidx.compose.material3.Slider
import androidx.compose.material3.SliderDefaults import androidx.compose.material3.SliderDefaults
@@ -35,21 +37,21 @@ import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import shirkneko.zako.sukisu.R import zako.zako.zako.ui.component.SwitchItem
import shirkneko.zako.sukisu.ui.component.SwitchItem import zako.zako.zako.ui.theme.CardConfig
import shirkneko.zako.sukisu.ui.theme.CardConfig import zako.zako.zako.ui.theme.ThemeColors
import shirkneko.zako.sukisu.ui.theme.ThemeColors import zako.zako.zako.ui.theme.ThemeConfig
import shirkneko.zako.sukisu.ui.theme.ThemeConfig import zako.zako.zako.ui.theme.saveCustomBackground
import shirkneko.zako.sukisu.ui.theme.saveCustomBackground import zako.zako.zako.ui.theme.saveThemeColors
import shirkneko.zako.sukisu.ui.theme.saveThemeColors import zako.zako.zako.ui.theme.saveThemeMode
import shirkneko.zako.sukisu.ui.theme.saveThemeMode import zako.zako.zako.ui.theme.saveDynamicColorState
import shirkneko.zako.sukisu.ui.theme.saveDynamicColorState import zako.zako.zako.ui.util.getSuSFS
import shirkneko.zako.sukisu.ui.util.getSuSFS import zako.zako.zako.ui.util.getSuSFSFeatures
import shirkneko.zako.sukisu.ui.util.getSuSFSFeatures import zako.zako.zako.ui.util.susfsSUS_SU_0
import shirkneko.zako.sukisu.ui.util.susfsSUS_SU_0 import zako.zako.zako.ui.util.susfsSUS_SU_2
import shirkneko.zako.sukisu.ui.util.susfsSUS_SU_2 import zako.zako.zako.ui.util.susfsSUS_SU_Mode
import shirkneko.zako.sukisu.ui.util.susfsSUS_SU_Mode
import androidx.core.content.edit import androidx.core.content.edit
import zako.zako.zako.R
fun saveCardConfig(context: Context) { fun saveCardConfig(context: Context) {
@@ -115,6 +117,28 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) {
isHideVersion = newValue isHideVersion = newValue
} }
// 隐藏模块数量等信息开关状态
var isHideOtherInfo by remember {
mutableStateOf(prefs.getBoolean("is_hide_other_info", false))
}
// 隐藏模块数量等信息开关状态
val onHideOtherInfoChange = { newValue: Boolean ->
prefs.edit { putBoolean("is_hide_other_info", newValue) }
isHideOtherInfo = newValue
}
// 隐藏 SuSFS 状态开关状态
var isHideSusfsStatus by remember {
mutableStateOf(prefs.getBoolean("is_hide_susfs_status", false))
}
// 隐藏 SuSFS 状态开关状态
val onHideSusfsStatusChange = { newValue: Boolean ->
prefs.edit { putBoolean("is_hide_susfs_status", newValue) }
isHideSusfsStatus = newValue
}
// SELinux 状态 // SELinux 状态
var selinuxEnabled by remember { var selinuxEnabled by remember {
mutableStateOf(Shell.cmd("getenforce").exec().out.firstOrNull() == "Enforcing") mutableStateOf(Shell.cmd("getenforce").exec().out.firstOrNull() == "Enforcing")
@@ -131,7 +155,7 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) {
val systemIsDark = isSystemInDarkTheme() val systemIsDark = isSystemInDarkTheme()
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
CardConfig.apply { CardConfig.apply {
cardAlpha = prefs.getFloat("card_alpha", 0.65f) cardAlpha = prefs.getFloat("card_alpha", 0.45f)
cardElevation = if (prefs.getBoolean("custom_background_enabled", false)) 0.dp else defaultElevation cardElevation = if (prefs.getBoolean("custom_background_enabled", false)) 0.dp else defaultElevation
isCustomAlphaSet = prefs.getBoolean("is_custom_alpha_set", false) isCustomAlphaSet = prefs.getBoolean("is_custom_alpha_set", false)
@@ -139,7 +163,7 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) {
if (!isCustomAlphaSet) { if (!isCustomAlphaSet) {
val isDarkMode = ThemeConfig.forceDarkMode ?: systemIsDark val isDarkMode = ThemeConfig.forceDarkMode ?: systemIsDark
if (isDarkMode) { if (isDarkMode) {
cardAlpha = 0.5f cardAlpha = 0.35f
} }
} }
} }
@@ -210,24 +234,70 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) {
} }
} }
// 添加简洁模块开关 var isExpanded by remember { mutableStateOf(false) }
SwitchItem(
icon = Icons.Filled.FormatPaint,
title = stringResource(R.string.simple_mode),
summary = stringResource(R.string.simple_mode_summary),
checked = isSimpleMode
) {
onSimpleModeChange(it)
}
// 隐藏内核部分版本号 ListItem(
SwitchItem( leadingContent = { Icon(Icons.Filled.AutoFixHigh, null) },
icon = Icons.Filled.FormatPaint, headlineContent = { Text(stringResource(R.string.custom_settings)) },
title = stringResource(R.string.hide_kernel_kernelsu_version), modifier = Modifier.clickable {
summary = stringResource(R.string.hide_kernel_kernelsu_version_summary), isExpanded = !isExpanded
checked = isHideVersion }
)
AnimatedVisibility(
visible = isExpanded,
modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
) { ) {
onHideVersionChange(it) // 添加简洁模块开关
SwitchItem(
icon = Icons.Filled.Brush,
title = stringResource(R.string.simple_mode),
summary = stringResource(R.string.simple_mode_summary),
checked = isSimpleMode
) {
onSimpleModeChange(it)
}
}
AnimatedVisibility(
visible = isExpanded,
modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
) {
// 隐藏内核部分版本号
SwitchItem(
icon = Icons.Filled.VisibilityOff,
title = stringResource(R.string.hide_kernel_kernelsu_version),
summary = stringResource(R.string.hide_kernel_kernelsu_version_summary),
checked = isHideVersion
) {
onHideVersionChange(it)
}
}
AnimatedVisibility(
visible = isExpanded,
modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
) {
// 模块数量等信息
SwitchItem(
icon = Icons.Filled.VisibilityOff,
title = stringResource(R.string.hide_other_info),
summary = stringResource(R.string.hide_other_info_summary),
checked = isHideOtherInfo
) {
onHideOtherInfoChange(it)
}
}
AnimatedVisibility(
visible = isExpanded,
modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
) {
// SuSFS 状态信息
SwitchItem(
icon = Icons.Filled.VisibilityOff,
title = stringResource(R.string.hide_susfs_status),
summary = stringResource(R.string.hide_susfs_status_summary),
checked = isHideSusfsStatus
) {
onHideSusfsStatusChange(it)
}
} }
// region SUSFS 配置(仅在支持时显示) // region SUSFS 配置(仅在支持时显示)
@@ -285,10 +355,12 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) {
} }
} }
// 只在未启用动态颜色时显示主题色选择 // 只在未启用动态颜色时显示主题色选择
if (!useDynamicColor) { AnimatedVisibility(
visible = !useDynamicColor
) {
ListItem( ListItem(
leadingContent = { Icon(Icons.Default.Palette, null) }, leadingContent = { Icon(Icons.Default.Palette, null) },
headlineContent = { Text("主题颜色") }, headlineContent = { Text(stringResource(R.string.theme_color)) },
supportingContent = { supportingContent = {
val currentThemeName = when (ThemeConfig.currentTheme) { val currentThemeName = when (ThemeConfig.currentTheme) {
is ThemeColors.Default -> stringResource(R.string.color_default) is ThemeColors.Default -> stringResource(R.string.color_default)
@@ -373,10 +445,10 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) {
context.saveCustomBackground(null) context.saveCustomBackground(null)
isCustomBackgroundEnabled = false isCustomBackgroundEnabled = false
CardConfig.cardElevation = CardConfig.defaultElevation CardConfig.cardElevation = CardConfig.defaultElevation
CardConfig.cardAlpha = 1f CardConfig.cardAlpha = 0.45f
CardConfig.isCustomAlphaSet = false CardConfig.isCustomAlphaSet = false
saveCardConfig(context) saveCardConfig(context)
cardAlpha = 0.65f cardAlpha = 0.35f
themeMode = 0 themeMode = 0
context.saveThemeMode(null) context.saveThemeMode(null)
CardConfig.isUserDarkModeEnabled = false CardConfig.isUserDarkModeEnabled = false
@@ -387,39 +459,45 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) {
) )
} }
) )
if (ThemeConfig.customBackgroundUri != null && showCardSettings) {
// 透明度 Slider // 透明度 Slider
ListItem( AnimatedVisibility(
leadingContent = { Icon(Icons.Filled.Opacity, null) }, visible = ThemeConfig.customBackgroundUri != null && showCardSettings,
headlineContent = { Text(stringResource(R.string.settings_card_alpha)) }, modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
supportingContent = { ) {
Slider( ListItem(
value = cardAlpha, leadingContent = { Icon(Icons.Filled.Opacity, null) },
onValueChange = { newValue -> headlineContent = { Text(stringResource(R.string.settings_card_alpha)) },
cardAlpha = newValue supportingContent = {
CardConfig.cardAlpha = newValue Slider(
CardConfig.isCustomAlphaSet = true value = cardAlpha,
prefs.edit { putBoolean("is_custom_alpha_set", true) } onValueChange = { newValue ->
prefs.edit { putFloat("card_alpha", newValue) } cardAlpha = newValue
}, CardConfig.cardAlpha = newValue
onValueChangeFinished = { CardConfig.isCustomAlphaSet = true
CoroutineScope(Dispatchers.IO).launch { prefs.edit { putBoolean("is_custom_alpha_set", true) }
saveCardConfig(context) prefs.edit { putFloat("card_alpha", newValue) }
},
onValueChangeFinished = {
CoroutineScope(Dispatchers.IO).launch {
saveCardConfig(context)
}
},
valueRange = 0f..1f,
colors = getSliderColors(cardAlpha, useCustomColors = true),
thumb = {
SliderDefaults.Thumb(
interactionSource = remember { MutableInteractionSource() },
thumbSize = DpSize(0.dp, 0.dp)
)
} }
}, )
valueRange = 0f..1f, }
colors = getSliderColors(cardAlpha, useCustomColors = true), )
thumb = { }
SliderDefaults.Thumb( AnimatedVisibility(
interactionSource = remember { MutableInteractionSource() }, visible = ThemeConfig.customBackgroundUri != null && showCardSettings,
thumbSize = DpSize(0.dp, 0.dp) modifier = Modifier.fillMaxWidth().padding(horizontal = 24.dp)
) ){
}
)
}
)
ListItem( ListItem(
leadingContent = { Icon(Icons.Filled.DarkMode, null) }, leadingContent = { Icon(Icons.Filled.DarkMode, null) },
headlineContent = { Text(stringResource(R.string.theme_mode)) }, headlineContent = { Text(stringResource(R.string.theme_mode)) },
@@ -428,7 +506,7 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) {
showThemeModeDialog = true showThemeModeDialog = true
} }
) )
}
// 主题模式选择对话框 // 主题模式选择对话框
if (showThemeModeDialog) { if (showThemeModeDialog) {
@@ -491,7 +569,7 @@ fun MoreSettingsScreen(navigator: DestinationsNavigator) {
} }
} }
} }
}
@Composable @Composable
private fun getSliderColors(cardAlpha: Float, useCustomColors: Boolean = false): SliderColors { private fun getSliderColors(cardAlpha: Float, useCustomColors: Boolean = false): SliderColors {

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.screen package zako.zako.zako.ui.screen
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@@ -20,9 +20,11 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Undo import androidx.compose.material.icons.automirrored.filled.Undo
import androidx.compose.material.icons.filled.* import androidx.compose.material.icons.filled.*
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.ModalBottomSheet
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
@@ -49,12 +51,9 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import com.maxkeppeker.sheets.core.models.base.Header import androidx.core.content.edit
import com.maxkeppeker.sheets.core.models.base.IconSource import com.maxkeppeker.sheets.core.models.base.IconSource
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
import com.maxkeppeler.sheets.list.ListDialog
import com.maxkeppeler.sheets.list.models.ListOption import com.maxkeppeler.sheets.list.models.ListOption
import com.maxkeppeler.sheets.list.models.ListSelection
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.generated.destinations.AppProfileTemplateScreenDestination import com.ramcosta.composedestinations.generated.destinations.AppProfileTemplateScreenDestination
@@ -65,24 +64,24 @@ import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import shirkneko.zako.sukisu.BuildConfig import zako.zako.zako.BuildConfig
import shirkneko.zako.sukisu.Natives import zako.zako.zako.Natives
import shirkneko.zako.sukisu.R import zako.zako.zako.R
import shirkneko.zako.sukisu.ui.component.AboutDialog import zako.zako.zako.ui.component.AboutDialog
import shirkneko.zako.sukisu.ui.component.ConfirmResult import zako.zako.zako.ui.component.ConfirmResult
import shirkneko.zako.sukisu.ui.component.DialogHandle import zako.zako.zako.ui.component.DialogHandle
import shirkneko.zako.sukisu.ui.component.SwitchItem import zako.zako.zako.ui.component.SwitchItem
import shirkneko.zako.sukisu.ui.component.rememberConfirmDialog import zako.zako.zako.ui.component.rememberConfirmDialog
import shirkneko.zako.sukisu.ui.component.rememberCustomDialog import zako.zako.zako.ui.component.rememberCustomDialog
import shirkneko.zako.sukisu.ui.component.rememberLoadingDialog import zako.zako.zako.ui.component.rememberLoadingDialog
import shirkneko.zako.sukisu.ui.util.LocalSnackbarHost import zako.zako.zako.ui.theme.CardConfig
import shirkneko.zako.sukisu.ui.util.getBugreportFile import zako.zako.zako.ui.theme.ThemeConfig
import zako.zako.zako.ui.theme.getCardColors
import zako.zako.zako.ui.theme.getCardElevation
import zako.zako.zako.ui.util.LocalSnackbarHost
import zako.zako.zako.ui.util.getBugreportFile
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import androidx.compose.material.icons.filled.ExpandMore
import androidx.compose.material3.MaterialTheme
import shirkneko.zako.sukisu.ui.theme.CardConfig
import androidx.core.content.edit
/** /**
@@ -111,7 +110,6 @@ fun SettingScreen(navigator: DestinationsNavigator) {
AboutDialog(it) AboutDialog(it)
} }
val loadingDialog = rememberLoadingDialog() val loadingDialog = rememberLoadingDialog()
val shrinkDialog = rememberConfirmDialog()
// endregion // endregion
Column( Column(
@@ -140,7 +138,6 @@ fun SettingScreen(navigator: DestinationsNavigator) {
loadingDialog.hide() loadingDialog.hide()
snackBarHost.showSnackbar(context.getString(R.string.log_saved)) snackBarHost.showSnackbar(context.getString(R.string.log_saved))
} }
// endregion
} }
// region 配置项列表 // region 配置项列表
// 配置文件模板入口 // 配置文件模板入口
@@ -218,12 +215,12 @@ fun SettingScreen(navigator: DestinationsNavigator) {
prefs.edit { putBoolean("enable_web_debugging", it) } prefs.edit { putBoolean("enable_web_debugging", it) }
enableWebDebugging = it enableWebDebugging = it
} }
// endregion // 更多设置
val newButtonTitle = stringResource(id = R.string.more_settings) val newButtonTitle = stringResource(id = R.string.more_settings)
ListItem( ListItem(
leadingContent = { leadingContent = {
Icon( Icon(
Icons.Filled.ExpandMore, Icons.Filled.Settings,
contentDescription = newButtonTitle contentDescription = newButtonTitle
) )
}, },
@@ -452,20 +449,73 @@ fun rememberUninstallDialog(onSelected: (UninstallType) -> Unit): DialogHandle {
} }
var selection = UninstallType.NONE var selection = UninstallType.NONE
ListDialog(state = rememberUseCaseState(visible = true, onFinishedRequest = { val cardColor = if (!ThemeConfig.useDynamicColor) {
if (selection != UninstallType.NONE) { ThemeConfig.currentTheme.ButtonContrast
onSelected(selection) } else {
} MaterialTheme.colorScheme.secondaryContainer
}, onCloseRequest = { }
dismiss()
}), header = Header.Default( AlertDialog(
title = stringResource(R.string.settings_uninstall), onDismissRequest = {
), selection = ListSelection.Single( dismiss()
showRadioButtons = false, },
options = listOptions, title = {
) { index, _ -> Text(text = stringResource(R.string.settings_uninstall))
selection = options[index] },
}) text = {
Column {
listOptions.forEachIndexed { index, option ->
Row(
modifier = Modifier
.clickable {
selection = options[index]
}
.padding(vertical = 8.dp)
) {
Icon(
imageVector = options[index].icon,
contentDescription = null,
modifier = Modifier.padding(end = 8.dp)
)
Column {
Text(text = option.titleText)
option.subtitleText?.let {
Text(
text = it,
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
}
}
},
confirmButton = {
androidx.compose.material3.TextButton(
onClick = {
if (selection != UninstallType.NONE) {
onSelected(selection)
}
dismiss()
}
) {
Text(text = stringResource(android.R.string.ok))
}
},
dismissButton = {
androidx.compose.material3.TextButton(
onClick = {
dismiss()
}
) {
Text(text = stringResource(android.R.string.cancel))
}
},
containerColor = getCardColors(cardColor.copy(alpha = 0.9f)).containerColor.copy(alpha = 0.9f),
shape = MaterialTheme.shapes.medium,
tonalElevation = getCardElevation()
)
} }
} }

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.screen package zako.zako.zako.ui.screen
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.gestures.detectTapGestures
@@ -24,6 +24,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import zako.zako.zako.R
import coil.compose.AsyncImage import coil.compose.AsyncImage
import coil.request.ImageRequest import coil.request.ImageRequest
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
@@ -31,11 +32,10 @@ import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.generated.destinations.AppProfileScreenDestination import com.ramcosta.composedestinations.generated.destinations.AppProfileScreenDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import shirkneko.zako.sukisu.Natives import zako.zako.zako.Natives
import shirkneko.zako.sukisu.R import zako.zako.zako.ui.component.SearchAppBar
import shirkneko.zako.sukisu.ui.component.SearchAppBar import zako.zako.zako.ui.util.ModuleModify
import shirkneko.zako.sukisu.ui.util.ModuleModify import zako.zako.zako.ui.viewmodel.SuperUserViewModel
import shirkneko.zako.sukisu.ui.viewmodel.SuperUserViewModel
@OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterialApi::class, ExperimentalMaterial3Api::class)
@Destination<RootGraph> @Destination<RootGraph>

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.screen package zako.zako.zako.ui.screen
import android.widget.Toast import android.widget.Toast
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@@ -59,8 +59,9 @@ import com.ramcosta.composedestinations.result.ResultRecipient
import com.ramcosta.composedestinations.result.getOr import com.ramcosta.composedestinations.result.getOr
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import shirkneko.zako.sukisu.R import zako.zako.zako.R
import shirkneko.zako.sukisu.ui.viewmodel.TemplateViewModel import zako.zako.zako.ui.theme.ThemeConfig
import zako.zako.zako.ui.viewmodel.TemplateViewModel
/** /**
* @author weishu * @author weishu
@@ -77,7 +78,11 @@ fun AppProfileTemplateScreen(
val viewModel = viewModel<TemplateViewModel>() val viewModel = viewModel<TemplateViewModel>()
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
val cardColor = MaterialTheme.colorScheme.secondaryContainer val cardColor = if (!ThemeConfig.useDynamicColor) {
ThemeConfig.currentTheme.ButtonContrast
} else {
MaterialTheme.colorScheme.secondaryContainer
}
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
if (viewModel.templateList.isEmpty()) { if (viewModel.templateList.isEmpty()) {

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.screen package zako.zako.zako.ui.screen
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
@@ -47,14 +47,14 @@ import androidx.compose.ui.text.input.KeyboardType
import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.result.ResultBackNavigator import com.ramcosta.composedestinations.result.ResultBackNavigator
import shirkneko.zako.sukisu.Natives import zako.zako.zako.Natives
import shirkneko.zako.sukisu.R import zako.zako.zako.R
import shirkneko.zako.sukisu.ui.component.profile.RootProfileConfig import zako.zako.zako.ui.component.profile.RootProfileConfig
import shirkneko.zako.sukisu.ui.util.deleteAppProfileTemplate import zako.zako.zako.ui.util.deleteAppProfileTemplate
import shirkneko.zako.sukisu.ui.util.getAppProfileTemplate import zako.zako.zako.ui.util.getAppProfileTemplate
import shirkneko.zako.sukisu.ui.util.setAppProfileTemplate import zako.zako.zako.ui.util.setAppProfileTemplate
import shirkneko.zako.sukisu.ui.viewmodel.TemplateViewModel import zako.zako.zako.ui.viewmodel.TemplateViewModel
import shirkneko.zako.sukisu.ui.viewmodel.toJSON import zako.zako.zako.ui.viewmodel.toJSON
import androidx.lifecycle.compose.dropUnlessResumed import androidx.lifecycle.compose.dropUnlessResumed
/** /**

View File

@@ -0,0 +1,556 @@
package zako.zako.zako.ui.screen
import android.app.Activity.RESULT_OK
import android.content.Context
import android.content.Intent
import android.util.Log
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import zako.zako.zako.ui.component.ConfirmResult
import zako.zako.zako.ui.component.SearchAppBar
import zako.zako.zako.ui.component.rememberConfirmDialog
import zako.zako.zako.ui.theme.getCardColors
import zako.zako.zako.ui.theme.getCardElevation
import zako.zako.zako.ui.viewmodel.KpmViewModel
import zako.zako.zako.ui.util.loadKpmModule
import zako.zako.zako.ui.util.unloadKpmModule
import java.io.File
import androidx.core.content.edit
import zako.zako.zako.ui.theme.ThemeConfig
import zako.zako.zako.ui.component.rememberCustomDialog
import zako.zako.zako.ui.component.ConfirmDialogHandle
import zako.zako.zako.R
import java.net.URLDecoder
import java.net.URLEncoder
/**
* KPM 管理界面
* 以下内核模块功能由KernelPatch开发经过修改后加入SukiSU Ultra的内核模块功能
* 开发者zako, Liaokong
*/
var globalModuleFileName: String = ""
@OptIn(ExperimentalMaterial3Api::class)
@Destination<RootGraph>
@Composable
fun KpmScreen(
navigator: DestinationsNavigator,
viewModel: KpmViewModel = viewModel()
) {
val context = LocalContext.current
val scope = rememberCoroutineScope()
val snackBarHost = remember { SnackbarHostState() }
val confirmDialog = rememberConfirmDialog()
val cardColor = if (!ThemeConfig.useDynamicColor) {
ThemeConfig.currentTheme.ButtonContrast
} else {
MaterialTheme.colorScheme.secondaryContainer
}
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
val kpmInstallSuccess = stringResource(R.string.kpm_install_success)
val kpmInstallFailed = stringResource(R.string.kpm_install_failed)
val cancel = stringResource(R.string.cancel)
val uninstall = stringResource(R.string.uninstall)
val failedToCheckModuleFile = stringResource(R.string.snackbar_failed_to_check_module_file)
val kpmUninstallSuccess = stringResource(R.string.kpm_uninstall_success)
val kpmUninstallFailed = stringResource(R.string.kpm_uninstall_failed)
val kpmInstallMode = stringResource(R.string.kpm_install_mode)
val kpmInstallModeLoad = stringResource(R.string.kpm_install_mode_load)
val kpmInstallModeEmbed = stringResource(R.string.kpm_install_mode_embed)
val kpmInstallModeDescription = stringResource(R.string.kpm_install_mode_description)
val invalidFileTypeMessage = stringResource(R.string.invalid_file_type)
val confirmTitle = stringResource(R.string.confirm_uninstall_title_with_filename)
val confirmContent = stringResource(R.string.confirm_uninstall_content, globalModuleFileName)
var tempFileForInstall by remember { mutableStateOf<File?>(null) }
val installModeDialog = rememberCustomDialog { dismiss ->
AlertDialog(
onDismissRequest = {
dismiss()
tempFileForInstall?.delete()
tempFileForInstall = null
},
title = { Text(kpmInstallMode) },
text = { Text(kpmInstallModeDescription) },
confirmButton = {
Column {
Button(
onClick = {
scope.launch {
dismiss()
tempFileForInstall?.let { tempFile ->
handleModuleInstall(
tempFile = tempFile,
isEmbed = false,
viewModel = viewModel,
snackBarHost = snackBarHost,
kpmInstallSuccess = kpmInstallSuccess,
kpmInstallFailed = kpmInstallFailed
)
}
tempFileForInstall = null
}
}
) {
Text(kpmInstallModeLoad)
}
Spacer(modifier = Modifier.height(8.dp))
Button(
onClick = {
scope.launch {
dismiss()
tempFileForInstall?.let { tempFile ->
handleModuleInstall(
tempFile = tempFile,
isEmbed = true,
viewModel = viewModel,
snackBarHost = snackBarHost,
kpmInstallSuccess = kpmInstallSuccess,
kpmInstallFailed = kpmInstallFailed
)
}
tempFileForInstall = null
}
}
) {
Text(kpmInstallModeEmbed)
}
}
},
dismissButton = {
TextButton(
onClick = {
dismiss()
tempFileForInstall?.delete()
tempFileForInstall = null
}
) {
Text(cancel)
}
}
)
}
val selectPatchLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult()
) { result ->
if (result.resultCode != RESULT_OK) return@rememberLauncherForActivityResult
val uri = result.data?.data ?: return@rememberLauncherForActivityResult
scope.launch {
val fileName = uri.lastPathSegment ?: "unknown.kpm"
val encodedFileName = URLEncoder.encode(fileName, "UTF-8")
val tempFile = File(context.cacheDir, encodedFileName)
context.contentResolver.openInputStream(uri)?.use { input ->
tempFile.outputStream().use { output ->
input.copyTo(output)
}
}
if (!tempFile.name.endsWith(".kpm")) {
snackBarHost.showSnackbar(
message = invalidFileTypeMessage,
duration = SnackbarDuration.Short
)
tempFile.delete()
return@launch
}
tempFileForInstall = tempFile
installModeDialog.show()
}
}
LaunchedEffect(Unit) {
while(true) {
viewModel.fetchModuleList()
delay(5000)
}
}
val sharedPreferences = context.getSharedPreferences("app_preferences", Context.MODE_PRIVATE)
var isNoticeClosed by remember { mutableStateOf(sharedPreferences.getBoolean("is_notice_closed", false)) }
Scaffold(
topBar = {
SearchAppBar(
title = { Text(stringResource(R.string.kpm_title)) },
searchText = viewModel.search,
onSearchTextChange = { viewModel.search = it },
onClearClick = { viewModel.search = "" },
scrollBehavior = scrollBehavior,
dropdownContent = {
IconButton(onClick = { viewModel.fetchModuleList() }) {
Icon(
imageVector = Icons.Outlined.Refresh,
contentDescription = stringResource(R.string.refresh)
)
}
}
)
},
floatingActionButton = {
ExtendedFloatingActionButton(
onClick = {
selectPatchLauncher.launch(
Intent(Intent.ACTION_GET_CONTENT).apply {
type = "*/*"
}
)
},
icon = {
Icon(
imageVector = Icons.Outlined.Add,
contentDescription = stringResource(R.string.kpm_install)
)
},
text = { Text(stringResource(R.string.kpm_install)) },
containerColor = cardColor.copy(alpha = 1f),
contentColor = MaterialTheme.colorScheme.onSecondaryContainer
)
},
snackbarHost = { SnackbarHost(snackBarHost) }
) { padding ->
Column(modifier = Modifier.padding(padding)) {
if (!isNoticeClosed) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = stringResource(R.string.kernel_module_notice),
modifier = Modifier.weight(1f),
textAlign = TextAlign.Center
)
IconButton(onClick = {
isNoticeClosed = true
sharedPreferences.edit { putBoolean("is_notice_closed", true) }
}) {
Icon(
imageVector = Icons.Outlined.Close,
contentDescription = stringResource(R.string.close_notice)
)
}
}
}
if (viewModel.moduleList.isEmpty()) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
stringResource(R.string.kpm_empty),
textAlign = TextAlign.Center
)
}
} else {
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
items(viewModel.moduleList) { module ->
KpmModuleItem(
module = module,
onUninstall = {
scope.launch {
handleModuleUninstall(
module = module,
viewModel = viewModel,
snackBarHost = snackBarHost,
kpmUninstallSuccess = kpmUninstallSuccess,
kpmUninstallFailed = kpmUninstallFailed,
failedToCheckModuleFile = failedToCheckModuleFile,
uninstall = uninstall,
cancel = cancel,
confirmDialog = confirmDialog,
confirmTitle = confirmTitle,
confirmContent = confirmContent
)
}
},
onControl = {
viewModel.loadModuleDetail(module.id)
}
)
}
}
}
}
}
}
private suspend fun handleModuleInstall(
tempFile: File,
isEmbed: Boolean,
viewModel: KpmViewModel,
snackBarHost: SnackbarHostState,
kpmInstallSuccess: String,
kpmInstallFailed: String
) {
val moduleId = extractModuleId(tempFile.name)
if (moduleId == null) {
Log.e("KsuCli", "Failed to extract module ID from file: ${tempFile.name}")
snackBarHost.showSnackbar(
message = kpmInstallFailed,
duration = SnackbarDuration.Short
)
tempFile.delete()
return
}
val targetPath = "/data/adb/kpm/$moduleId.kpm"
try {
if (isEmbed) {
Runtime.getRuntime().exec(arrayOf("su", "-c", "mkdir -p /data/adb/kpm")).waitFor()
Runtime.getRuntime().exec(arrayOf("su", "-c", "cp ${tempFile.absolutePath} $targetPath")).waitFor()
}
val loadResult = loadKpmModule(tempFile.absolutePath)
if (loadResult.startsWith("Error")) {
Log.e("KsuCli", "Failed to load KPM module: $loadResult")
snackBarHost.showSnackbar(
message = kpmInstallFailed,
duration = SnackbarDuration.Short
)
} else {
viewModel.fetchModuleList()
snackBarHost.showSnackbar(
message = kpmInstallSuccess,
duration = SnackbarDuration.Short
)
}
} catch (e: Exception) {
Log.e("KsuCli", "Failed to load KPM module: ${e.message}", e)
snackBarHost.showSnackbar(
message = kpmInstallFailed,
duration = SnackbarDuration.Short
)
}
tempFile.delete()
}
private fun extractModuleId(fileName: String): String? {
return try {
val decodedFileName = URLDecoder.decode(fileName, "UTF-8")
val pattern = "([^/]*?)\\.kpm$".toRegex()
val matchResult = pattern.find(decodedFileName)
matchResult?.groupValues?.get(1)
} catch (e: Exception) {
Log.e("KsuCli", "Failed to extract module ID: ${e.message}", e)
null
}
}
private suspend fun handleModuleUninstall(
module: KpmViewModel.ModuleInfo,
viewModel: KpmViewModel,
snackBarHost: SnackbarHostState,
kpmUninstallSuccess: String,
kpmUninstallFailed: String,
failedToCheckModuleFile: String,
uninstall: String,
cancel: String,
confirmTitle : String,
confirmContent : String,
confirmDialog: ConfirmDialogHandle
) {
val moduleFileName = "${module.id}.kpm"
globalModuleFileName = moduleFileName
val moduleFilePath = "/data/adb/kpm/$moduleFileName"
val fileExists = try {
val result = Runtime.getRuntime().exec(arrayOf("su", "-c", "ls /data/adb/kpm/$moduleFileName")).waitFor() == 0
result
} catch (e: Exception) {
Log.e("KsuCli", "Failed to check module file existence: ${e.message}", e)
snackBarHost.showSnackbar(
message = failedToCheckModuleFile,
duration = SnackbarDuration.Short
)
false
}
val confirmResult = confirmDialog.awaitConfirm(
title = confirmTitle,
content = confirmContent,
confirm = uninstall,
dismiss = cancel
)
if (confirmResult == ConfirmResult.Confirmed) {
try {
val unloadResult = unloadKpmModule(module.id)
if (unloadResult.startsWith("Error")) {
Log.e("KsuCli", "Failed to unload KPM module: $unloadResult")
snackBarHost.showSnackbar(
message = kpmUninstallFailed,
duration = SnackbarDuration.Short
)
return
}
if (fileExists) {
Runtime.getRuntime().exec(arrayOf("su", "-c", "rm $moduleFilePath")).waitFor()
}
viewModel.fetchModuleList()
snackBarHost.showSnackbar(
message = kpmUninstallSuccess,
duration = SnackbarDuration.Short
)
} catch (e: Exception) {
Log.e("KsuCli", "Failed to unload KPM module: ${e.message}", e)
snackBarHost.showSnackbar(
message = kpmUninstallFailed,
duration = SnackbarDuration.Short
)
}
}
}
@Composable
private fun KpmModuleItem(
module: KpmViewModel.ModuleInfo,
onUninstall: () -> Unit,
onControl: () -> Unit
) {
val viewModel: KpmViewModel = viewModel()
val scope = rememberCoroutineScope()
val snackBarHost = remember { SnackbarHostState() }
val successMessage = stringResource(R.string.kpm_control_success)
val failureMessage = stringResource(R.string.kpm_control_failed)
if (viewModel.showInputDialog && viewModel.selectedModuleId == module.id) {
AlertDialog(
onDismissRequest = { viewModel.hideInputDialog() },
title = { Text(stringResource(R.string.kpm_control)) },
text = {
OutlinedTextField(
value = viewModel.inputArgs,
onValueChange = { viewModel.updateInputArgs(it) },
label = { Text(stringResource(R.string.kpm_args)) },
placeholder = { Text(module.args) }
)
},
confirmButton = {
TextButton(
onClick = {
scope.launch {
val result = viewModel.executeControl()
val message = when (result) {
0 -> successMessage
else -> failureMessage
}
snackBarHost.showSnackbar(message)
onControl()
}
}
) {
Text(stringResource(R.string.confirm))
}
},
dismissButton = {
TextButton(onClick = { viewModel.hideInputDialog() }) {
Text(stringResource(R.string.cancel))
}
}
)
}
ElevatedCard(
colors = getCardColors(MaterialTheme.colorScheme.secondaryContainer),
elevation = CardDefaults.cardElevation(defaultElevation = getCardElevation())
) {
Column(
modifier = Modifier.padding(16.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Column(modifier = Modifier.weight(1f)) {
Text(
text = module.name,
style = MaterialTheme.typography.titleMedium
)
Text(
text = "${stringResource(R.string.kpm_version)}: ${module.version}",
style = MaterialTheme.typography.bodyMedium
)
Text(
text = "${stringResource(R.string.kpm_author)}: ${module.author}",
style = MaterialTheme.typography.bodyMedium
)
Text(
text = "${stringResource(R.string.kpm_args)}: ${module.args}",
style = MaterialTheme.typography.bodyMedium
)
}
}
Spacer(modifier = Modifier.height(8.dp))
Text(
text = module.description,
style = MaterialTheme.typography.bodyMedium
)
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
FilledTonalButton(
onClick = { viewModel.showInputDialog(module.id) },
enabled = module.hasAction
) {
Icon(
imageVector = Icons.Outlined.Settings,
contentDescription = null
)
Text(stringResource(R.string.kpm_control))
}
FilledTonalButton(
onClick = onUninstall
) {
Icon(
imageVector = Icons.Outlined.Delete,
contentDescription = null
)
Text(stringResource(R.string.kpm_uninstall))
}
}
}
}
}

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.theme package zako.zako.zako.ui.theme
import android.content.Context import android.content.Context
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
@@ -15,7 +15,7 @@ import androidx.compose.ui.unit.dp
object CardConfig { object CardConfig {
val defaultElevation: Dp = 0.dp val defaultElevation: Dp = 0.dp
var cardAlpha by mutableStateOf(1f) var cardAlpha by mutableStateOf(0.45f)
var cardElevation by mutableStateOf(defaultElevation) var cardElevation by mutableStateOf(defaultElevation)
var isShadowEnabled by mutableStateOf(true) var isShadowEnabled by mutableStateOf(true)
var isCustomAlphaSet by mutableStateOf(false) var isCustomAlphaSet by mutableStateOf(false)
@@ -38,7 +38,7 @@ object CardConfig {
fun load(context: Context) { fun load(context: Context) {
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE) val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
cardAlpha = prefs.getFloat("card_alpha", 1f) cardAlpha = prefs.getFloat("card_alpha", 0.45f)
cardElevation = if (prefs.getBoolean("custom_background_enabled", false)) 0.dp else defaultElevation cardElevation = if (prefs.getBoolean("custom_background_enabled", false)) 0.dp else defaultElevation
isCustomAlphaSet = prefs.getBoolean("is_custom_alpha_set", false) isCustomAlphaSet = prefs.getBoolean("is_custom_alpha_set", false)
isUserDarkModeEnabled = prefs.getBoolean("is_user_dark_mode_enabled", false) isUserDarkModeEnabled = prefs.getBoolean("is_user_dark_mode_enabled", false)
@@ -53,7 +53,7 @@ object CardConfig {
fun setDarkModeDefaults() { fun setDarkModeDefaults() {
if (!isCustomAlphaSet) { if (!isCustomAlphaSet) {
cardAlpha = 0.5f cardAlpha = 0.35f
cardElevation = 0.dp cardElevation = 0.dp
} }
} }

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.theme package zako.zako.zako.ui.theme
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.theme package zako.zako.zako.ui.theme
import android.content.ContentResolver import android.content.ContentResolver
import android.content.Context import android.content.Context
@@ -43,23 +43,23 @@ object ThemeConfig {
@Composable @Composable
private fun getDarkColorScheme() = darkColorScheme( private fun getDarkColorScheme() = darkColorScheme(
primary = ThemeConfig.currentTheme.Primary.copy(alpha = 0.8f), primary = ThemeConfig.currentTheme.Primary.copy(alpha = 0.8f),
onPrimary = Color.White, onPrimary = mixColors(ThemeConfig.currentTheme.Primary, Color.White, 0.2f),
primaryContainer = ThemeConfig.currentTheme.PrimaryContainer.copy(alpha = 0.15f), primaryContainer = ThemeConfig.currentTheme.PrimaryContainer.copy(alpha = 0.15f),
onPrimaryContainer = Color.White, onPrimaryContainer = mixColors(ThemeConfig.currentTheme.Primary, Color.White, 0.2f),
secondary = ThemeConfig.currentTheme.Secondary.copy(alpha = 0.8f), secondary = ThemeConfig.currentTheme.Secondary.copy(alpha = 0.8f),
onSecondary = Color.White, onSecondary = mixColors(ThemeConfig.currentTheme.Secondary, Color.White, 0.2f),
secondaryContainer = ThemeConfig.currentTheme.SecondaryContainer.copy(alpha = 0.15f), secondaryContainer = ThemeConfig.currentTheme.SecondaryContainer.copy(alpha = 0.15f),
onSecondaryContainer = Color.White, onSecondaryContainer = mixColors(ThemeConfig.currentTheme.Secondary, Color.White, 0.2f),
tertiary = ThemeConfig.currentTheme.Tertiary.copy(alpha = 0.8f), tertiary = ThemeConfig.currentTheme.Tertiary.copy(alpha = 0.8f),
onTertiary = Color.White, onTertiary = mixColors(ThemeConfig.currentTheme.Tertiary, Color.White, 0.2f),
tertiaryContainer = ThemeConfig.currentTheme.TertiaryContainer.copy(alpha = 0.15f), tertiaryContainer = ThemeConfig.currentTheme.TertiaryContainer.copy(alpha = 0.15f),
onTertiaryContainer = Color.White, onTertiaryContainer = mixColors(ThemeConfig.currentTheme.Tertiary, Color.White, 0.2f),
background = Color.Transparent, background = Color.Transparent,
surface = Color.Transparent, surface = Color.Transparent,
onBackground = Color.White.copy(alpha = 0.87f), onBackground = mixColors(ThemeConfig.currentTheme.Primary, Color.White, 0.1f),
onSurface = Color.White.copy(alpha = 0.87f), onSurface = mixColors(ThemeConfig.currentTheme.Primary, Color.White, 0.1f),
surfaceVariant = Color(0xFF2F2F2F), surfaceVariant = Color(0xFF2F2F2F),
onSurfaceVariant = Color.White.copy(alpha = 0.78f), onSurfaceVariant = mixColors(ThemeConfig.currentTheme.Primary, Color.White, 0.2f),
outline = Color.White.copy(alpha = 0.12f), outline = Color.White.copy(alpha = 0.12f),
outlineVariant = Color.White.copy(alpha = 0.12f) outlineVariant = Color.White.copy(alpha = 0.12f)
) )
@@ -339,4 +339,12 @@ private fun adjustColor(color: Color): Color {
luminance = maxLuminance luminance = maxLuminance
} }
return color.copy(luminance) return color.copy(luminance)
}
private fun mixColors(color1: Color, color2: Color, ratio: Float): Color {
val r = (color1.red * ratio + color2.red * (1 - ratio))
val g = (color1.green * ratio + color2.green * (1 - ratio))
val b = (color1.blue * ratio + color2.blue * (1 - ratio))
val a = (color1.alpha * ratio + color2.alpha * (1 - ratio))
return Color(r, g, b, a)
} }

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.theme package zako.zako.zako.ui.theme
import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontFamily

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.util package zako.zako.zako.ui.util
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.compositionLocalOf import androidx.compose.runtime.compositionLocalOf

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.util package zako.zako.zako.ui.util
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.DownloadManager import android.app.DownloadManager
@@ -12,7 +12,7 @@ import android.util.Log
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import shirkneko.zako.sukisu.ui.util.module.LatestVersionInfo import zako.zako.zako.ui.util.module.LatestVersionInfo
/** /**
* @author weishu * @author weishu
@@ -63,7 +63,6 @@ fun download(
} }
fun checkNewVersion(): LatestVersionInfo { fun checkNewVersion(): LatestVersionInfo {
// 改为新的 release 接口
val url = "https://api.github.com/repos/ShirkNeko/SukiSU-Ultra/releases/latest" val url = "https://api.github.com/repos/ShirkNeko/SukiSU-Ultra/releases/latest"
val defaultValue = LatestVersionInfo() val defaultValue = LatestVersionInfo()
return runCatching { return runCatching {

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.util; package zako.zako.zako.ui.util;
/* /*
* Copyright (C) 2009 The Android Open Source Project * Copyright (C) 2009 The Android Open Source Project
* *

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.util package zako.zako.zako.ui.util
import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.util package zako.zako.zako.ui.util
import android.content.ContentResolver import android.content.ContentResolver
import android.content.Context import android.content.Context
@@ -16,9 +16,9 @@ import com.topjohnwu.superuser.ShellUtils
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import shirkneko.zako.sukisu.BuildConfig import zako.zako.zako.BuildConfig
import shirkneko.zako.sukisu.Natives import zako.zako.zako.Natives
import shirkneko.zako.sukisu.ksuApp import zako.zako.zako.ksuApp
import org.json.JSONArray import org.json.JSONArray
import java.io.File import java.io.File
@@ -30,7 +30,7 @@ import java.io.File
private const val TAG = "KsuCli" private const val TAG = "KsuCli"
private fun getKsuDaemonPath(): String { private fun getKsuDaemonPath(): String {
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libzakomk.so" return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libzakozako.so"
} }
object KsuCli { object KsuCli {
@@ -437,7 +437,7 @@ fun restartApp(packageName: String) {
} }
private fun getSuSFSDaemonPath(): String { private fun getSuSFSDaemonPath(): String {
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libzakomksd.so" return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libzakozakozako.so"
} }
fun getSuSFS(): String { fun getSuSFS(): String {
@@ -481,7 +481,7 @@ fun susfsSUS_SU_Mode(): String {
return result return result
} }
private fun getKpmmgrPath(): String { fun getKpmmgrPath(): String {
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libkpmmgr.so" return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libkpmmgr.so"
} }
@@ -489,48 +489,62 @@ private fun getKpmmgrPath(): String {
fun loadKpmModule(path: String, args: String? = null): String { fun loadKpmModule(path: String, args: String? = null): String {
val shell = getRootShell() val shell = getRootShell()
val cmd = "${getKpmmgrPath()} load $path ${args ?: ""}" val cmd = "${getKpmmgrPath()} load $path ${args ?: ""}"
val result = ShellUtils.fastCmd(shell, cmd) return ShellUtils.fastCmd(shell, cmd)
return result
} }
fun unloadKpmModule(name: String): String { fun unloadKpmModule(name: String): String {
val shell = getRootShell() val shell = getRootShell()
val cmd = "${getKpmmgrPath()} unload $name" val cmd = "${getKpmmgrPath()} unload $name"
val result = ShellUtils.fastCmd(shell, cmd) return ShellUtils.fastCmd(shell, cmd)
return result
} }
fun getKpmModuleCount(): String { fun getKpmModuleCount(): Int {
val shell = getRootShell() val shell = getRootShell()
val cmd = "${getKpmmgrPath()} num" val cmd = "${getKpmmgrPath()} num"
val result = ShellUtils.fastCmd(shell, cmd) val result = ShellUtils.fastCmd(shell, cmd)
return result return result.trim().toIntOrNull() ?: 0
}
fun runCmd(shell : Shell, cmd : String) : String {
return shell.newJob()
.add(cmd)
.to(mutableListOf<String>(), null)
.exec().out
.joinToString("\n")
} }
fun listKpmModules(): String { fun listKpmModules(): String {
val shell = getRootShell() val shell = getRootShell()
val cmd = "${getKpmmgrPath()} list" val cmd = "${getKpmmgrPath()} list"
val result = ShellUtils.fastCmd(shell, cmd) return try {
return result runCmd(shell, cmd).trim()
} catch (e: Exception) {
Log.e(TAG, "Failed to list KPM modules", e)
""
}
} }
fun getKpmModuleInfo(name: String): String { fun getKpmModuleInfo(name: String): String {
val shell = getRootShell() val shell = getRootShell()
val cmd = "${getKpmmgrPath()} info $name" val cmd = "${getKpmmgrPath()} info $name"
val result = ShellUtils.fastCmd(shell, cmd) return try {
return result runCmd(shell, cmd).trim()
} catch (e: Exception) {
Log.e(TAG, "Failed to get KPM module info: $name", e)
""
}
} }
fun controlKpmModule(name: String, args: String? = null): String { fun controlKpmModule(name: String, args: String? = null): Int {
val shell = getRootShell() val shell = getRootShell()
val cmd = "${getKpmmgrPath()} control $name ${args ?: ""}" val cmd = "${getKpmmgrPath()} control $name ${args ?: ""}"
val result = ShellUtils.fastCmd(shell, cmd) val result = runCmd(shell, cmd)
return result return result.trim().toIntOrNull() ?: -1
} }
fun printKpmModules(): String { fun getKpmVersion(): String {
val shell = getRootShell() val shell = getRootShell()
val cmd = "${getKpmmgrPath()} print" val cmd = "${getKpmmgrPath()} version"
val result = ShellUtils.fastCmd(shell, cmd) val result = ShellUtils.fastCmd(shell, cmd)
return result return result.trim()
} }

View File

@@ -1,11 +1,11 @@
package shirkneko.zako.sukisu.ui.util package zako.zako.zako.ui.util
import android.content.Context import android.content.Context
import android.os.Build import android.os.Build
import android.system.Os import android.system.Os
import com.topjohnwu.superuser.ShellUtils import com.topjohnwu.superuser.ShellUtils
import shirkneko.zako.sukisu.Natives import zako.zako.zako.Natives
import shirkneko.zako.sukisu.ui.screen.getManagerVersion import zako.zako.zako.ui.screen.getManagerVersion
import java.io.File import java.io.File
import java.io.FileWriter import java.io.FileWriter
import java.io.PrintWriter import java.io.PrintWriter
@@ -24,7 +24,7 @@ fun getBugreportFile(context: Context): File {
val pstoreFile = File(bugreportDir, "pstore.tar.gz") val pstoreFile = File(bugreportDir, "pstore.tar.gz")
// Xiaomi/Readmi devices have diag in /data/vendor/diag // Xiaomi/Readmi devices have diag in /data/vendor/diag
val diagFile = File(bugreportDir, "diag.tar.gz") val diagFile = File(bugreportDir, "diag.tar.gz")
val opulsFile = File(bugreportDir, "opuls.tar.gz") val oplusFile = File(bugreportDir, "oplus.tar.gz")
val bootlogFile = File(bugreportDir, "bootlog.tar.gz") val bootlogFile = File(bugreportDir, "bootlog.tar.gz")
val mountsFile = File(bugreportDir, "mounts.txt") val mountsFile = File(bugreportDir, "mounts.txt")
val fileSystemsFile = File(bugreportDir, "filesystems.txt") val fileSystemsFile = File(bugreportDir, "filesystems.txt")
@@ -46,7 +46,7 @@ fun getBugreportFile(context: Context): File {
shell.newJob().add("tar -czf ${dropboxFile.absolutePath} -C /data/system/dropbox .").exec() shell.newJob().add("tar -czf ${dropboxFile.absolutePath} -C /data/system/dropbox .").exec()
shell.newJob().add("tar -czf ${pstoreFile.absolutePath} -C /sys/fs/pstore .").exec() shell.newJob().add("tar -czf ${pstoreFile.absolutePath} -C /sys/fs/pstore .").exec()
shell.newJob().add("tar -czf ${diagFile.absolutePath} -C /data/vendor/diag . --exclude=./minidump.gz").exec() shell.newJob().add("tar -czf ${diagFile.absolutePath} -C /data/vendor/diag . --exclude=./minidump.gz").exec()
shell.newJob().add("tar -czf ${opulsFile.absolutePath} -C /mnt/oplus/op2/media/log/boot_log/ .").exec() shell.newJob().add("tar -czf ${oplusFile.absolutePath} -C /mnt/oplus/op2/media/log/boot_log/ .").exec()
shell.newJob().add("tar -czf ${bootlogFile.absolutePath} -C /data/adb/ksu/log .").exec() shell.newJob().add("tar -czf ${bootlogFile.absolutePath} -C /data/adb/ksu/log .").exec()
shell.newJob().add("cat /proc/1/mountinfo > ${mountsFile.absolutePath}").exec() shell.newJob().add("cat /proc/1/mountinfo > ${mountsFile.absolutePath}").exec()

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.util package zako.zako.zako.ui.util
import android.app.AlertDialog import android.app.AlertDialog
import android.content.Context import android.content.Context
@@ -16,7 +16,7 @@ import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import shirkneko.zako.sukisu.R import zako.zako.zako.R
import java.io.BufferedReader import java.io.BufferedReader
import java.io.IOException import java.io.IOException
import java.io.InputStreamReader import java.io.InputStreamReader

View File

@@ -1,9 +1,9 @@
package shirkneko.zako.sukisu.ui.util package zako.zako.zako.ui.util
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import shirkneko.zako.sukisu.R import zako.zako.zako.R
@Composable @Composable
fun getSELinuxStatus(): String { fun getSELinuxStatus(): String {

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.util.module package zako.zako.zako.ui.util.module
data class LatestVersionInfo( data class LatestVersionInfo(
val versionCode : Int = 0, val versionCode : Int = 0,

View File

@@ -0,0 +1,167 @@
package zako.zako.zako.ui.viewmodel
import android.util.Log
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import zako.zako.zako.ui.util.*
class KpmViewModel : ViewModel() {
var moduleList by mutableStateOf(emptyList<ModuleInfo>())
private set
var search by mutableStateOf("")
internal set
var isRefreshing by mutableStateOf(false)
private set
var currentModuleDetail by mutableStateOf("")
private set
fun fetchModuleList() {
viewModelScope.launch {
isRefreshing = true
try {
val moduleCount = getKpmModuleCount()
Log.d("KsuCli", "Module count: $moduleCount")
moduleList = getAllKpmModuleInfo()
// 获取 KPM 版本信息
val kpmVersion = getKpmVersion()
Log.d("KsuCli", "KPM Version: $kpmVersion")
} catch (e: Exception) {
Log.e("KsuCli", "获取模块列表失败", e)
} finally {
isRefreshing = false
}
}
}
private fun getAllKpmModuleInfo(): List<ModuleInfo> {
val result = mutableListOf<ModuleInfo>()
try {
val str = listKpmModules()
val moduleNames = str
.split("\n")
.filter { it.isNotBlank() }
for (name in moduleNames) {
try {
val moduleInfo = parseModuleInfo(name)
moduleInfo?.let { result.add(it) }
} catch (e: Exception) {
Log.e("KsuCli", "Error processing module $name", e)
}
}
} catch (e: Exception) {
Log.e("KsuCli", "Failed to get module list", e)
}
return result
}
private fun parseModuleInfo(name: String): ModuleInfo? {
val info = getKpmModuleInfo(name)
if (info.isBlank()) return null
val properties = info.lineSequence()
.filter { line ->
val trimmed = line.trim()
trimmed.isNotEmpty() && !trimmed.startsWith("#")
}
.mapNotNull { line ->
line.split("=", limit = 2).let { parts ->
when (parts.size) {
2 -> parts[0].trim() to parts[1].trim()
1 -> parts[0].trim() to ""
else -> null
}
}
}
.toMap()
return ModuleInfo(
id = name,
name = properties["name"] ?: name,
version = properties["version"] ?: "",
author = properties["author"] ?: "",
description = properties["description"] ?: "",
args = properties["args"] ?: "",
enabled = true,
hasAction = true
)
}
fun loadModuleDetail(moduleId: String) {
viewModelScope.launch {
try {
currentModuleDetail = withContext(Dispatchers.IO) {
getKpmModuleInfo(moduleId)
}
Log.d("KsuCli", "Module detail loaded: $currentModuleDetail")
} catch (e: Exception) {
Log.e("KsuCli", "Failed to load module detail", e)
currentModuleDetail = "Error: ${e.message}"
}
}
}
var showInputDialog by mutableStateOf(false)
private set
var selectedModuleId by mutableStateOf<String?>(null)
private set
var inputArgs by mutableStateOf("")
private set
fun showInputDialog(moduleId: String) {
selectedModuleId = moduleId
showInputDialog = true
}
fun hideInputDialog() {
showInputDialog = false
selectedModuleId = null
inputArgs = ""
}
fun updateInputArgs(args: String) {
inputArgs = args
}
fun executeControl(): Int {
val moduleId = selectedModuleId ?: return -1
val result = controlKpmModule(moduleId, inputArgs)
hideInputDialog()
return result
}
fun controlModule(moduleId: String, args: String? = null): Int {
return try {
val result = controlKpmModule(moduleId, args)
Log.d("KsuCli", "Control module $moduleId result: $result")
result
} catch (e: Exception) {
Log.e("KsuCli", "Failed to control module $moduleId", e)
-1
}
}
data class ModuleInfo(
val id: String,
val name: String,
val version: String,
val author: String,
val description: String,
val args: String,
val enabled: Boolean,
val hasAction: Boolean
)
}

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.viewmodel package zako.zako.zako.ui.viewmodel
import android.os.SystemClock import android.os.SystemClock
import android.util.Log import android.util.Log
@@ -10,8 +10,8 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import shirkneko.zako.sukisu.ui.util.HanziToPinyin import zako.zako.zako.ui.util.HanziToPinyin
import shirkneko.zako.sukisu.ui.util.listModules import zako.zako.zako.ui.util.listModules
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
import java.text.Collator import java.text.Collator

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.viewmodel package zako.zako.zako.ui.viewmodel
import android.content.ComponentName import android.content.ComponentName
import android.content.Intent import android.content.Intent
@@ -18,12 +18,12 @@ import com.topjohnwu.superuser.Shell
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import shirkneko.zako.sukisu.IKsuInterface import zako.zako.zako.IKsuInterface
import shirkneko.zako.sukisu.Natives import zako.zako.zako.Natives
import shirkneko.zako.sukisu.ksuApp import zako.zako.zako.ksuApp
import shirkneko.zako.sukisu.ui.KsuService import zako.zako.zako.ui.KsuService
import shirkneko.zako.sukisu.ui.util.HanziToPinyin import zako.zako.zako.ui.util.HanziToPinyin
import shirkneko.zako.sukisu.ui.util.KsuCli import zako.zako.zako.ui.util.KsuCli
import java.text.Collator import java.text.Collator
import java.util.* import java.util.*
import kotlin.coroutines.resume import kotlin.coroutines.resume

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.viewmodel package zako.zako.zako.ui.viewmodel
import android.os.Parcelable import android.os.Parcelable
import android.util.Log import android.util.Log
@@ -10,12 +10,12 @@ import androidx.lifecycle.ViewModel
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import shirkneko.zako.sukisu.Natives import zako.zako.zako.Natives
import shirkneko.zako.sukisu.profile.Capabilities import zako.zako.zako.profile.Capabilities
import shirkneko.zako.sukisu.profile.Groups import zako.zako.zako.profile.Groups
import shirkneko.zako.sukisu.ui.util.getAppProfileTemplate import zako.zako.zako.ui.util.getAppProfileTemplate
import shirkneko.zako.sukisu.ui.util.listAppProfileTemplates import zako.zako.zako.ui.util.listAppProfileTemplates
import shirkneko.zako.sukisu.ui.util.setAppProfileTemplate import zako.zako.zako.ui.util.setAppProfileTemplate
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import org.json.JSONArray import org.json.JSONArray

View File

@@ -14,7 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
package shirkneko.zako.sukisu.ui.webui; package zako.zako.zako.ui.webui;
import java.net.URLConnection; import java.net.URLConnection;

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.webui; package zako.zako.zako.ui.webui;
import android.content.Context; import android.content.Context;
import android.util.Log; import android.util.Log;

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.webui package zako.zako.zako.ui.webui
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.ActivityManager import android.app.ActivityManager
@@ -16,7 +16,7 @@ import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateLayoutParams import androidx.core.view.updateLayoutParams
import androidx.webkit.WebViewAssetLoader import androidx.webkit.WebViewAssetLoader
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import shirkneko.zako.sukisu.ui.util.createRootShell import zako.zako.zako.ui.util.createRootShell
import java.io.File import java.io.File
@SuppressLint("SetJavaScriptEnabled") @SuppressLint("SetJavaScriptEnabled")

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.ui.webui package zako.zako.zako.ui.webui
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
@@ -14,11 +14,13 @@ import androidx.core.view.WindowInsetsControllerCompat
import com.topjohnwu.superuser.CallbackList import com.topjohnwu.superuser.CallbackList
import com.topjohnwu.superuser.ShellUtils import com.topjohnwu.superuser.ShellUtils
import com.topjohnwu.superuser.internal.UiThreadHandler import com.topjohnwu.superuser.internal.UiThreadHandler
import shirkneko.zako.sukisu.ui.util.createRootShell import zako.zako.zako.ui.util.createRootShell
import shirkneko.zako.sukisu.ui.util.listModules import zako.zako.zako.ui.util.listModules
import shirkneko.zako.sukisu.ui.util.withNewRootShell import zako.zako.zako.ui.util.withNewRootShell
import org.json.JSONArray import org.json.JSONArray
import org.json.JSONObject import org.json.JSONObject
import zako.zako.zako.ui.util.controlKpmModule
import zako.zako.zako.ui.util.listKpmModules
import java.io.File import java.io.File
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
@@ -197,6 +199,18 @@ class WebViewInterface(
} }
return currentModuleInfo.toString() return currentModuleInfo.toString()
} }
// =================== KPM支持 =============================
@JavascriptInterface
fun listAllKpm() : String {
return listKpmModules()
}
@JavascriptInterface
fun controlKpm(name: String, args: String) : Int {
return controlKpmModule(name, args)
}
} }
fun hideSystemUI(window: Window) = fun hideSystemUI(window: Window) =

View File

@@ -1,4 +1,4 @@
package shirkneko.zako.sukisu.utils package zako.zako.zako.utils
import android.content.Context import android.content.Context
import java.io.File import java.io.File

View File

@@ -1,3 +1,3 @@
libzakomk.so libzakozako.so
libzakomksd.so libzakozakozako.so
libkpmmgr.so libkpmmgr.so

View File

@@ -8,28 +8,34 @@
<string name="home_superuser_count">スーパーユーザー: %d</string> <string name="home_superuser_count">スーパーユーザー: %d</string>
<string name="home_module_count">モジュール: %d</string> <string name="home_module_count">モジュール: %d</string>
<string name="home_unsupported">非対応</string> <string name="home_unsupported">非対応</string>
<string name="home_unsupported_reason">現在、 KernelSU は GKI カーネルにのみ対応しています</string> <string name="home_unsupported_reason">カーネルの KernelSU ドライバが未検出です。カーネルが間違ってませんか?</string>
<string name="home_kernel">カーネル</string> <string name="home_kernel">カーネルのバージョン</string>
<string name="home_manager_version">アプリのバージョン</string> <string name="home_susfs">SuSFS: %s</string>
<string name="home_susfs_version">SuSFS のバージョン</string>
<string name="home_susfs_sus_su">SuS SU</string>
<string name="home_manager_version">マネージャーのバージョン</string>
<string name="home_fingerprint">Fingerprint</string> <string name="home_fingerprint">Fingerprint</string>
<string name="home_selinux_status">SELinux の状態</string> <string name="home_selinux_status">SELinux のステータス</string>
<string name="selinux_status_disabled">Disabled</string> <string name="selinux_status_disabled">無効</string>
<string name="selinux_status_enforcing">Enforcing</string> <string name="selinux_status_enforcing">Enforcing</string>
<string name="selinux_status_permissive">Permissive</string> <string name="selinux_status_permissive">Permissive</string>
<string name="selinux_status_unknown">不明</string> <string name="selinux_status_unknown">不明</string>
<string name="superuser">スーパーユーザー</string> <string name="superuser">スーパーユーザー</string>
<string name="module_failed_to_enable">%s モジュールをオンにできませんでした</string> <string name="module_failed_to_enable">%s モジュールを ON にできませんでした</string>
<string name="module_failed_to_disable">%s モジュールをオフにできませんでした</string> <string name="module_failed_to_disable">%s モジュールを OFF にできませんでした</string>
<string name="module_empty">モジュールがインストールされていません</string> <string name="module_empty">モジュールがインストールされていません</string>
<string name="module">モジュール</string> <string name="module">モジュール</string>
<string name="module_sort_action_first">並べ替え (アクション優先)</string>
<string name="module_sort_enabled_first">並べ替え (最初に有効)</string>
<string name="uninstall">アンインストール</string> <string name="uninstall">アンインストール</string>
<string name="restore">復元</string>
<string name="module_install">インストール</string> <string name="module_install">インストール</string>
<string name="install">インストール</string> <string name="install">インストール</string>
<string name="reboot">再起動</string> <string name="reboot">再起動</string>
<string name="settings">設定</string> <string name="settings">設定</string>
<string name="reboot_userspace">通常の再起動</string> <string name="reboot_userspace">ソフトリブート</string>
<string name="reboot_recovery">リカバリーへ再起動</string> <string name="reboot_recovery">リカバリーへ再起動</string>
<string name="reboot_bootloader">ブートローダー へ再起動</string> <string name="reboot_bootloader">ブートローダーへ再起動</string>
<string name="reboot_download">ダウンロードモードへ再起動</string> <string name="reboot_download">ダウンロードモードへ再起動</string>
<string name="reboot_edl">EDL へ再起動</string> <string name="reboot_edl">EDL へ再起動</string>
<string name="about">アプリについて</string> <string name="about">アプリについて</string>
@@ -37,21 +43,21 @@
<string name="module_uninstall_success">%s はアンインストールされました</string> <string name="module_uninstall_success">%s はアンインストールされました</string>
<string name="module_uninstall_failed">%s をアンインストールできませんでした</string> <string name="module_uninstall_failed">%s をアンインストールできませんでした</string>
<string name="module_version">バージョン</string> <string name="module_version">バージョン</string>
<string name="module_author">作者</string> <string name="module_author">作者</string>
<string name="refresh">更新</string> <string name="refresh">更新</string>
<string name="show_system_apps">システムアプリを表示</string> <string name="show_system_apps">システムアプリを表示</string>
<string name="hide_system_apps">システムアプリを非表示</string> <string name="hide_system_apps">システムアプリを非表示</string>
<string name="send_log">ログを送信</string> <string name="send_log">ログを送信する</string>
<string name="safe_mode">セーフモード</string> <string name="safe_mode">セーフモード</string>
<string name="reboot_to_apply">再起動すると有効化されます</string> <string name="reboot_to_apply">再起動すると有効化されます</string>
<string name="module_magisk_conflict">モジュールが Magisk との競合により利用できません!</string> <string name="module_magisk_conflict">モジュールが Magisk との競合により利用できません</string>
<string name="home_learn_kernelsu">KernelSU について</string> <string name="home_learn_kernelsu">KernelSU について学ぶ</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/ja_JP/guide/what-is-kernelsu.html</string> <string name="home_learn_kernelsu_url">https://kernelsu.org/ja_JP/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">KernelSU のインストール方法やモジュールの使い方はこちら</string> <string name="home_click_to_learn_kernelsu">KernelSU のインストール方法やモジュールの使い方を学習できます。</string>
<string name="home_support_title">支援する</string> <string name="home_support_title">支援する</string>
<string name="home_support_content">KernelSU はこれからもずっと無料でオープンソースです。寄付をして頂くことで、開発を支援していただけます。</string> <string name="home_support_content">KernelSU は今後も無料でオープンソースです。ですが、寄付をして頂けると開発者への貢献になります。</string>
<string name="profile">アプリのプロファイル</string> <string name="about_source_code"><![CDATA[ソースコードは %1$s で確認できます。<br/>%2$s チャンネルにご参加ください。]]></string>
<string name="profile_default">既定</string> <string name="profile_default">デフォルト</string>
<string name="profile_template">テンプレート</string> <string name="profile_template">テンプレート</string>
<string name="profile_custom">カスタム</string> <string name="profile_custom">カスタム</string>
<string name="profile_name">プロファイル名</string> <string name="profile_name">プロファイル名</string>
@@ -59,76 +65,205 @@
<string name="profile_namespace_inherited">継承</string> <string name="profile_namespace_inherited">継承</string>
<string name="profile_namespace_global">共通</string> <string name="profile_namespace_global">共通</string>
<string name="profile_namespace_individual">分離</string> <string name="profile_namespace_individual">分離</string>
<string name="profile_umount_modules">モジュールのアンマウント</string>
<string name="profile_groups">グループ</string> <string name="profile_groups">グループ</string>
<string name="profile_capabilities">ケーパビリティ</string>
<string name="profile_selinux_context">SELinux コンテキスト</string> <string name="profile_selinux_context">SELinux コンテキスト</string>
<string name="profile_umount_modules">モジュールのアンマウント</string>
<string name="failed_to_update_app_profile">%s のアプリのプロファイルの更新をできませでした</string> <string name="failed_to_update_app_profile">%s のアプリのプロファイルの更新をできませでした</string>
<string name="require_kernel_version" formatted="false">現在の KernelSU のバージョン %d は低すぎるため、マネージャーは正常に動作しません。バージョン %d 以上に更新してください!</string>
<string name="settings_umount_modules_default">デフォルトでモジュールのマウントを解除する</string>
<string name="settings_umount_modules_default_summary">アプリプロファイルの「モジュールのアンマウント」の共通のデフォルト値です。 有効にすると、プロファイルセットを持たないアプリのシステムに対するすべてのモジュールの変更が削除されます。</string>
<string name="settings_susfs_toggle">Kprobe フックを非表示にする</string>
<string name="settings_susfs_toggle_summary">KSU によって生成された Kprobe フックを無効化して、代替となる組み込みの非 Kprobe を有効化します。Kprobe をサポートしない 非 GKI カーネルに適用される同等の機能を実装します。</string>
<string name="profile_umount_modules_summary">このオプションを有効にすると、KernelSU はこのアプリのモジュールによって変更されたファイルを復元できるようになります。</string>
<string name="profile_selinux_domain">ドメイン</string> <string name="profile_selinux_domain">ドメイン</string>
<string name="profile_selinux_rules">ルール</string> <string name="profile_selinux_rules">ルール</string>
<string name="new_version_available">新しいバージョン %s が利用可能です。タップしてダウンロード。</string> <string name="module_update">更新</string>
<string name="module_update">アップデート</string> <string name="module_downloading">モジュールをダウンロード中: %s</string>
<string name="module_start_downloading">ダウンロードを開始: %s</string> <string name="module_start_downloading">ダウンロードを開始: %s</string>
<string name="new_version_available">新しいバージョン %s が利用可能です。タップしてダウンロード。</string>
<string name="launch_app">起動</string> <string name="launch_app">起動</string>
<string name="force_stop_app" formatted="false">強制停止</string> <string name="force_stop_app" formatted="false">強制停止</string>
<string name="restart_app">再起動</string> <string name="restart_app">再起動</string>
<string name="failed_to_update_sepolicy">SELinux ルールの更新に失敗しました %s</string> <string name="failed_to_update_sepolicy">SELinux ルールの更新に失敗しました %s</string>
<string name="profile_capabilities">ケーパビリティ</string>
<string name="module_downloading">モジュールをダウンロード中: %s</string>
<string name="profile_umount_modules_summary">このオプションを有効にすると、KernelSU はこのアプリのモジュールによって変更されたファイルを復元できるようになります。</string>
<string name="settings_umount_modules_default">既定でモジュールのマウントを解除</string>
<string name="settings_umount_modules_default_summary">アプリプロファイルの「モジュールのアンマウント」の共通のデフォルト値です。 有効にすると、プロファイルセットを持たないアプリのシステムに対するすべてのモジュールの変更が削除されます。</string>
<string name="module_changelog">変更履歴</string> <string name="module_changelog">変更履歴</string>
<string name="app_profile_template_import_success">インポート成功</string> <string name="settings_profile_template">アプリプロファイルのテンプレート</string>
<string name="app_profile_export_to_clipboard">クリップボードからエクスポート</string> <string name="settings_profile_template_summary">アプリプロファイルのローカルおよびオンラインテンプレートを管理します。</string>
<string name="app_profile_template_export_empty">エクスポートするローカル テンプレートが見つかりません!</string>
<string name="app_profile_template_id_exist">テンプレート ID はすでに存在します!</string>
<string name="app_profile_import_from_clipboard">クリップボードからインポート</string>
<string name="module_changelog_failed">変更ログの取得に失敗しました: %s</string>
<string name="app_profile_template_name">名前</string>
<string name="app_profile_template_id_invalid">無効なテンプレート ID</string>
<string name="app_profile_template_sync">オンラインテンプレートの同期</string>
<string name="app_profile_template_create">テンプレートの作成</string> <string name="app_profile_template_create">テンプレートの作成</string>
<string name="app_profile_template_readonly">読み取り専用</string>
<string name="app_profile_import_export">インポート/エクスポート</string>
<string name="app_profile_template_save_failed">テンプレートの保存に失敗しました</string>
<string name="app_profile_template_edit">テンプレートの編集</string> <string name="app_profile_template_edit">テンプレートの編集</string>
<string name="app_profile_template_id">ID</string> <string name="app_profile_template_id">ID</string>
<string name="settings_profile_template">アプリプロファイルのテンプレート</string> <string name="app_profile_template_id_invalid">無効なテンプレート ID</string>
<string name="app_profile_template_name">名前</string>
<string name="app_profile_template_description">説明</string> <string name="app_profile_template_description">説明</string>
<string name="app_profile_template_save">保存</string> <string name="app_profile_template_save">保存</string>
<string name="settings_profile_template_summary">アプリプロファイルのローカルおよびオンラインテンプレートを管理する</string>
<string name="app_profile_template_delete">消去</string> <string name="app_profile_template_delete">消去</string>
<string name="app_profile_template_import_empty">クリップボードが空です!</string>
<string name="app_profile_template_view">テンプレートを表示</string> <string name="app_profile_template_view">テンプレートを表示</string>
<string name="settings_check_update">アップデートを確認</string> <string name="app_profile_template_readonly">読み取り専用</string>
<string name="settings_check_update_summary">アプリを開いたときにアップデートを自動的に確認する</string> <string name="app_profile_template_id_exist">テンプレート ID はすでに存在します!</string>
<string name="app_profile_import_export">インポートとエクスポート</string>
<string name="app_profile_import_from_clipboard">クリップボードからインポート</string>
<string name="app_profile_export_to_clipboard">クリップボードからエクスポート</string>
<string name="app_profile_template_export_empty">エクスポートするローカル テンプレートが見つかりません!</string>
<string name="app_profile_template_import_success">インポートが成功しました</string>
<string name="app_profile_template_sync">オンラインテンプレートの同期</string>
<string name="app_profile_template_save_failed">テンプレートの保存に失敗しました</string>
<string name="app_profile_template_import_empty">クリップボードが空です!</string>
<string name="module_changelog_failed">変更ログの取得に失敗しました: %s</string>
<string name="settings_check_update">更新を確認する</string>
<string name="settings_check_update_summary">アプリを開いたときに更新を自動的に確認します。</string>
<string name="grant_root_failed">root の付与に失敗しました!</string> <string name="grant_root_failed">root の付与に失敗しました!</string>
<string name="action">アクション</string>
<string name="open">開く</string> <string name="open">開く</string>
<string name="enable_web_debugging">WebView デバッグを有効する</string> <string name="enable_web_debugging">WebView デバッグを有効する</string>
<string name="enable_web_debugging_summary">WebUI のデバッグに使用できます。必要な場合にのみ有効にしてください。</string> <string name="enable_web_debugging_summary">WebUI のデバッグに使用できます。必要な場合にのみ有効にしてください。</string>
<string name="select_file_tip">%1$s パーティション イメージが推奨されます</string> <string name="direct_install">直接インストール (推奨)</string>
<string name="select_kmi">KMI を選択してください</string> <string name="select_file">ファイルを選択</string>
<string name="install_next">次に</string>
<string name="install_inactive_slot">非アクティブなスロットにインストール (OTA 後)</string> <string name="install_inactive_slot">非アクティブなスロットにインストール (OTA 後)</string>
<string name="install_inactive_slot_warning">再起動後、デバイスは**強制的に**、現在非アクティブなスロットから起動します。 <string name="install_inactive_slot_warning">再起動後、デバイスは**強制的に**、現在非アクティブなスロットから起動します。
\nこのオプションは、OTA が完了した後にのみ使用してください。 \nこのオプションは、OTA が完了した後にのみ使用してください。
\n続</string> \n続行しますか</string>
<string name="direct_install">直接インストール (推奨)</string> <string name="install_next">次へ</string>
<string name="select_file">ファイルを選択してください</string> <string name="select_file_tip">%1$s のパーティションイメージを推奨します</string>
<string name="select_kmi">KMI を選択してください</string>
<string name="settings_uninstall">アンインストール</string>
<string name="settings_uninstall_temporary">一時的にアンインストールする</string>
<string name="settings_uninstall_permanent">完全にアンインストールする</string> <string name="settings_uninstall_permanent">完全にアンインストールする</string>
<string name="settings_restore_stock_image">ストックイメージを復元</string> <string name="settings_restore_stock_image">ストックイメージを復元</string>
<string name="settings_uninstall_temporary">一時的にアンインストールする</string>
<string name="settings_uninstall">アンインストール</string>
<string name="settings_uninstall_temporary_message">KernelSU を一時的にアンインストールし、次回の再起動後に元の状態に戻します。</string> <string name="settings_uninstall_temporary_message">KernelSU を一時的にアンインストールし、次回の再起動後に元の状態に戻します。</string>
<string name="settings_uninstall_permanent_message">KernelSU (ルートおよびすべてのモジュール) を完全かつ永久にアンインストールします。</string> <string name="settings_uninstall_permanent_message">KernelSU (root およびすべてのモジュール) を完全かつ恒久的にアンインストールします。</string>
<string name="settings_restore_stock_image_message">バックアップが存在する場合、工場出荷時のイメージを復元できます (OTA の前に使用してください)。KernelSU をアンインストールする必要がある場合は、「完全にアンインストールする」を使用してください。</string> <string name="settings_restore_stock_image_message">バックアップが存在する場合、工場出荷時のイメージを復元できます (OTA の前に使用してください)。KernelSU をアンインストールする必要がある場合は、「完全にアンインストールする」を使用してください。</string>
<string name="flashing">フラッシュ</string> <string name="flashing">フラッシュ</string>
<string name="flash_success">フラッシュ成功</string> <string name="flash_success">フラッシュ成功しました</string>
<string name="flash_failed">フラッシュ失敗</string> <string name="flash_failed">フラッシュ失敗しました</string>
<string name="selected_lkm">選択された LKM: %s</string> <string name="selected_lkm">選択された LKM: %s</string>
<string name="save_log">ログを保存</string> <string name="save_log">ログを保存</string>
<string name="action">アクション</string>
<string name="log_saved">保存されたログ</string> <string name="log_saved">保存されたログ</string>
<string name="module_sort_enabled_first">並べ替え(最初に有効)</string> <string name="status_supported">対応</string>
<string name="module_sort_action_first">並べ替え(アクション優先)</string> <string name="status_not_supported">非対応</string>
<string name="status_unknown">不明</string>
<string name="sus_su_mode">SuS SU モード:</string>
<!-- Module related -->
<string name="module_install_confirm">%1$s のモジュールをインストールしますか?</string>
<string name="unknown_module">不明なモジュール</string>
<!-- Restore related -->
<string name="restore_confirm_title">モジュールの復元を確認</string>
<string name="restore_confirm_message">この操作によりモジュールが上書きされます。続行しますか?</string>
<string name="confirm">確認</string>
<string name="cancel">キャンセル</string>
<!-- Backup related -->
<string name="backup_success">バックアップが完了しました (tar.gz)</string>
<string name="backup_failed">バックアップに失敗: %1$s</string>
<string name="backup_modules">モジュールをバックアップ</string>
<string name="restore_modules">モジュールを復元</string>
<!-- Restore related messages -->
<string name="restore_success">モジュールは正常に復元されました、再起動が必要です</string>
<string name="restore_failed">復元に失敗: %1$s</string>
<string name="restart_now">今すぐ再起動</string>
<string name="unknown_error">不明なエラー</string>
<!-- Command related -->
<string name="command_execution_failed">コマンドの実行に失敗しました: %1$s</string>
<!-- Allowlist related -->
<string name="allowlist_backup_success">許可リストのバックアップが成功しました</string>
<string name="allowlist_backup_failed">許可リストのバックアップに失敗: %1$s</string>
<string name="allowlist_restore_confirm_title">許可リストの復元を確認</string>
<string name="allowlist_restore_confirm_message">この操作により許可リストが上書きされます。続行しますか?</string>
<string name="allowlist_restore_success">許可リストの復元が成功しました</string>
<string name="allowlist_restore_failed">許可リストの復元に失敗: %1$s</string>
<string name="backup_allowlist">許可リストをバックアップ</string>
<string name="restore_allowlist">許可リストを復元</string>
<string name="settings_custom_background">カスタム背景を設定</string>
<string name="settings_custom_background_summary">カスタム背景を設定します。</string>
<string name="settings_card_manage">カードの管理</string>
<string name="settings_card_alpha">カードのアルファ</string>
<string name="settings_restore_default">デフォルトに復元</string>
<string name="home_android_version">Android のバージョン</string>
<string name="home_device_model">デバイスモデル</string>
<string name="su_not_allowed">%s にスーパーユーザー権限を付与することはできません</string>
<string name="settings_disable_su">su の互換性を無効化する</string>
<string name="settings_disable_su_summary">su コマンドを使用してアプリが root 権限を取得する動作を一時的に無効化します (既存の root プロセスは影響を受けません)。</string>
<string name="using_mksu_manager">SukiSU Beta Manager を使用しています。</string>
<string name="module_install_multiple_confirm">選択した %d 個のモジュールをインストールしてもよろしいですか?</string>
<string name="module_install_multiple_confirm_with_names">%1$d 個のモジュールをインストールしてもよろしいですか?\n\n%2$s</string>
<string name="more_settings">その他の設定</string>
<string name="selinux">SELinux</string>
<string name="selinux_enabled">有効</string>
<string name="selinux_disabled">無効</string>
<string name="simple_mode">シンプルモード</string>
<string name="simple_mode_summary">ON にすると不要なカードを非表示にします。</string>
<string name="hide_kernel_kernelsu_version">カーネルのバージョンを非表示にする</string>
<string name="hide_kernel_kernelsu_version_summary">カーネルのバージョンを非表示にします。</string>
<string name="hide_other_info">その他の情報を非表示にする</string>
<string name="hide_other_info_summary">ホームページ上のスーパーユーザー、モジュール、KPM モジュールの数に関する情報を非表示にします。</string>
<string name="hide_susfs_status">SuSFS ステータスを非表示にする</string>
<string name="hide_susfs_status_summary">ホームページ上の SuSFS ステータス情報を非表示にします。</string>
<string name="theme_mode">テーマモード</string>
<string name="theme_follow_system">システムに従う</string>
<string name="theme_light">ライトカラー</string>
<string name="theme_dark">ダークカラー</string>
<string name="manual_hook">手動でフック</string>
<string name="dynamic_color_title">ダイナミックカラー</string>
<string name="dynamic_color_summary">システムテーマのダイナミックカラーを使用します</string>
<string name="choose_theme_color">テーマカラーを選択</string>
<string name="color_default">ホワイト</string>
<string name="color_blue">ブルー</string>
<string name="color_green">グリーン</string>
<string name="color_purple">パープル</string>
<string name="color_orange">オレンジ</string>
<string name="color_pink">ピンク</string>
<string name="color_gray">グレー</string>
<string name="color_ivory">アイボリー</string>
<string name="flash_option">ブラシの設定</string>
<string name="flash_option_tip">フラッシュするファイルを選択</string>
<string name="horizon_kernel">AnyKernel3 をフラッシュ</string>
<string name="root_required">root 権限が必要です</string>
<string name="copy_failed">ファイルのコピーに失敗しました</string>
<string name="reboot_complete_title">スクラブが完了しました</string>
<string name="reboot_complete_msg">すぐに再起動しますか?</string>
<string name="yes">はい</string>
<string name="no">いいえ</string>
<string name="failed_reboot">再起動に失敗しました</string>
<string name="batch_authorization">bulk ライセンス</string>
<string name="batch_cancel_authorization">認証を一括でキャンセル</string>
<string name="backup">バックアップ</string>
<string name="color_yellow">イエロー</string>
<string name="kpm">KPM モジュール</string>
<string name="kpm_title">カーネルモジュール</string>
<string name="kpm_empty">カーネルモジュールは現在インストールされていません</string>
<string name="kpm_version">リリース</string>
<string name="kpm_author">作者</string>
<string name="kpm_uninstall">アンインストール</string>
<string name="kpm_uninstall_success">アンインストールに失敗しました</string>
<string name="kpm_uninstall_failed">アンインストールに失敗しました</string>
<string name="kpm_install">インストールを選択</string>
<string name="kpm_install_success">KPM モジュールの読み込みに成功しました</string>
<string name="kpm_install_failed">KPM モジュールの読み込みに失敗しました</string>
<string name="kpm_args">KPM パラメーター</string>
<string name="kpm_control">完全</string>
<string name="home_kpm_version">KPM のバージョン</string>
<string name="close_notice">閉じる</string>
<string name="kernel_module_notice">以下のカーネルモジュール関数は KernelPatch によって開発され、SukiSU Ultra のカーネルモジュール関数を含むように変更されました</string>
<string name="home_ContributionCard_kernelsu">SukiSU Ultra の今後にご期待ください</string>
<string name="kpm_control_success">成功</string>
<string name="kpm_control_failed">失敗</string>
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra は将来的に KSU から比較的に独立したブランチになりますが、公式の KernelSU や MKSU などの貢献に繋がるでしょう!</string>
<string name="not_supported">非対応</string>
<string name="supported">対応</string>
<string name="home_kpm_module">KPM モジュール数: %d </string>
<string name="kpm_invalid_file">無効な KPM ファイル</string>
<string name="kernel_patched">カーネルはパッチされていません</string>
<string name="kernel_not_enabled">カーネルは設定されていません</string>
<string name="custom_settings">カスタム設定</string>
<string name="kpm_install_mode">インストール</string>
<string name="kpm_install_mode_load">読み込む</string>
<string name="kpm_install_mode_embed">埋め込む</string>
<string name="kpm_install_mode_description">モジュールのインストールモードを選択してください:\n\n読み込む: モジュールを一時的に読み込みます\n埋め込む: システムに恒久的にインストールします</string>
<string name="log_failed_to_check_module_file">モジュールファイルの存在を確認できませんでした</string>
<string name="snackbar_failed_to_check_module_file">モジュールファイルが存在するか確認できません</string>
<string name="confirm_uninstall_title">アンインストールを確認</string>
<string name="confirm_uninstall_confirm">アンインストール中</string>
<string name="confirm_uninstall_dismiss">中止</string>
<string name="theme_color">テーマカラー</string>
<string name="invalid_file_type">ファイルの種類が正しくありません。.kpm ファイルを選択してください。</string>
<string name="confirm_uninstall_title_with_filename">アンインストール</string>
<string name="confirm_uninstall_content">次の KPM モジュールがアンインストールされます:\n%s</string>
</resources> </resources>

View File

@@ -1,2 +1,266 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources></resources> <resources>
<string name="app_name" translatable="false">SukiSU Ultra</string>
<string name="home">Home</string>
<string name="home_not_installed">Chưa cài đặt</string>
<string name="home_click_to_install">Nhấn vào đây để cài đặt</string>
<string name="home_working">Đang hoạt động</string>
<string name="home_working_version">Phiên bản: %d</string>
<string name="home_superuser_count">Superusers: %d</string>
<string name="home_module_count">Mô-đun: %d</string>
<string name="home_unsupported">Không được hỗ trợ</string>
<string name="home_unsupported_reason">Không phát hiện được trình điều khiển KernelSU trên kernel của bạn.</string>
<string name="home_kernel">Phiên bản Kernel</string>
<string name="home_susfs">SuSFS: %s</string>
<string name="home_susfs_version">Phiên bản SuSFS</string>
<string name="home_susfs_sus_su">SuS SU</string>
<string name="home_manager_version">Phiên bản quản lý</string>
<string name="home_fingerprint">Dấu vân tay</string>
<string name="home_selinux_status">Trạng thái SELinux</string>
<string name="selinux_status_disabled">Disabled</string>
<string name="selinux_status_enforcing">Enforcing</string>
<string name="selinux_status_permissive">Permissive</string>
<string name="selinux_status_unknown">Không rõ</string>
<string name="superuser">Superuser</string>
<string name="module_failed_to_enable">Không thể bật mô-đun: %s</string>
<string name="module_failed_to_disable">Không thể vô hiệu hóa mô-đun: %s</string>
<string name="module_empty">Không có mô-đun nào được cài đặt</string>
<string name="module">Mô-đun</string>
<string name="module_sort_action_first">Sắp xếp (theo hành động)</string>
<string name="module_sort_enabled_first">Sắp xếp (theo trạng thái)</string>
<string name="uninstall">Gỡ cài đặt</string>
<string name="restore">Khôi phục</string>
<string name="module_install">Cài đặt</string>
<string name="install">Cài đặt</string>
<string name="reboot">Khởi động lại</string>
<string name="settings">Cài đặt</string>
<string name="reboot_userspace">Khởi động lại không gian người dùng</string>
<string name="reboot_recovery">Khởi động lại vào Recovery</string>
<string name="reboot_bootloader">Khởi động lại vào Bootloader</string>
<string name="reboot_download">Khởi động lại vào Download mode</string>
<string name="reboot_edl">Khởi động lại vào EDL</string>
<string name="about">Thông tin thêm</string>
<string name="module_uninstall_confirm">Bạn có chắc chắn muốn gỡ cài đặt mô-đun %s không?</string>
<string name="module_uninstall_success">%s đã gỡ cài đặt</string>
<string name="module_uninstall_failed">Không thể gỡ cài đặt: %s</string>
<string name="module_version">Phiên bản</string>
<string name="module_author">Tác giả</string>
<string name="refresh">Làm mới</string>
<string name="show_system_apps">Hiển thị ứng dụng hệ thống</string>
<string name="hide_system_apps">Ẩn ứng dụng hệ thống</string>
<string name="send_log">Gửi nhật ký</string>
<string name="safe_mode">Chế độ an toàn</string>
<string name="reboot_to_apply">Khởi động lại để có hiệu lực</string>
<string name="module_magisk_conflict">Các mô-đun không khả dụng do xung đột với Magisk!</string>
<string name="home_learn_kernelsu">Tìm hiểu KernelSU</string>
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
<string name="home_click_to_learn_kernelsu">Tìm hiểu cách cài đặt KernelSU và sử dụng các mô-đun</string>
<string name="home_support_title">Hỗ trợ chúng tôi</string>
<string name="home_support_content">KernelSU sẽ luôn là miễn phí và mã nguồn mở. Tuy nhiên, bạn có thể cho chúng tôi thấy rằng bạn quan tâm bằng cách quyên góp.</string>
<string name="about_source_code"><![CDATA[View source code at %1$s<br/>Tham gia kênh %2$s của chúng tôi]]></string>
<string name="profile" translatable="false">Hồ sơ ứng dụng</string>
<string name="profile_default">Mặc định</string>
<string name="profile_template">Bản mẫu</string>
<string name="profile_custom">Tùy chỉnh</string>
<string name="profile_name">Tên hồ sơ</string>
<string name="profile_namespace">Tên không gian gắn kết</string>
<string name="profile_namespace_inherited">Được thừa hưởng</string>
<string name="profile_namespace_global">Toàn cầu</string>
<string name="profile_namespace_individual">Cá nhân</string>
<string name="profile_groups">Nhóm</string>
<string name="profile_capabilities">Khả năng</string>
<string name="profile_selinux_context">Bối cảnh SELinux</string>
<string name="profile_umount_modules">Bỏ gắn kết mô-đun</string>
<string name="failed_to_update_app_profile">Không cập nhật được Hồ sơ ứng dụng cho %s</string>
<string name="require_kernel_version" formatted="false">Phiên bản KernelSU hiện tại %d quá thấp để trình quản lý hoạt động bình thường. Vui lòng nâng cấp lên phiên bản %d hoặc cao hơn!</string>
<string name="settings_umount_modules_default">Bỏ gắn kết các mô-đun theo mặc định</string>
<string name="settings_umount_modules_default_summary">Giá trị mặc định cho \"Bỏ gắn kết các mô-đun\" trong Hồ sơ ứng dụng. Nếu được bật, nó sẽ xóa tất cả các sửa đổi của mô-đun đối với hệ thống đối và các ứng dụng không có hồ sơ được đặt.</string>
<string name="settings_susfs_toggle">Ẩn móc kprobe</string>
<string name="settings_susfs_toggle_summary">Vô hiệu hóa các móc kprobe do KSU tạo ra và kích hoạt các móc không phải kprobe tích hợp sẵn, triển khai sẽ được áp dụng cho hạt nhân không phải GKI, không hỗ trợ krp</string>
<string name="profile_umount_modules_summary">Bật tùy chọn này sẽ cho phép KernelSU khôi phục mọi tệp đã được các mô-đun sửa đổi cho ứng dụng này.</string>
<string name="profile_selinux_domain">Domain</string>
<string name="profile_selinux_rules">Quy tắc</string>
<string name="module_update">Cập nhật</string>
<string name="module_downloading">Đang tải xuống mô-đun: %s</string>
<string name="module_start_downloading">Bắt đầu tải xuống: %s</string>
<string name="new_version_available">Phiên bản mới %s đã có sẵn, hãy nhấp để cập nhật.</string>
<string name="launch_app">Khởi chạy</string>
<string name="force_stop_app" formatted="false">Buộc dừng</string>
<string name="restart_app">Khởi động lại</string>
<string name="failed_to_update_sepolicy">Không cập nhật được quy tắc SELinux cho %s</string>
<string name="module_changelog">Nhật ký thay đổi</string>
<string name="settings_profile_template">Mẫu hồ sơ ứng dụng</string>
<string name="settings_profile_template_summary">Quản lý mẫu cục bộ và trực tuyến của Hồ sơ ứng dụng</string>
<string name="app_profile_template_create">Tạo mẫu</string>
<string name="app_profile_template_edit">Chỉnh sửa mẫu</string>
<string name="app_profile_template_id">ID</string>
<string name="app_profile_template_id_invalid">ID mẫu không hợp lệ</string>
<string name="app_profile_template_name">Tên</string>
<string name="app_profile_template_description">Miêu tả</string>
<string name="app_profile_template_save">Lưu</string>
<string name="app_profile_template_delete">Xoá</string>
<string name="app_profile_template_view">Xem mẫu</string>
<string name="app_profile_template_readonly">Chỉ đọc</string>
<string name="app_profile_template_id_exist">ID mẫu đã tồn tại!</string>
<string name="app_profile_import_export">Nhập/Xuất</string>
<string name="app_profile_import_from_clipboard">Nhập từ bộ nhớ tạm clipboard</string>
<string name="app_profile_export_to_clipboard">Xuất vào bộ nhớ tạm clipboard</string>
<string name="app_profile_template_export_empty">Cannot find local template to export!</string>
<string name="app_profile_template_import_success">Đã nhập thành công</string>
<string name="app_profile_template_sync">Đồng bộ mẫu trực tuyến</string>
<string name="app_profile_template_save_failed">Không lưu được mẫu</string>
<string name="app_profile_template_import_empty">Bộ nhớ tạm đang trống!</string>
<string name="module_changelog_failed">Lấy nhật ký thay đổi không thành công: %s</string>
<string name="settings_check_update">Kiểm tra cập nhật</string>
<string name="settings_check_update_summary">Tự động kiểm tra cập nhật khi mở ứng dụng</string>
<string name="grant_root_failed">Không cấp được quyền root!</string>
<string name="action">Khởi chạy</string>
<string name="open">Mở</string>
<string name="enable_web_debugging">Bật gỡ lỗi WebView</string>
<string name="enable_web_debugging_summary">Có thể sử dụng để gỡ lỗi WebUI. Vui lòng chỉ bật khi cần thiết.</string>
<string name="direct_install">Cài đặt trực tiếp (Khuyến nghị)</string>
<string name="select_file">Chọn một tập tin</string>
<string name="install_inactive_slot">Cài đặt vào khe không hoạt động (Sau OTA)</string>
<string name="install_inactive_slot_warning">Thiết bị của bạn sẽ **BUỘC** phải khởi động vào khe cắm hiện tại không hoạt động sau khi khởi động lại!\nChỉ sử dụng tùy chọn này sau khi OTA hoàn tất.\nTiếp tục?</string>
<string name="install_next">Kế tiếp</string>
<string name="select_file_tip">Phân vùng %1$s được khuyến nghị</string>
<string name="select_kmi">Chọn KMI</string>
<string name="settings_uninstall">Uninstall</string>
<string name="settings_uninstall_temporary">Gỡ cài đặt tạm thời</string>
<string name="settings_uninstall_permanent">Gỡ cài đặt vĩnh viễn</string>
<string name="settings_restore_stock_image">Khôi phục img ban đầu</string>
<string name="settings_uninstall_temporary_message">Gỡ cài đặt tạm thời KernelSU, khôi phục lại trạng thái ban đầu sau lần khởi động lại tiếp theo.</string>
<string name="settings_uninstall_permanent_message">Gỡ cài đặt KernelSU (Root và tất cả các mô-đun) hoàn toàn và vĩnh viễn.</string>
<string name="settings_restore_stock_image_message">Khôi phục ảnh gốc (nếu có bản sao lưu), thường được sử dụng trước OTA; nếu bạn cần gỡ cài đặt KernelSU, vui lòng sử dụng \"Gỡ cài đặt vĩnh viễn\".</string>
<string name="flashing">Đang flash...</string>
<string name="flash_success">Flash thành công</string>
<string name="flash_failed">Lỗi flash</string>
<string name="selected_lkm">LKM đã chọn: %s</string>
<string name="save_log">Lưu nhật ký</string>
<string name="log_saved">Nhật ký đã lưu</string>
<string name="status_supported">Được hỗ trợ</string>
<string name="status_not_supported">Không được hỗ trợ</string>
<string name="status_unknown">Không rõ</string>
<string name="sus_su_mode">Chế độ SuS: SU</string>
<!-- Module related -->
<string name="module_install_confirm">Xác nhận cài đặt mô-đun %1$s ?</string>
<string name="unknown_module">Mô-đun không xác định</string>
<!-- Restore related -->
<string name="restore_confirm_title">Xác nhận khôi phục mô-đun</string>
<string name="restore_confirm_message">Hành động này sẽ ghi đè lên tất cả các mô-đun hiện có. Tiếp tục?</string>
<string name="confirm">Xác nhận</string>
<string name="cancel">Hủy bỏ</string>
<!-- Backup related -->
<string name="backup_success">Sao lưu thành công (tar.gz)</string>
<string name="backup_failed">Sao lưu không thành công: %1$s</string>
<string name="backup_modules">Sao lưu mô-đun</string>
<string name="restore_modules">Khôi phục các mô-đun</string>
<!-- Restore related messages -->
<string name="restore_success">Các mô-đun đã được khôi phục thành công, cần khởi động lại</string>
<string name="restore_failed">Khôi phục không thành công: %1$s</string>
<string name="restart_now">Khởi động lại ngay</string>
<string name="unknown_error">Lỗi không xác định</string>
<!-- Command related -->
<string name="command_execution_failed">Thực hiện lệnh không thành công: %1$s</string>
<!-- Allowlist related -->
<string name="allowlist_backup_success">Sao lưu danh sách cho phép thành công</string>
<string name="allowlist_backup_failed">Sao lưu danh sách cho phép không thành công: %1$s</string>
<string name="allowlist_restore_confirm_title">Xác nhận khôi phục danh sách cho phép</string>
<string name="allowlist_restore_confirm_message">Hành động này sẽ ghi đè lên danh sách cho phép hiện tại. Tiếp tục?</string>
<string name="allowlist_restore_success">Đã khôi phục danh sách cho phép thành công</string>
<string name="allowlist_restore_failed">Khôi phục danh sách cho phép không thành công: %1$s</string>
<string name="backup_allowlist">Sao lưu danh sách cho phép</string>
<string name="restore_allowlist">Khôi phục danh sách cho phép</string>
<string name="settings_custom_background">Cài đặt nền tùy chỉnh</string>
<string name="settings_custom_background_summary">Cài đặt tùy chỉnh nền</string>
<string name="settings_card_manage">Quản lý thẻ</string>
<string name="settings_card_alpha">Thẻ alpha</string>
<string name="settings_restore_default">Khôi phục mặc định</string>
<string name="home_android_version">Phiên bản Android</string>
<string name="home_device_model">Model thiết bị</string>
<string name="su_not_allowed">Không được phép cấp siêu người dùng cho %s</string>
<string name="settings_disable_su">Vô hiệu hóa khả năng tương thích su</string>
<string name="settings_disable_su_summary">Tạm thời vô hiệu hóa mọi ứng dụng khỏi quyền root thông qua lệnh su (các tiến trình root hiện có sẽ không bị ảnh hưởng).</string>
<string name="using_mksu_manager">Bạn đang sử dụng trình quản lý SukiSU thử nghiệm</string>
<string name="module_install_multiple_confirm">Bạn có chắc chắn muốn cài đặt các mô-đun %d đã chọn không?</string>
<string name="module_install_multiple_confirm_with_names">Bạn có chắc chắn muốn cài đặt các mô-đun %1$d sau không? \n\n%2$s</string>
<string name="more_settings">Nhiều thiết lập hơn</string>
<string name="selinux">SELinux</string>
<string name="selinux_enabled">Đã bật</string>
<string name="selinux_disabled">Đã tắt</string>
<string name="simple_mode">Chế độ đơn giản</string>
<string name="simple_mode_summary">Ẩn các thẻ không cần thiết khi bật</string>
<string name="hide_kernel_kernelsu_version">Ẩn phiên bản kernel</string>
<string name="hide_kernel_kernelsu_version_summary">Ẩn phiên bản kernel hiện tại</string>
<string name="hide_other_info">Ẩn thông tin thêm</string>
<string name="hide_other_info_summary">Ẩn thông tin về số lượng app đã được cấp root, mô-đun và mô-đun KPM trên trang chủ</string>
<string name="hide_susfs_status">Ẩn trạng thái SuSFS</string>
<string name="hide_susfs_status_summary">Ẩn thông tin trạng thái SuSFS trên trang chủ</string>
<string name="theme_mode">Chế độ chủ đề</string>
<string name="theme_follow_system">Theo hệ thống</string>
<string name="theme_light">Sáng</string>
<string name="theme_dark">Tối</string>
<string name="manual_hook">Móc thủ công</string>
<string name="dynamic_color_title">Màu sắc động</string>
<string name="dynamic_color_summary">Màu sắc động sử dụng chủ đề hệ thống</string>
<string name="choose_theme_color">Chọn màu chủ đề</string>
<string name="color_default">Trắng</string>
<string name="color_blue">Xanh dương</string>
<string name="color_green">Xanh lá</string>
<string name="color_purple">Tím</string>
<string name="color_orange">Cam</string>
<string name="color_pink">Hồng</string>
<string name="color_gray">Xám</string>
<string name="color_ivory">Ngà voi</string>
<string name="flash_option">Tùy chọn cọ</string>
<string name="flash_option_tip">Chọn tập tin cần flash</string>
<string name="horizon_kernel">File Anykernel3</string>
<string name="root_required">Yêu cầu quyền root</string>
<string name="copy_failed">Sao chép tập tin không thành công</string>
<string name="reboot_complete_title">Hoàn tất</string>
<string name="reboot_complete_msg">khởi động lại ngay lập tức?</string>
<string name="yes"></string>
<string name="no">Không</string>
<string name="failed_reboot">Khởi động lại không thành công</string>
<string name="batch_authorization">Giấy phép số lượng lớn</string>
<string name="batch_cancel_authorization">Hủy ủy quyền hàng loạt</string>
<string name="backup">Sao lưu</string>
<string name="color_yellow">Vàng</string>
<string name="kpm">Mô-đun kpm</string>
<string name="kpm_title">Mô-đun KPM</string>
<string name="kpm_empty">Không có mô-đun nào được cài đặt.</string>
<string name="kpm_version">Phát hành</string>
<string name="kpm_author">Tác giả</string>
<string name="kpm_uninstall">Gỡ cài đặt</string>
<string name="kpm_uninstall_success">Đã gỡ cài đặt thành công</string>
<string name="kpm_uninstall_failed">Không thể gỡ cài đặt</string>
<string name="kpm_install">Cài đặt</string>
<string name="kpm_install_success">Tải module kpm thành công</string>
<string name="kpm_install_failed">Tải module kpm không thành công</string>
<string name="kpm_args">thông số kpm</string>
<string name="kpm_control">Tùy chỉnh</string>
<string name="home_kpm_version">Phiên bản KPM</string>
<string name="close_notice">Đóng</string>
<string name="kernel_module_notice">Các chức năng mô-đun kernel sau đây được KernelPatch phát triển và sửa đổi để bao gồm các chức năng mô-đun hạt nhân của SukiSU Ultra</string>
<string name="home_ContributionCard_kernelsu">SukiSU Ultra mong đợi:</string>
<string name="kpm_control_success">Thành công</string>
<string name="kpm_control_failed">Thất bại</string>
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra sẽ là một nhánh tương đối độc lập của KSU trong tương lai, nhưng xin cảm ơn KernelSU và MKSU chính thức vì những đóng góp của họ!</string>
<string name="not_supported">Không được hỗ trợ</string>
<string name="supported">Được hỗ trợ</string>
<string name="home_kpm_module">Mô-đun KPM%d </string>
<string name="kpm_invalid_file">Tệp KPM không hợp lệ</string>
<string name="kernel_patched">chưa được vá</string>
<string name="kernel_not_enabled">chưa được vá</string>
<string name="custom_settings">Cài đặt tùy chỉnh</string>
<string name="kpm_install_mode">Cài đặt</string>
<string name="kpm_install_mode_load">Tải</string>
<string name="kpm_install_mode_embed">Nhúng</string>
<string name="kpm_install_mode_description">Vui lòng chọn chế độ cài đặt mô-đun: \n\nTải: Tải tạm thời mô-đun \nNhúng: Cài đặt vĩnh viễn vào hệ thống</string>
<string name="log_failed_to_check_module_file">Không kiểm tra được sự tồn tại của tệp mô-đun</string>
<string name="snackbar_failed_to_check_module_file">Không thể kiểm tra xem tệp mô-đun có tồn tại không</string>
<string name="confirm_uninstall_title">Xác nhận gỡ cài đặt</string>
<string name="confirm_uninstall_confirm">loại bỏ</string>
<string name="confirm_uninstall_dismiss">bãi bỏ</string>
<string name="theme_color">Màu chủ đề</string>
</resources>

View File

@@ -180,7 +180,7 @@
<string name="su_not_allowed">不允许授予 %s 超级用户权限</string> <string name="su_not_allowed">不允许授予 %s 超级用户权限</string>
<string name="settings_disable_su">禁用 su 兼容性</string> <string name="settings_disable_su">禁用 su 兼容性</string>
<string name="settings_disable_su_summary">临时禁止任何应用程序通过 su 命令获取 Root 权限(现有的 Root 进程不受影响)</string> <string name="settings_disable_su_summary">临时禁止任何应用程序通过 su 命令获取 Root 权限(现有的 Root 进程不受影响)</string>
<string name="using_mksu_manager">你正在使用的是 MKSU 第三方管理器</string> <string name="using_mksu_manager">你正在使用的是 SukiSU Beta 版管理器</string>
<string name="module_install_multiple_confirm">确定要安装选择的 %d 个模块吗?</string> <string name="module_install_multiple_confirm">确定要安装选择的 %d 个模块吗?</string>
<string name="module_install_multiple_confirm_with_names">确定要安装以下 %1$d 个模块吗?\n\n%2$s</string> <string name="module_install_multiple_confirm_with_names">确定要安装以下 %1$d 个模块吗?\n\n%2$s</string>
<string name="more_settings">更多设置</string> <string name="more_settings">更多设置</string>
@@ -191,6 +191,10 @@
<string name="simple_mode_summary">开启后将隐藏不必要的卡片</string> <string name="simple_mode_summary">开启后将隐藏不必要的卡片</string>
<string name="hide_kernel_kernelsu_version">隐藏内核版本号</string> <string name="hide_kernel_kernelsu_version">隐藏内核版本号</string>
<string name="hide_kernel_kernelsu_version_summary">隐藏内核部分的 KernelSU 版本号</string> <string name="hide_kernel_kernelsu_version_summary">隐藏内核部分的 KernelSU 版本号</string>
<string name="hide_other_info">强迫症开关</string>
<string name="hide_other_info_summary">隐藏主页上的超级用户数、模块数和 KPM 模块数信息</string>
<string name="hide_susfs_status">隐藏 SuSFS 状态信息</string>
<string name="hide_susfs_status_summary">隐藏主页上的 SuSFS 状态信息</string>
<string name="theme_mode">主题模式</string> <string name="theme_mode">主题模式</string>
<string name="theme_follow_system">跟随系统</string> <string name="theme_follow_system">跟随系统</string>
<string name="theme_light">浅色</string> <string name="theme_light">浅色</string>
@@ -225,13 +229,37 @@
<string name="kpm_empty">暂无已安装的内核模块</string> <string name="kpm_empty">暂无已安装的内核模块</string>
<string name="kpm_version">版本</string> <string name="kpm_version">版本</string>
<string name="kpm_author">作者</string> <string name="kpm_author">作者</string>
<string name="kpm_execute">执行</string>
<string name="kpm_uninstall">卸载</string> <string name="kpm_uninstall">卸载</string>
<string name="kpm_uninstall_confirm">确定要卸载内核模块 %1$s 吗?</string>
<string name="kpm_uninstall_success">卸载成功</string> <string name="kpm_uninstall_success">卸载成功</string>
<string name="kpm_uninstall_failed">卸载失败</string> <string name="kpm_uninstall_failed">卸载失败</string>
<string name="kpm_install">安装kpm模块</string> <string name="kpm_install">选择安装</string>
<string name="kpm_install_confirm">确认安装吗?</string> <string name="kpm_install_success">加载 kpm 模块成功</string>
<string name="kpm_install_success">安装kpm模块成功</string> <string name="kpm_install_failed">加载 kpm 模块失败</string>
<string name="kpm_install_failed">安装kpm模块失败</string> <string name="home_kpm_version">KPM 版本</string>
</resources> <string name="kpm_control">执行</string>
<string name="close_notice">关闭</string>
<string name="kpm_control_success">成功</string>
<string name="kpm_control_failed">错误</string>
<string name="not_supported">不支持</string>
<string name="supported">支持</string>
<string name="home_kpm_module">KPM 模块数:%d </string>
<string name="kpm_invalid_file">KPM 文件无效</string>
<string name="kernel_patched">内核未进行补丁</string>
<string name="kernel_not_enabled">内核未配置</string>
<string name="kernel_module_notice">以下内核模块功能由 KernelPatch 开发,经过修改后加入 SukiSU Ultra 的内核模块功能</string>
<string name="home_ContributionCard_kernelsu">SukiSU Ultra 展望</string>
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra 未来将会成为一个相对独立的 KSU 分支,但是依然感谢官方 KernelSU 和 MKSU 等做出的贡献</string>
<string name="custom_settings">个性化设置</string>
<string name="kpm_install_mode">安装</string>
<string name="kpm_install_mode_load">加载</string>
<string name="kpm_install_mode_embed">嵌入</string>
<string name="kpm_install_mode_description">请选择模块安装模式:\n\n加载临时加载模块\n嵌入永久安装到系统</string>
<string name="snackbar_failed_to_check_module_file">无法检查模块文件是否存在</string>
<string name="confirm_uninstall_title">确认卸载</string>
<string name="confirm_uninstall_confirm">删除</string>
<string name="confirm_uninstall_dismiss">取消</string>
<string name="theme_color">主题颜色</string>
<string name="invalid_file_type">文件类型不正确,请选择 .kpm 文件</string>
<string name="confirm_uninstall_title_with_filename">卸载</string>
<string name="confirm_uninstall_content">将卸载以下 kpm 模块:\n%s</string>
</resources>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name" translatable="false">SukiSU</string> <string name="app_name" translatable="false">SukiSU Ultra</string>
<string name="home">Home</string> <string name="home">Home</string>
<string name="home_not_installed">Not installed</string> <string name="home_not_installed">Not installed</string>
<string name="home_click_to_install">Click to install</string> <string name="home_click_to_install">Click to install</string>
@@ -10,7 +10,7 @@
<string name="home_module_count">Modules: %d</string> <string name="home_module_count">Modules: %d</string>
<string name="home_unsupported">Unsupported</string> <string name="home_unsupported">Unsupported</string>
<string name="home_unsupported_reason">No KernelSU driver detected on your kernel, wrong kernel?.</string> <string name="home_unsupported_reason">No KernelSU driver detected on your kernel, wrong kernel?.</string>
<string name="home_kernel">Kernel</string> <string name="home_kernel">Kernel version</string>
<string name="home_susfs">SuSFS: %s</string> <string name="home_susfs">SuSFS: %s</string>
<string name="home_susfs_version">Version de SuSFS</string> <string name="home_susfs_version">Version de SuSFS</string>
<string name="home_susfs_sus_su">SuS SU</string> <string name="home_susfs_sus_su">SuS SU</string>
@@ -181,7 +181,7 @@
<string name="su_not_allowed">Granting superuser to %s is not allowed</string> <string name="su_not_allowed">Granting superuser to %s is not allowed</string>
<string name="settings_disable_su">Disable su compatibility</string> <string name="settings_disable_su">Disable su compatibility</string>
<string name="settings_disable_su_summary">Temporarily disable any applications from obtaining root privileges via the su command (existing root processes will not be affected).</string> <string name="settings_disable_su_summary">Temporarily disable any applications from obtaining root privileges via the su command (existing root processes will not be affected).</string>
<string name="using_mksu_manager">You are using the MKSU third-party manager</string> <string name="using_mksu_manager">You are using the SukiSU Beta manager</string>
<string name="module_install_multiple_confirm">Are you sure you want to install the selected %d modules?</string> <string name="module_install_multiple_confirm">Are you sure you want to install the selected %d modules?</string>
<string name="module_install_multiple_confirm_with_names">Sure you want to install the following %1$d modules? \n\n%2$s</string> <string name="module_install_multiple_confirm_with_names">Sure you want to install the following %1$d modules? \n\n%2$s</string>
<string name="more_settings">more settings</string> <string name="more_settings">more settings</string>
@@ -192,6 +192,10 @@
<string name="simple_mode_summary">Hides unnecessary cards when turned on</string> <string name="simple_mode_summary">Hides unnecessary cards when turned on</string>
<string name="hide_kernel_kernelsu_version">Hide kernel version</string> <string name="hide_kernel_kernelsu_version">Hide kernel version</string>
<string name="hide_kernel_kernelsu_version_summary">Hide kernel version</string> <string name="hide_kernel_kernelsu_version_summary">Hide kernel version</string>
<string name="hide_other_info">Hide other info</string>
<string name="hide_other_info_summary">Hides information about the number of super users, modules and KPM modules on the home page</string>
<string name="hide_susfs_status">Hide SuSFS status</string>
<string name="hide_susfs_status_summary">Hide SuSFS status information on the home page</string>
<string name="theme_mode">Theme Mode</string> <string name="theme_mode">Theme Mode</string>
<string name="theme_follow_system">follow-up system</string> <string name="theme_follow_system">follow-up system</string>
<string name="theme_light">light color</string> <string name="theme_light">light color</string>
@@ -210,7 +214,7 @@
<string name="color_ivory">ivory</string> <string name="color_ivory">ivory</string>
<string name="flash_option">Brush Options</string> <string name="flash_option">Brush Options</string>
<string name="flash_option_tip">Select the file to be flashed</string> <string name="flash_option_tip">Select the file to be flashed</string>
<string name="horizon_kernel">Anykernel3 Flush</string> <string name="horizon_kernel">Anykernel3 Flash</string>
<string name="root_required">Requires root privileges</string> <string name="root_required">Requires root privileges</string>
<string name="copy_failed">File Copy Failure</string> <string name="copy_failed">File Copy Failure</string>
<string name="reboot_complete_title">Scrubbing complete</string> <string name="reboot_complete_title">Scrubbing complete</string>
@@ -227,15 +231,39 @@
<string name="kpm_empty">No installed kernel modules at this time</string> <string name="kpm_empty">No installed kernel modules at this time</string>
<string name="kpm_version">releases</string> <string name="kpm_version">releases</string>
<string name="kpm_author">author</string> <string name="kpm_author">author</string>
<string name="kpm_execute">fulfillment</string>
<string name="kpm_uninstall">uninstallation</string> <string name="kpm_uninstall">uninstallation</string>
<string name="kpm_uninstall_confirm">Determine the kernel module to uninstall: %1$s </string>
<string name="kpm_uninstall_success">Uninstalled successfully</string> <string name="kpm_uninstall_success">Uninstalled successfully</string>
<string name="kpm_uninstall_failed">Failed to uninstall</string> <string name="kpm_uninstall_failed">Failed to uninstall</string>
<string name="kpm_install">Installing the kpm module</string> <string name="kpm_install">Select Installation</string>
<string name="kpm_install_confirm">Confirm installation?</string> <string name="kpm_install_success">Load of kpm module successful</string>
<string name="kpm_install_success">Installation of kpm module successful</string> <string name="kpm_install_failed">Load of kpm module failed</string>
<string name="kpm_install_failed">Installation of kpm module failed</string> <string name="kpm_args">kpm parameters</string>
<string name="kpm_args">kpm 参数</string> <string name="kpm_control">fulfillment</string>
<string name="kpm_control">kpm 控制</string> <string name="home_kpm_version">KPM Version</string>
<string name="close_notice">close</string>
<string name="kernel_module_notice">The following kernel module functions were developed by KernelPatch and modified to include the kernel module functions of SukiSU Ultra</string>
<string name="home_ContributionCard_kernelsu">SukiSU Ultra Look forward to</string>
<string name="kpm_control_success">success</string>
<string name="kpm_control_failed">failed</string>
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra will be a relatively independent branch of KSU in the future, but thanks to the official KernelSU and MKSU etc. for their contributions!</string>
<string name="not_supported">unsupported</string>
<string name="supported">supported</string>
<string name="home_kpm_module">Number of KPM modules%d </string>
<string name="kpm_invalid_file">Invalid KPM file</string>
<string name="kernel_patched">Kernel not patched</string>
<string name="kernel_not_enabled">Kernel not configured</string>
<string name="custom_settings">Custom settings</string>
<string name="kpm_install_mode">install</string>
<string name="kpm_install_mode_load">Load</string>
<string name="kpm_install_mode_embed">embed</string>
<string name="kpm_install_mode_description">Please select the module installation mode: \n\nLoad: Temporarily load the module \nEmbedded: Permanently install into the system</string>
<string name="log_failed_to_check_module_file">Failed to check module file existence</string>
<string name="snackbar_failed_to_check_module_file">Unable to check if module file exists</string>
<string name="confirm_uninstall_title">Confirm uninstallation</string>
<string name="confirm_uninstall_confirm">removing</string>
<string name="confirm_uninstall_dismiss">abolish</string>
<string name="theme_color">Theme Color</string>
<string name="invalid_file_type">Incorrect file type, select .kpm file</string>
<string name="confirm_uninstall_title_with_filename">uninstallation</string>
<string name="confirm_uninstall_content">The following kpm modules will be uninstalled\n%s</string>
</resources> </resources>

View File

@@ -13,15 +13,38 @@
#define CMD_KPM_CONTROL_MAX 7 #define CMD_KPM_CONTROL_MAX 7
// 控制代码 // 控制代码
#define SUKISU_KPM_LOAD 1 // prctl(xxx, 28, "PATH", "ARGS")
#define SUKISU_KPM_UNLOAD 2 // success return 0, error return -N
#define SUKISU_KPM_NUM 3 #define SUKISU_KPM_LOAD 28
#define SUKISU_KPM_LIST 4
#define SUKISU_KPM_INFO 5
#define SUKISU_KPM_CONTROL 6
#define SUKISU_KPM_PRINT 7
#define CONTROL_CODE(n) (CMD_KPM_CONTROL + n - 1) // prctl(xxx, 29, "NAME")
// success return 0, error return -N
#define SUKISU_KPM_UNLOAD 29
// num = prctl(xxx, 30)
// error return -N
// success return +num or 0
#define SUKISU_KPM_NUM 30
// prctl(xxx, 31, Buffer, BufferSize)
// success return +out, error return -N
#define SUKISU_KPM_LIST 31
// prctl(xxx, 32, "NAME", Buffer[256])
// success return +out, error return -N
#define SUKISU_KPM_INFO 32
// prctl(xxx, 33, "NAME", "ARGS")
// success return KPM's result value
// error return -N
#define SUKISU_KPM_CONTROL 33
// prctl(xxx, 34, buffer, bufferSize)
// success return KPM's result value
// error return -N
#define SUKISU_KPM_VERSION 34
#define CONTROL_CODE(n) (n)
void print_usage(const char *prog) { void print_usage(const char *prog) {
printf("Usage: %s <command> [args]\n", prog); printf("Usage: %s <command> [args]\n", prog);
@@ -32,7 +55,7 @@ void print_usage(const char *prog) {
printf(" list List loaded KPM modules\n"); printf(" list List loaded KPM modules\n");
printf(" info <name> Get info of a KPM module\n"); printf(" info <name> Get info of a KPM module\n");
printf(" control <name> <args> Send control command to a KPM module\n"); printf(" control <name> <args> Send control command to a KPM module\n");
printf(" print Print KPM module list to stdout\n"); printf(" version Print KPM Loader version\n");
} }
int main(int argc, char *argv[]) { int main(int argc, char *argv[]) {
@@ -47,6 +70,9 @@ int main(int argc, char *argv[]) {
if (strcmp(argv[1], "load") == 0 && argc >= 3) { if (strcmp(argv[1], "load") == 0 && argc >= 3) {
// 加载 KPM 模块 // 加载 KPM 模块
ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_LOAD), argv[2], (argc > 3 ? argv[3] : NULL), &out); ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_LOAD), argv[2], (argc > 3 ? argv[3] : NULL), &out);
if(out > 0) {
printf("Success");
}
} else if (strcmp(argv[1], "unload") == 0 && argc >= 3) { } else if (strcmp(argv[1], "unload") == 0 && argc >= 3) {
// 卸载 KPM 模块 // 卸载 KPM 模块
ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_UNLOAD), argv[2], NULL, &out); ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_UNLOAD), argv[2], NULL, &out);
@@ -59,22 +85,25 @@ int main(int argc, char *argv[]) {
// 获取模块列表 // 获取模块列表
char buffer[1024] = {0}; char buffer[1024] = {0};
ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_LIST), buffer, sizeof(buffer), &out); ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_LIST), buffer, sizeof(buffer), &out);
if (ret >= 0) { if (out >= 0) {
printf("%s", buffer); printf("%s", buffer);
} }
} else if (strcmp(argv[1], "info") == 0 && argc >= 3) { } else if (strcmp(argv[1], "info") == 0 && argc >= 3) {
// 获取指定模块信息 // 获取指定模块信息
char buffer[256] = {0}; char buffer[256] = {0};
ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_INFO), argv[2], buffer, &out); ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_INFO), argv[2], buffer, &out);
if (ret >= 0) { if (out >= 0) {
printf("%s\n", buffer); printf("%s\n", buffer);
} }
} else if (strcmp(argv[1], "control") == 0 && argc >= 4) { } else if (strcmp(argv[1], "control") == 0 && argc >= 4) {
// 控制 KPM 模块 // 控制 KPM 模块
ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_CONTROL), argv[2], argv[3], &out); ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_CONTROL), argv[2], argv[3], &out);
} else if (strcmp(argv[1], "print") == 0) { } else if (strcmp(argv[1], "version") == 0) {
// 在 stdout 输出 KPM 列表 char buffer[1024] = {0};
ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_PRINT), NULL, NULL, &out); ret = prctl(KSU_OPTIONS, CONTROL_CODE(SUKISU_KPM_VERSION), buffer, sizeof(buffer), &out);
if (out >= 0) {
printf("%s", buffer);
}
} else { } else {
print_usage(argv[0]); print_usage(argv[0]);
return 1; return 1;

View File

@@ -784,7 +784,7 @@ dependencies = [
] ]
[[package]] [[package]]
name = "zakomk" name = "zakozako"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"android-properties", "android-properties",

View File

@@ -1,11 +1,12 @@
[package] [package]
name = "zakomk" name = "zakozako"
version = "0.1.0" version = "0.1.0"
edition = "2024" edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
notify = "6.1"
anyhow = "1" anyhow = "1"
clap = { version = "4", features = ["derive"] } clap = { version = "4", features = ["derive"] }
const_format = "0.2" const_format = "0.2"

View File

@@ -1,5 +1,10 @@
____ _ _ _ ____ _____ _____ ____ _ _ ____ _ _
/ ___|| |__(_) |__ | ___|| ____|_ _| / ___| _ _| | _(_) ___|| | | |
\___ \| '_ \ | '_ \|___ \| _| | | \___ \| | | | |/ / \___ \| | | |
___) | | | | | | | ___) | |___ | | ___) | |_| | <| |___) | |_| |
|____/|_| |_|_| |_| |____/|_____| |_| |____/ \__,_|_|\_\_|____/ \___/
_ _ _ _
| | | | | |_ _ __ __ _
| | | | | __| '__/ _\ |
| |_| | | |_| | | (_| |
\___/|_|\__|_| \__,_|

View File

@@ -5,10 +5,13 @@ use anyhow::{Context, Result};
use log::{info, warn}; use log::{info, warn};
use rustix::fs::{MountFlags, mount}; use rustix::fs::{MountFlags, mount};
use std::path::Path; use std::path::Path;
use crate::kpm;
pub fn on_post_data_fs() -> Result<()> { pub fn on_post_data_fs() -> Result<()> {
ksucalls::report_post_fs_data(); ksucalls::report_post_fs_data();
kpm::start_kpm_watcher()?;
utils::umask(0); utils::umask(0);
#[cfg(unix)] #[cfg(unix)]
@@ -98,6 +101,9 @@ pub fn on_post_data_fs() -> Result<()> {
run_stage("post-mount", true); run_stage("post-mount", true);
// load kpm modules
kpm::load_kpm_modules()?;
Ok(()) Ok(())
} }

181
userspace/ksud/src/kpm.rs Normal file
View File

@@ -0,0 +1,181 @@
use anyhow::Result;
use notify::{Watcher, RecursiveMode};
use std::path::Path;
use std::fs;
use anyhow::anyhow;
use std::ffi::OsStr;
pub const KPM_DIR: &str = "/data/adb/kpm";
pub const KPMMGR_PATH: &str = "/data/adb/ksu/bin/kpmmgr";
// 确保 KPM 目录存在,如果不存在则创建
pub fn ensure_kpm_dir() -> Result<()> {
if !Path::new(KPM_DIR).exists() {
fs::create_dir_all(KPM_DIR)?;
}
Ok(())
}
pub fn start_kpm_watcher() -> Result<()> {
ensure_kpm_dir()?;
// 检查是否处于安全模式
if crate::utils::is_safe_mode() {
log::warn!("The system is in safe mode and is deleting all KPM modules...");
if let Err(e) = remove_all_kpms() {
log::error!("Error deleting all KPM modules: {}", e);
}
return Ok(());
}
let mut watcher = notify::recommended_watcher(|res| {
match res {
Ok(event) => handle_kpm_event(event),
Err(e) => log::error!("monitoring error: {:?}", e),
}
})?;
watcher.watch(Path::new(KPM_DIR), RecursiveMode::NonRecursive)?;
Ok(())
}
// 处理 KPM 事件
pub fn handle_kpm_event(event: notify::Event) {
match event.kind {
notify::EventKind::Create(_) => handle_create_event(event.paths),
notify::EventKind::Remove(_) => handle_remove_event(event.paths),
notify::EventKind::Modify(_) => handle_modify_event(event.paths),
_ => {}
}
}
fn handle_create_event(paths: Vec<std::path::PathBuf>) {
for path in paths {
if path.extension() == Some(OsStr::new("kpm")) {
if let Err(e) = load_kpm(&path) {
log::warn!("Failed to load {}: {}", path.display(), e);
}
}
}
}
fn handle_remove_event(paths: Vec<std::path::PathBuf>) {
for path in paths {
if let Some(name) = path.file_stem().and_then(|s| s.to_str()) {
if let Err(e) = unload_kpm(name) {
log::warn!("Failed to unload {}: {}", name, e);
}
if let Err(e) = fs::remove_file(&path) {
log::error!("Failed to delete file: {}: {}", path.display(), e);
}
}
}
}
fn handle_modify_event(paths: Vec<std::path::PathBuf>) {
for path in paths {
log::info!("Modified file: {}", path.display());
}
}
// 加载 KPM 模块
pub fn load_kpm(path: &Path) -> Result<()> {
let path_str = path.to_str().ok_or_else(|| anyhow!("Invalid path: {}", path.display()))?;
let status = std::process::Command::new(KPMMGR_PATH)
.args(["load", path_str, ""])
.status()?;
if status.success() {
log::info!("Loaded KPM: {}", path.display());
}
Ok(())
}
// 卸载 KPM 模块并尝试删除对应文件
pub fn unload_kpm(name: &str) -> Result<()> {
let status = std::process::Command::new(KPMMGR_PATH)
.args(["unload", name])
.status()
.map_err(|e| anyhow!("Failed to execute kpmmgr: {}", e))?;
if status.success() {
let kpm_path = find_kpm_file(name)?;
if let Some(path) = kpm_path {
fs::remove_file(&path)
.map_err(|e| anyhow!("Failed to delete KPM file: {}: {}", path.display(), e))?;
log::info!("Deleted KPM file: {}", path.display());
}
log::info!("Successfully unloaded KPM: {}", name);
} else {
log::warn!("KPM unloading may have failed: {}", name);
}
Ok(())
}
// 通过名称查找 KPM 文件
fn find_kpm_file(name: &str) -> Result<Option<std::path::PathBuf>> {
let kpm_dir = Path::new(KPM_DIR);
if !kpm_dir.exists() {
return Ok(None);
}
for entry in fs::read_dir(kpm_dir)? {
let path = entry?.path();
if let Some(file_name) = path.file_stem() {
if let Some(file_name_str) = file_name.to_str() {
if file_name_str == name && path.extension() == Some(OsStr::new("kpm")) {
return Ok(Some(path));
}
}
}
}
Ok(None)
}
// 安全模式下删除所有 KPM 模块
pub fn remove_all_kpms() -> Result<()> {
ensure_kpm_dir()?;
for entry in fs::read_dir(KPM_DIR)? {
let path = entry?.path();
if path.extension().is_some_and(|ext| ext == "kpm") {
if let Some(name) = path.file_stem() {
if let Err(e) = unload_kpm(name.to_string_lossy().as_ref()) {
log::error!("Failed to remove KPM: {}", e);
}
if let Err(e) = fs::remove_file(&path) {
log::error!("Failed to delete file: {}: {}", path.display(), e);
}
}
}
}
Ok(())
}
// 加载 KPM 模块
pub fn load_kpm_modules() -> Result<()> {
ensure_kpm_dir()?;
for entry in std::fs::read_dir(KPM_DIR)? {
let path = entry?.path();
if let Some(file_name) = path.file_stem() {
if let Some(file_name_str) = file_name.to_str() {
if file_name_str.is_empty() {
log::warn!("Invalid KPM file name: {}", path.display());
continue;
}
}
}
if path.extension().is_some_and(|ext| ext == "kpm") {
match load_kpm(&path) {
Ok(()) => log::info!("Successfully loaded KPM module: {}", path.display()),
Err(e) => log::warn!("Failed to load KPM module {}: {}", path.display(), e),
}
}
}
Ok(())
}

View File

@@ -14,6 +14,7 @@ mod restorecon;
mod sepolicy; mod sepolicy;
mod su; mod su;
mod utils; mod utils;
mod kpm;
fn main() -> anyhow::Result<()> { fn main() -> anyhow::Result<()> {
cli::run() cli::run()

View File

@@ -1,6 +1,6 @@
LOCAL_PATH := $(call my-dir) LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE := zakomksd LOCAL_MODULE := zakozakozako
LOCAL_SRC_FILES := zakomksd.c LOCAL_SRC_FILES := susfs.c
include $(BUILD_EXECUTABLE) include $(BUILD_EXECUTABLE)