Compare commits
107 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8b75349e0 | ||
|
|
3f3e520641 | ||
|
|
21aa58f1ee | ||
|
|
da164ebeed | ||
|
|
35ae324df5 | ||
|
|
fdda1f4ec0 | ||
|
|
770ed1fdf2 | ||
|
|
106c10d6f8 | ||
|
|
f10de68deb | ||
|
|
44db32e8de | ||
|
|
3ced30b427 | ||
|
|
624a8d9f86 | ||
|
|
16007f5892 | ||
|
|
6bb83fdb07 | ||
|
|
ec6991f98b | ||
|
|
f6337e2d52 | ||
|
|
6b2bf23946 | ||
|
|
dbc662486b | ||
|
|
e96194c7ff | ||
|
|
49b01aad74 | ||
|
|
656a23a250 | ||
|
|
11a628f536 | ||
|
|
57fcf86579 | ||
|
|
ecb2dae743 | ||
|
|
75e0cd05a9 | ||
|
|
1eb0f19ca6 | ||
|
|
a7ee0423a3 | ||
|
|
355e1c648a | ||
|
|
3cde3e1659 | ||
|
|
8dcc3f7c46 | ||
|
|
a041b90891 | ||
|
|
c1c648e34d | ||
|
|
0754fc8920 | ||
|
|
4d3bae1113 | ||
|
|
0fdd7d437f | ||
|
|
6e89c81407 | ||
|
|
f8b8c7f671 | ||
|
|
cd4edf97bd | ||
|
|
e3f1e49fe1 | ||
|
|
abe0dee4da | ||
|
|
bc3fcec514 | ||
|
|
d225f0bae9 | ||
|
|
48d7a13028 | ||
|
|
7e7d2a28af | ||
|
|
acfba3d0f8 | ||
|
|
bf5a8a8909 | ||
|
|
7c4d8da7d1 | ||
|
|
c656d87e42 | ||
|
|
1b7c7fd726 | ||
|
|
ea68183f80 | ||
|
|
b1ee07fee1 | ||
|
|
c42b4ffe4b | ||
|
|
cfd070f33c | ||
|
|
3ae1a3b10a | ||
|
|
d21f92d817 | ||
|
|
6bb66e2819 | ||
|
|
115206bcc6 | ||
|
|
5ba4f73eeb | ||
|
|
139899d05d | ||
|
|
d2ab325e18 | ||
|
|
be393ddb7c | ||
|
|
467d6e0838 | ||
|
|
46fcf97be3 | ||
|
|
72ed1bc4a2 | ||
|
|
b8544b4f53 | ||
|
|
60f0a721ce | ||
|
|
c62676d643 | ||
|
|
dd2b2e995a | ||
|
|
86456a4d95 | ||
|
|
11e9e37f43 | ||
|
|
a8a2aef4b5 | ||
|
|
77ac0d70fd | ||
|
|
2a00ef96c2 | ||
|
|
39ee1cc41d | ||
|
|
51556d1253 | ||
|
|
94df64f234 | ||
|
|
02f545b3fb | ||
|
|
62c7aac75b | ||
|
|
755d454960 | ||
|
|
9717fa0de6 | ||
|
|
bd6eb7fddd | ||
|
|
9f706873f2 | ||
|
|
c424d5bab4 | ||
|
|
e795387c30 | ||
|
|
044dd9471f | ||
|
|
213a15cdb6 | ||
|
|
a255ea9d56 | ||
|
|
6cbe13dafc | ||
|
|
fb5c7c2f9d | ||
|
|
b8f2d15bd1 | ||
|
|
2bd6929d24 | ||
|
|
42b883240e | ||
|
|
4840540038 | ||
|
|
7bfb37a11e | ||
|
|
dd6d695020 | ||
|
|
6a60b72e21 | ||
|
|
ea3a0cf73b | ||
|
|
1816d15ce8 | ||
|
|
79c8f7a709 | ||
|
|
b0cc0e6f6d | ||
|
|
eccc70c0c9 | ||
|
|
fc828ff3aa | ||
|
|
acb7cfff1b | ||
|
|
3729c22dd0 | ||
|
|
a84cf70730 | ||
|
|
2278fe49d2 | ||
|
|
be14da387e |
5
.github/ISSUE_TEMPLATE/config.yml
vendored
5
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,5 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: Feature Request
|
||||
url: https://github.com/tiann/KernelSU/issues/1705
|
||||
about: "We do not accept external Feature Requests, see this link for more details."
|
||||
39
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
39
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Feature Request
|
||||
description: "Suggest an idea for this project"
|
||||
title: "[Feature]"
|
||||
labels: "feature"
|
||||
body:
|
||||
- type: markdown
|
||||
id: feature-info
|
||||
attributes:
|
||||
value: "## Feature Infomation"
|
||||
- type: textarea
|
||||
id: feature-main
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "Is your feature request related to a problem? Please describe."
|
||||
description: "A clear and concise description of what the problem is."
|
||||
placeholder: "I'm always frustrated when [...]"
|
||||
- type: textarea
|
||||
id: feature-solution
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "Describe the solution you'd like."
|
||||
description: "A clear and concise description of what you want to happen."
|
||||
- type: textarea
|
||||
id: feature-describe
|
||||
validations:
|
||||
required: true
|
||||
attributes:
|
||||
label: "Describe alternatives you've considered."
|
||||
description: "A clear and concise description of any alternative solutions or features you've considered."
|
||||
- type: textarea
|
||||
id: feature-extra
|
||||
validations:
|
||||
required: false
|
||||
attributes:
|
||||
label: "Additional context"
|
||||
description: "Add any other context or screenshots about the feature request here."
|
||||
|
||||
@@ -1,166 +0,0 @@
|
||||
# SukiSU Ultra
|
||||
|
||||
**English** | [简体中文](README.md) | [日本語](README-ja.md) | [Türkçe](README-tr.md)
|
||||
|
||||
Android device root solution based on [KernelSU](https://github.com/tiann/KernelSU)
|
||||
|
||||
**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 are reserved to [@tiann](https://github.com/tiann)
|
||||
>
|
||||
> However, we will be a separately maintained branch of KSU in the future
|
||||
|
||||
## How to add
|
||||
|
||||
Using main branching (non-GKI device builds are not supported) (requires manual integration of susfs)
|
||||
|
||||
```
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
|
||||
Using branches that support non-GKI devices (requires manual integration of susfs)
|
||||
|
||||
```
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
|
||||
```
|
||||
|
||||
## How to use integrated susfs
|
||||
|
||||
> [!Note]
|
||||
>
|
||||
> - Due to SuSFS version changes and unpredictability issues
|
||||
> - This susfs-main branch will only merge the latest new version after a full update
|
||||
> - Please keep an eye on the susfs branch to avoid build failures and incompatibilities caused by the various versions
|
||||
|
||||
1. Use susfs-main or other susfs-\* branches directly, no need to integrate susfs again (supports non-GKI device builds)
|
||||
|
||||
```
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-main
|
||||
```
|
||||
|
||||
## Hook method
|
||||
|
||||
- This method references the hook [method by rsuntk](https://github.com/rsuntk/KernelSU)
|
||||
|
||||
1. **KPROBES hook:**
|
||||
|
||||
- Also used for Loadable Kernel Module (LKM)
|
||||
- Default hook method on GKI kernels.
|
||||
- Need `CONFIG_KPROBES=y`
|
||||
|
||||
2. **Manual hook:**
|
||||
- Standard KernelSU hook: https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
|
||||
|
||||
- backslashxx's syscall manual hook: https://github.com/backslashxx/KernelSU/issues/5 (v1.5 version is not available at the moment, if you want to use it, please use v1.4 version, or standard KernelSU hooks)
|
||||
|
||||
- Default hook method on Non-GKI kernels.
|
||||
- Need `CONFIG_KSU_MANUAL_HOOK=y`
|
||||
|
||||
## KPM Support
|
||||
|
||||
- Based on KernelPatch, we have removed duplicates of KSU and kept only KPM support.
|
||||
- We will introduce more APatch-compatible functions to ensure the integrity of KPM functionality.
|
||||
|
||||
Repository address: https://github.com/ShirkNeko/SukiSU_KernelPatch_patch
|
||||
|
||||
KPM templates: https://github.com/udochina/KPM-Build-Anywhere
|
||||
|
||||
> [!Note]
|
||||
>
|
||||
> 1. `CONFIG_KPM=y` needs to be added.
|
||||
> 2. Non-GKI devices need to add `CONFIG_KALLSYMS=y` and `CONFIG_KALLSYMS_ALL=y` as well.
|
||||
> 3. Some kernel source code below `4.19` also needs to be backport from `4.19` to the header file `set_memory.h`.
|
||||
|
||||
## How to do a system update to retain ROOT
|
||||
|
||||
- After OTA, don't reboot first, go to the manager flashing/patching kernel interface, find `GKI/non_GKI install` and select the Anykernel3 kernel zip file that needs to be flashed, select the slot that is opposite to the current running slot of the system for flashing, and then reboot to retain the GKI mode update (This method is not supported for all non-GKI devices, so please try it yourself. It is the safest way to use TWRP for non-GKI devices.)
|
||||
- Or use LKM mode to install to the unused slot (after OTA).
|
||||
|
||||
## Compatibility Status
|
||||
|
||||
- KernelSU (versions prior to v1.0.0) officially supports Android GKI 2.0 devices (kernel 5.10+)
|
||||
|
||||
- Older kernels (4.4+) are also compatible, but the kernel must be built manually
|
||||
|
||||
- KernelSU can support 3.x kernels (3.4-3.18) through additional reverse ports
|
||||
|
||||
- Currently supports `arm64-v8a`, `armeabi-v7a (bare)` and some `X86_64`
|
||||
|
||||
## More links
|
||||
|
||||
**If you need to submit a translation for the manager go to** https://crowdin.com/project/SukiSU-Ultra
|
||||
|
||||
Projects compiled based on Sukisu and susfs
|
||||
|
||||
- [More patched GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS) including ZRAM patches, KPM, susfs...
|
||||
- [Less patched GKI](https://github.com/MiRinFork/GKI_SukiSU_SUSFS/releases) only susfs
|
||||
- [OnePlus](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
|
||||
|
||||
## Usage
|
||||
|
||||
### Universal GKI
|
||||
|
||||
Please **all** refer to https://kernelsu.org/zh_CN/guide/installation.html
|
||||
|
||||
> [!Note]
|
||||
>
|
||||
> 1. for devices with GKI 2.0 such as Xiaomi, Redmi, Samsung, etc. (excludes kernel-modified manufacturers such as Meizu, OnePlus, Zenith, and oppo)
|
||||
> 2. Find the GKI build in [more links](#%E6%9B%B4%E5%A4%9A%E9%93%BE%E6%8E%A5). Find the device kernel version. Then download it and use TWRP or kernel flashing tool to flash the zip file with AnyKernel3 suffix. Pixel user need use _Less patched GKI_.
|
||||
> 3. The .zip archive without suffix is uncompressed, the gz suffix is the compression used by Tenguet models.
|
||||
|
||||
### OnePlus
|
||||
|
||||
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 parts of kernel versions, such as 5.10, 5.15, 6.1, or 6.6.
|
||||
> - 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
|
||||
|
||||
1. Kernel-based `su` and root access management.
|
||||
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. [App Profile](https://kernelsu.org/guide/app-profile.html): Lock root privileges in a cage.
|
||||
4. Bringing back non-GKI/GKI 1.0 support
|
||||
5. More customization
|
||||
6. Support for KPM kernel modules
|
||||
7. Introducing the Manager for SuSFS Configuration and Advanced Features
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
1. Uninstalling the KernelSU Manager device is stuck. → Uninstall the application with package name com.sony.playmemories.mobile.
|
||||
|
||||
## License
|
||||
|
||||
- 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.
|
||||
|
||||
- The images of the files `ic_launcher(?!.*alt.*).*` with anime character emoticons are copyrighted by [五十根大虾仁](https://space.bilibili.com/370927), the Brand Intellectual Property in the images is owned by [明风 OuO](https://space.bilibili.com/274939213), and the vectorization is done by @MiRinChan. Before using these files, in addition to complying with [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt), you also need to comply with the authorization of the two authors to use these artistic contents.
|
||||
|
||||
- Except for the files or directories mentioned above, all other parts are under [GPL-3.0 or later](https://www.gnu.org/licenses/gpl-3.0.html) license.
|
||||
|
||||
## Afdian link
|
||||
- https://afdian.com/a/shirkneko
|
||||
|
||||
## Sponsorship list
|
||||
|
||||
- [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
|
||||
- [wswzgdg](https://github.com/wswzgdg) Many thanks for supporting this project
|
||||
- [yspbwx2010](https://github.com/yspbwx2010) Many thanks
|
||||
- [DARKWWEE](https://github.com/DARKWWEE) Thanks for the 100 USDT Lao
|
||||
- [Saksham Singla](https://github.com/TypeFlu) Website provision as well as maintenance
|
||||
- [OukaroMF](https://github.com/OukaroMF) Donation of website domain name
|
||||
|
||||
## Contributions
|
||||
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): original project
|
||||
- [MKSU](https://github.com/5ec1cff/KernelSU): Used 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
|
||||
- [KernelSU](https://git.zx2c4.com/kernel-assisted-superuser/about/): KernelSU conceptualization
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): Powerful root utility
|
||||
- [genuine](https://github.com/brevent/genuine/): APK v2 Signature Verification
|
||||
- [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
|
||||
212
docs/README.md
212
docs/README.md
@@ -1,167 +1,101 @@
|
||||
<img align='right' src='zakomonochrome-128.svg' width='100px' alt="logo">
|
||||
|
||||
# SukiSU Ultra
|
||||
|
||||
**简体中文** | [English](README-en.md) | [日本語](README-ja.md) | [Türkçe](README-tr.md)
|
||||
**English** | [简体中文](./zh/README.md) | [日本語](./ja/README.md) | [Türkçe](./tr/README.md)
|
||||
|
||||
基于 [KernelSU](https://github.com/tiann/KernelSU) 的安卓设备 root 解决方案
|
||||
A kernel-based root solution for Android devices, forked from [`tiann/KernelSU`](https://github.com/tiann/KernelSU), and added some interesting changes.
|
||||
|
||||
**实验性! 使用风险自负!**
|
||||
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||
[](https://t.me/Sukiksu)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
> 这是非官方分支,[@tiann](https://github.com/tiann) 有权保留所有权利
|
||||
>
|
||||
> 但是,我们将会在未来成为一个单独维护的 KSU 分支
|
||||
## Features
|
||||
|
||||
## 如何添加
|
||||
1. Kernel-based `su` and root access management
|
||||
2. Module system based on [Magic Mount](https://github.com/5ec1cff/KernelSU)
|
||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html): Lock up the root power in a cage
|
||||
4. Support non-GKI and GKI 1.0
|
||||
5. KPM Support
|
||||
6. Tweaks to the manager theme and the built-in susfs management tool.
|
||||
|
||||
在内核源码的根目录下执行以下命令:
|
||||
## Compatibility Status
|
||||
|
||||
使用 main 分支 (不支持非 GKI 设备构建) (需要手动集成 susfs)
|
||||
- KernelSU (before v1.0.0) officially supports Android GKI 2.0 devices (kernel 5.10+).
|
||||
|
||||
```
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
- Older kernels (4.4+) are also compatible, but the kernel will have to be built manually.
|
||||
|
||||
使用支持非 GKI 设备的分支 (需要手动集成 susfs)
|
||||
- With more backports, KernelSU can supports 3.x kernel (3.4-3.18).
|
||||
|
||||
```
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
|
||||
```
|
||||
- Currently, only `arm64-v8a`, `armeabi-v7a (bare)` and `X86_64`(some) are supported.
|
||||
|
||||
## 如何集成 susfs
|
||||
## Installation
|
||||
|
||||
1. 直接使用 susfs-main 或者其他 susfs-\* 分支,不需要再集成 susfs (支持非 GKI 设备构建)
|
||||
See [`guide/installation.md`](guide/installation.md)
|
||||
|
||||
## Integration
|
||||
|
||||
See [`guide/how-to-integrate.md`](guide/how-to-integrate.md)
|
||||
|
||||
## Translation
|
||||
|
||||
If you need to submit a translation for the manager, please go to [Crowdin](https://crowdin.com/project/SukiSU-Ultra).
|
||||
|
||||
## KPM Support
|
||||
|
||||
- Based on KernelPatch, we removed features redundant with KSU and retained only KPM support.
|
||||
- Work in Progress: Expanding APatch compatibility by integrating additional functions to ensure compatibility across different implementations.
|
||||
|
||||
**Open-source repository**: [https://github.com/ShirkNeko/SukiSU_KernelPatch_patch](https://github.com/ShirkNeko/SukiSU_KernelPatch_patch)
|
||||
|
||||
**KPM template**: [https://github.com/udochina/KPM-Build-Anywhere](https://github.com/udochina/KPM-Build-Anywhere)
|
||||
|
||||
> [!Note]
|
||||
>
|
||||
> - 因 SuSFS 版本的变化和不可测问题
|
||||
> - 本 susfs-main 分支只在完整更新后再合并最新新版本
|
||||
> - 请随时留意 susfs 分支的变化情况以免导致构建失败以及各种版本导致的不兼容问题
|
||||
> 1. Requires `CONFIG_KPM=y`
|
||||
> 2. Non-GKI devices requires `CONFIG_KALLSYMS=y` and `CONFIG_KALLSYMS_ALL=y`
|
||||
> 3. For kernels below `4.19`, backporting from `set_memory.h` from `4.19` is required.
|
||||
|
||||
```
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-main
|
||||
```
|
||||
## Troubleshooting
|
||||
|
||||
## 钩子方法
|
||||
1. Device stuck upon manager app uninstallation?
|
||||
Uninstall _com.sony.playmemories.mobile_
|
||||
|
||||
- 此部分引用自 [rsuntk 的钩子方法](https://github.com/rsuntk/KernelSU)
|
||||
## Sponsor
|
||||
|
||||
1. **KPROBES 钩子:**
|
||||
- [ShirkNeko](https://afdian.com/a/shirkneko) (maintainer of SukiSU)
|
||||
- [weishu](https://github.com/sponsors/tiann) (author of KernelSU)
|
||||
|
||||
- 用于可加载内核模块 (LKM)
|
||||
- GKI 2.0 内核的默认钩子方法
|
||||
- 需要 `CONFIG_KPROBES=y`
|
||||
## ShirkNeko's sponsorship list
|
||||
|
||||
2. **手动钩子:**
|
||||
- 标准的 KernelSU 钩子:https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source
|
||||
- [Ktouls](https://github.com/Ktouls) Thanks so much for bringing me support.
|
||||
- [zaoqi123](https://github.com/zaoqi123) Thanks for the milk tea.
|
||||
- [wswzgdg](https://github.com/wswzgdg) Many thanks for supporting this project.
|
||||
- [yspbwx2010](https://github.com/yspbwx2010) Many thanks.
|
||||
- [DARKWWEE](https://github.com/DARKWWEE) 100 USDT
|
||||
- [Saksham Singla](https://github.com/TypeFlu) Provide and maintain the website
|
||||
- [OukaroMF](https://github.com/OukaroMF) Donation of website domain name
|
||||
|
||||
- backslashxx 的 syscall 手动钩子:https://github.com/backslashxx/KernelSU/issues/5 (v1.5 版本暂不可用,如要使用请使用 v1.4 版本,或者标准 KernelSU 钩子)
|
||||
## License
|
||||
|
||||
- 非 GKI 内核的默认挂钩方法
|
||||
- 需要 `CONFIG_KSU_MANUAL_HOOK=y`
|
||||
- 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.
|
||||
- The images of the files `ic_launcher(?!.*alt.*).*` with anime character sticker are copyrighted by [怡子曰曰](https://space.bilibili.com/10545509), the Brand Intellectual Property in the images is owned by [明风 OuO](https://space.bilibili.com/274939213), and the vectorization is done by @MiRinChan. Before using these files, in addition to complying with [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt), you also need to comply with the authorization of the two authors to use these artistic contents.
|
||||
- Except for the files or directories mentioned above, all other parts are under [GPL-3.0 or later](https://www.gnu.org/licenses/gpl-3.0.html) license.
|
||||
|
||||
## KPM 支持
|
||||
## Credit
|
||||
|
||||
- 我们基于 KernelPatch 去掉了和 KSU 重复的功能,仅保留了 KPM 支持
|
||||
- 我们将会引入更多的兼容 APatch 的函数来确保 KPM 功能的完整性
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): upstream
|
||||
- [MKSU](https://github.com/5ec1cff/KernelSU): Magic Mount
|
||||
- [RKSU](https://github.com/rsuntk/KernelsU): support non-GKI
|
||||
- [susfs](https://gitlab.com/simonpunk/susfs4ksu): An addon root hiding kernel patches and userspace module for KernelSU.
|
||||
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch is a key part of the APatch implementation of the kernel module
|
||||
|
||||
开源地址: https://github.com/ShirkNeko/SukiSU_KernelPatch_patch
|
||||
<details>
|
||||
<summary>KernelSU's credit</summary>
|
||||
|
||||
KPM 模板地址: https://github.com/udochina/KPM-Build-Anywhere
|
||||
|
||||
> [!Note]
|
||||
>
|
||||
> 1. 需要 `CONFIG_KPM=y`
|
||||
> 2. 非 GKI 设备还需要 `CONFIG_KALLSYMS=y` 和 `CONFIG_KALLSYMS_ALL=y`
|
||||
> 3. 部分内核 `4.19` 以下源码还需要从 `4.19` 向后移植头文件 `set_memory.h`
|
||||
|
||||
## 如何进行系统更新保留 ROOT
|
||||
|
||||
- OTA 后先不要重启,进入管理器刷写/修补内核界面,找到 `GKI/non_GKI安装` 选择需要刷写的 Anykernel3 内核压缩文件,选择与现在系统运行槽位相反的槽位进行刷写并重启即可保留 GKI 模式更新(暂不支持所有非 GKI 设备使用这种方法,请自行尝试。非 GKI 设备使用 TWRP 刷写是最稳妥的)
|
||||
- 或者使用 LKM 模式的安装到未使用的槽位(OTA 后)
|
||||
|
||||
## 兼容状态
|
||||
|
||||
- KernelSU(v1.0.0 之前版本)正式支持 Android GKI 2.0 设备(内核 5.10+)
|
||||
|
||||
- 旧内核(4.4+)也兼容,但必须手动构建内核
|
||||
|
||||
- 通过更多的反向移植,KernelSU 可以支持 3.x 内核(3.4-3.18)
|
||||
|
||||
- 目前支持 `arm64-v8a` ,`armeabi-v7a (bare)` 和部分 `X86_64`
|
||||
|
||||
## 更多链接
|
||||
|
||||
**如果你需要为管理器提交翻译请前往** https://crowdin.com/project/SukiSU-Ultra
|
||||
|
||||
基于 SukiSU 和 susfs 编译的项目
|
||||
|
||||
- [增强 GKI](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)(包括 ZRAM 算法等补丁、KPM、susfs 等)
|
||||
- [GKI](https://github.com/MiRinFork/GKI_SukiSU_SUSFS/releases)(若增强 GKI boot 失败再尝试这份,这份没有 KPM 等修改,只有 susfs)
|
||||
- [一加](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 普适的 GKI
|
||||
|
||||
请**全部**参考 https://kernelsu.org/zh_CN/guide/installation.html
|
||||
|
||||
> [!Note]
|
||||
>
|
||||
> 1. 适用于如小米、红米、三星等的 GKI 2.0 的设备 (不包含魔改内核的厂商如魅族、一加、真我和 oppo)
|
||||
> 2. 找到[更多链接](#%E6%9B%B4%E5%A4%9A%E9%93%BE%E6%8E%A5)里的 GKI 构建的项目。找到设备内核版本。然后下载下来,用 TWRP 或者内核刷写工具刷入带 AnyKernel3 后缀的压缩包即可。Pixel 请使用不是增强的 GKI。
|
||||
> 3. 一般不带后缀的 .zip 压缩包是未压缩的,gz 后缀的为天玑机型所使用的压缩方式
|
||||
|
||||
### 一加
|
||||
|
||||
1.找到更多链接里的一加项目进行自行填写,然后云编译构建,最后刷入带 AnyKernel3 后缀的压缩包即可
|
||||
|
||||
> [!Note]
|
||||
>
|
||||
> - 内核版本只需要填写前两位即可,如 5.10,5.15,6.1,6.6
|
||||
> - 处理器代号请自行搜索,一般为全英文不带数字的代号
|
||||
> - 分支和配置文件请自行到一加内核开源地址进行填写
|
||||
|
||||
## 特点
|
||||
|
||||
1. 基于内核的 `su` 和 root 访问管理
|
||||
2. 基于 5ec1cff 的 [Magic Mount](https://github.com/5ec1cff/KernelSU) 的模块系统
|
||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html):将 root 权限锁在笼子里
|
||||
4. 恢复对非 GKI 2.0 内核的支持
|
||||
5. 更多自定义功能
|
||||
6. 对 KPM 内核模块的支持
|
||||
7. 引入SuSFS配置的管理器以及进阶功能
|
||||
|
||||
## 疑难解答
|
||||
|
||||
1. 卸载 KernelSU 管理器设备卡死。→ 卸载包名为 com.sony.playmemories.mobile 的应用。
|
||||
|
||||
## 许可证
|
||||
|
||||
- `kernel` 目录下的文件是 [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)。
|
||||
- 有动漫人物图片表情包的这些文件 `ic_launcher(?!.*alt.*).*` 的图像版权为[五十根大虾仁](https://space.bilibili.com/370927)所有,图像中的 Brand Intellectual Property 由[明风 OuO](https://space.bilibili.com/274939213)所有,矢量化由 @MiRinChan 完成,在使用这些文件之前,除了必须遵守 [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt) 以外,还需要遵守向前两者索要使用这些艺术内容的授权。
|
||||
- 除了以上所述的文件或目录外,所有其他部分均为 [GPL-3.0 或更高版本](https://www.gnu.org/licenses/gpl-3.0.html)。
|
||||
|
||||
## 爱发电链接
|
||||
|
||||
- https://afdian.com/a/shirkneko
|
||||
|
||||
## 赞助名单
|
||||
|
||||
- [Ktouls](https://github.com/Ktouls) 非常感谢你给我带来的支持
|
||||
- [zaoqi123](https://github.com/zaoqi123) 请我喝奶茶也不错
|
||||
- [wswzgdg](https://github.com/wswzgdg) 非常感谢对此项目的支持
|
||||
- [yspbwx2010](https://github.com/yspbwx2010) 非常感谢
|
||||
- [DARKWWEE](https://github.com/DARKWWEE) 感谢老哥的 100 USDT
|
||||
- [Saksham Singla](https://github.com/TypeFlu) 网站的提供以及维护
|
||||
- [OukaroMF](https://github.com/OukaroMF) 网站域名捐赠
|
||||
|
||||
## 贡献
|
||||
|
||||
- [KernelSU](https://github.com/tiann/KernelSU):原始项目
|
||||
- [MKSU](https://github.com/5ec1cff/KernelSU):使用的项目
|
||||
- [RKSU](https://github.com/rsuntk/KernelsU):使用该项目的 kernel 对非 GKI 设备重新进行支持
|
||||
- [susfs4ksu](https://gitlab.com/simonpunk/susfs4ksu):使用的 susfs 文件系统
|
||||
- [kernel-assisted-superuser](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 实现内核模块的关键部分
|
||||
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): The KernelSU idea.
|
||||
- [Magisk](https://github.com/topjohnwu/Magisk): The powerful root tool.
|
||||
- [genuine](https://github.com/brevent/genuine/): APK v2 signature validation.
|
||||
- [Diamorphine](https://github.com/m0nad/Diamorphine): Some rootkit skills.
|
||||
</details>
|
||||
|
||||
97
docs/guide/how-to-integrate.md
Normal file
97
docs/guide/how-to-integrate.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# Integrate
|
||||
|
||||
SukiSU can be integrated into both _GKI_ and _non-GKI_ kernels and has been backported to _4.14_.
|
||||
|
||||
<!-- It should be 3.4, but backslashxx's syscall manual hook cannot use in SukiSU-->
|
||||
|
||||
Some OEMs' customization could result in as much as 50% of kernel code being out-of-tree code and not from upstream Linux kernels or ACKs. Due to this, the custom nature of _non-GKI_ kernels resulted in significant kernel fragmentation, and we lacked a universal method for building them. Therefore, we cannot provide boot images of _non-GKI_ kernels.
|
||||
|
||||
Prerequisites: open source bootable kernel.
|
||||
|
||||
### Hook method
|
||||
|
||||
1. **KPROBES hook:**
|
||||
|
||||
- Default hook method on GKI kernels.
|
||||
- Requires `# CONFIG_KSU_MANUAL_HOOK is not set` & `CONFIG_KPROBES=y`
|
||||
- Used for Loadable Kernel Module (LKM).
|
||||
|
||||
2. **Manual hook:**
|
||||
|
||||
<!-- - backslashxx's syscall manual hook: https://github.com/backslashxx/KernelSU/issues/5 (v1.5 version is not available at the moment, if you want to use it, please use v1.4 version, or standard KernelSU hooks)-->
|
||||
|
||||
- Requires `CONFIG_KSU_MANUAL_HOOK=y`
|
||||
- Requires [`guide/how-to-integrate.md`](guide/how-to-integrate.md)
|
||||
- Requires [https://github.com/~](https://github.com/tiann/KernelSU/blob/main/website/docs/guide/how-to-integrate-for-non-gki.md#manually-modify-the-kernel-source)
|
||||
|
||||
3. **Tracepoint Hook:**
|
||||
|
||||
- Hook method introduced since SukiSU commit [49b01aad](https://github.com/SukiSU-Ultra/SukiSU-Ultra/commit/49b01aad74bcca6dba5a8a2e053bb54b648eb124)
|
||||
- Requires `CONFIG_KSU_TRACEPOINT_HOOK=y`
|
||||
- Requires [`guide/tracepoint-hook.md`](tracepoint-hook.md)
|
||||
|
||||
<!-- This part refer to [rsuntk/KernelSU](https://github.com/rsuntk/KernelSU). -->
|
||||
|
||||
If you're able to build a bootable kernel, there are two ways to integrate KernelSU into the kernel source code:
|
||||
|
||||
1. Automatically with `kprobe`
|
||||
2. Manually
|
||||
|
||||
## Integrate with kprobe
|
||||
|
||||
Applicable:
|
||||
|
||||
- _GKI_ kernel
|
||||
|
||||
Not applicable:
|
||||
|
||||
- _non-GKI_ kernel
|
||||
|
||||
KernelSU uses kprobe to do kernel hooks. If kprobe runs well in your kernel, it's recommended to use it this way.
|
||||
|
||||
Please refer to this document [https://github.com/~](https://github.com/tiann/KernelSU/blob/main/website/docs/guide/how-to-integrate-for-non-gki.md#integrate-with-kprobe). Although it is titled “for _non-GKI_,” it only applies to _GKI_.
|
||||
|
||||
The execution command for the step that adds KernelSU to your kernel source tree is replaced with:
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
|
||||
## Manually modify the kernel source
|
||||
|
||||
Applicable:
|
||||
|
||||
- GKI kernel
|
||||
- non-GKI kernel
|
||||
|
||||
Please refer to this document [https://github.com/~ (Integrate for non-GKI)](https://github.com/tiann/KernelSU/blob/main/website/docs/guide/how-to-integrate-for-non-gki.md#manually-modify-the-kernel-source) and [https://github.com/~ (Build for GKI)](https://kernelsu.org/zh_CN/guide/how-to-build.html) to integrate manually, although first link is titled “for non-GKI,” it also applies to GKI. It can work on them both.
|
||||
|
||||
There is another way to integrate but still work in the process.
|
||||
|
||||
<!-- It is backslashxx's syscall manual hook, but it cannot be used now. -->
|
||||
|
||||
Run command for the step that adds KernelSU(SukiSU) to your kernel source tree is replaced with:
|
||||
|
||||
### GKI kernel
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
|
||||
### non-GKI kernel
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
|
||||
```
|
||||
|
||||
### GKI / non-GKI kernel with susfs (experiment)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-{{branch}}
|
||||
```
|
||||
|
||||
Branch:
|
||||
|
||||
- `main` (susfs-main)
|
||||
- `test` (susfs-test)
|
||||
- version (for example: susfs-1.5.7, you should check the [branches](https://github.com/SukiSU-Ultra/SukiSU-Ultra/branches))
|
||||
34
docs/guide/installation.md
Normal file
34
docs/guide/installation.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# Installation
|
||||
|
||||
You can go to [KernelSU Documentation - Installation](https://kernelsu.org/guide/installation.html) for a reference on how to install it, here are just additional instructions.
|
||||
|
||||
## Installation by loading the Loadable Kernel Module(LKM)
|
||||
|
||||
See [KernelSU Documentation - LKM Installation](https://kernelsu.org/guide/installation.html#lkm-installation)
|
||||
|
||||
Beginning with **Android™** (trademark meaning licensed Google Mobile Services) 12, devices shipping with kernel version 5.10 or higher must ship with the GKI kernel. You may be able to use LKM mode.
|
||||
|
||||
## Installation by installing the kernel
|
||||
|
||||
See [KernelSU Documentation - GKI mode Installation](https://kernelsu.org/guide/installation.html#gki-mode-installation)
|
||||
|
||||
We provide pre-built kernels for you to use:
|
||||
|
||||
- [ShirkNeko flavor kernel](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS) (add ZRAM compression algorithm patch, susfs, KPM. Works on many devices.)
|
||||
- [MiRinFork flavored kernel](https://github.com/MiRinFork/GKI_SukiSU_SUSFS) (adds susfs, KPM. Closest kernel to GKI, works on most devices.)
|
||||
|
||||
Although some devices can be installed using LKM mode, they cannot be installed on the device by using the GKI kernel; therefore, the kernel needs to be modified manually to compile it. For example:
|
||||
|
||||
- OPPO(OnePlus, REALME)
|
||||
- Meizu
|
||||
|
||||
Also, we provide pre-built kernels for your OnePlus device to use:
|
||||
|
||||
- [ShirkNeko/Action_OnePlus_MKSU_SUSFS](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS) (add ZRAM compression algorithm patch, susfs, KPM.)
|
||||
|
||||
Using the link above, Fork into GitHub Action, fill in the build parameters, compile, and finally flush in the zip with the AnyKernel3 suffix.
|
||||
|
||||
> [!Note]
|
||||
>
|
||||
> - You only need to fill in the first two parts of the version number, e.g. `5.10`, `6.1`...
|
||||
> - Make sure you know the processor designation, kernel version, etc. before you use it.
|
||||
270
docs/guide/tracepoint-hook.md
Normal file
270
docs/guide/tracepoint-hook.md
Normal file
@@ -0,0 +1,270 @@
|
||||
# Tracepoint Hook Integration
|
||||
|
||||
## Introduction
|
||||
|
||||
Since commit [49b01aad](https://github.com/SukiSU-Ultra/SukiSU-Ultra/commit/49b01aad74bcca6dba5a8a2e053bb54b648eb124), SukiSU has introduced Tracepoint Hook
|
||||
|
||||
This Hook theoretically has lower performance overhead compared to Kprobes Hook, but is inferior to Manual Hook / Syscall Hook
|
||||
|
||||
> [!NOTE]
|
||||
> This tutorial references the syscall hook v1.4 version from [backslashxx/KernelSU#5](https://github.com/backslashxx/KernelSU/issues/5), as well as the original KernelSU's [Manual Hook](https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source)
|
||||
|
||||
## Guide
|
||||
|
||||
### execve Hook (`exec.c`)
|
||||
|
||||
Generally need to modify the `do_execve` and `compat_do_execve` methods in `fs/exec.c`
|
||||
|
||||
```patch
|
||||
--- a/fs/exec.c
|
||||
+++ b/fs/exec.c
|
||||
@@ -78,6 +78,10 @@
|
||||
#include <trace/hooks/sched.h>
|
||||
#endif
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(task_rename);
|
||||
|
||||
static int bprm_creds_from_file(struct linux_binprm *bprm);
|
||||
@@ -2037,6 +2041,9 @@ static int do_execve(struct filename *filename,
|
||||
{
|
||||
struct user_arg_ptr argv = { .ptr.native = __argv };
|
||||
struct user_arg_ptr envp = { .ptr.native = __envp };
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_execveat_hook((int *)AT_FDCWD, &filename, &argv, &envp, 0);
|
||||
+#endif
|
||||
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
|
||||
}
|
||||
|
||||
@@ -2064,6 +2071,9 @@ static int compat_do_execve(struct filename *filename,
|
||||
.is_compat = true,
|
||||
.ptr.compat = __envp,
|
||||
};
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_execveat_sucompat_hook((int *)AT_FDCWD, &filename, NULL, NULL, NULL); /* 32-bit su */
|
||||
+#endif
|
||||
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
|
||||
}
|
||||
```
|
||||
|
||||
### faccessat Hook (`open.c`)
|
||||
|
||||
Generally need to modify the `do_faccessat` method in `/fs/open.c`
|
||||
|
||||
```patch
|
||||
--- a/fs/open.c
|
||||
+++ b/fs/open.c
|
||||
@@ -37,6 +37,10 @@
|
||||
#include "internal.h"
|
||||
#include <trace/hooks/syscall_check.h>
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
int do_truncate(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||
loff_t length, unsigned int time_attrs, struct file *filp)
|
||||
{
|
||||
@@ -468,6 +472,9 @@ static long do_faccessat(int dfd, const char __user *filename, int mode, int fla
|
||||
|
||||
SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
|
||||
{
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_faccessat_hook(&dfd, &filename, &mode, NULL);
|
||||
+#endif
|
||||
return do_faccessat(dfd, filename, mode, 0);
|
||||
}
|
||||
```
|
||||
|
||||
If there's no `do_faccessat` method, you can find the `faccessat` SYSCALL definition (for kernels earlier than 4.17)
|
||||
|
||||
```patch
|
||||
--- a/fs/open.c
|
||||
+++ b/fs/open.c
|
||||
@@ -31,6 +31,9 @@
|
||||
#include <linux/ima.h>
|
||||
#include <linux/dnotify.h>
|
||||
#include <linux/compat.h>
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
@@ -369,6 +372,9 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
|
||||
int res;
|
||||
unsigned int lookup_flags = LOOKUP_FOLLOW;
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_faccessat_hook(&dfd, &filename, &mode, NULL);
|
||||
+#endif
|
||||
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
|
||||
return -EINVAL;
|
||||
```
|
||||
|
||||
### sys_read Hook (`read_write.c`)
|
||||
|
||||
Need to modify the `sys_read` method in `fs/read_write.c` (4.19 and above)
|
||||
|
||||
```patch
|
||||
--- a/fs/read_write.c
|
||||
+++ b/fs/read_write.c
|
||||
@@ -25,6 +25,10 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
const struct file_operations generic_ro_fops = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read_iter = generic_file_read_iter,
|
||||
@@ -630,6 +634,9 @@ ssize_t ksys_read(unsigned int fd, char __user *buf, size_t count)
|
||||
|
||||
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
|
||||
{
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_sys_read_hook(fd, &buf, &count);
|
||||
+#endif
|
||||
return ksys_read(fd, buf, count);
|
||||
}
|
||||
```
|
||||
|
||||
Or the `read` SYSCALL definition (4.14 and below)
|
||||
|
||||
```patch
|
||||
--- a/fs/read_write.c
|
||||
+++ b/fs/read_write.c
|
||||
@@ -25,6 +25,11 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
+
|
||||
const struct file_operations generic_ro_fops = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read_iter = generic_file_read_iter,
|
||||
@@ -575,6 +580,9 @@ SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
|
||||
|
||||
if (f.file) {
|
||||
loff_t pos = file_pos_read(f.file);
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_sys_read_hook(fd, &buf, &count);
|
||||
+#endif
|
||||
ret = vfs_read(f.file, buf, count, &pos);
|
||||
if (ret >= 0)
|
||||
file_pos_write(f.file, pos);
|
||||
```
|
||||
|
||||
### fstatat Hook (`stat.c`)
|
||||
|
||||
Need to modify the `newfstatat` SYSCALL definition in `stat.c`
|
||||
|
||||
If 32-bit support is needed, also need to modify the `statat64` SYSCALL definition
|
||||
|
||||
```patch
|
||||
--- a/fs/stat.c
|
||||
+++ b/fs/stat.c
|
||||
@@ -24,6 +24,10 @@
|
||||
#include "internal.h"
|
||||
#include "mount.h"
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
/**
|
||||
* generic_fillattr - Fill in the basic attributes from the inode struct
|
||||
* @mnt_userns: user namespace of the mount the inode was found from
|
||||
@@ -408,6 +412,10 @@ SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename,
|
||||
struct kstat stat;
|
||||
int error;
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_stat_hook(&dfd, &filename, &flag);
|
||||
+#endif
|
||||
+
|
||||
error = vfs_fstatat(dfd, filename, &stat, flag);
|
||||
if (error)
|
||||
return error;
|
||||
@@ -559,6 +567,10 @@ SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,
|
||||
struct kstat stat;
|
||||
int error;
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_stat_hook(&dfd, &filename, &flag); /* 32-bit su support */
|
||||
+#endif
|
||||
+
|
||||
error = vfs_fstatat(dfd, filename, &stat, flag);
|
||||
if (error)
|
||||
return error;
|
||||
```
|
||||
|
||||
### input Hook (`input.c`, for entering KSU built-in security mode)
|
||||
|
||||
Need to modify the `input_event` method in `drivers/input/input.c`, not `input_handle_event`
|
||||
|
||||
```patch
|
||||
--- a/drivers/input/input.c
|
||||
+++ b/drivers/input/input.c
|
||||
@@ -26,6 +26,10 @@
|
||||
#include "input-compat.h"
|
||||
#include "input-poller.h"
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
|
||||
MODULE_DESCRIPTION("Input core");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -451,6 +455,10 @@ void input_event(struct input_dev *dev,
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_input_hook(&type, &code, &value);
|
||||
+#endif
|
||||
+
|
||||
if (is_event_supported(type, dev->evbit, EV_MAX)) {
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
```
|
||||
|
||||
### devpts Hook (`pty.c`)
|
||||
|
||||
Need to modify the `pts_unix98_lookup` method in `drivers/tty/pty.c`
|
||||
|
||||
```patch
|
||||
--- a/drivers/tty/pty.c
|
||||
+++ b/drivers/tty/pty.c
|
||||
@@ -31,6 +31,10 @@
|
||||
#include <linux/compat.h>
|
||||
#include "tty.h"
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
#undef TTY_DEBUG_HANGUP
|
||||
#ifdef TTY_DEBUG_HANGUP
|
||||
# define tty_debug_hangup(tty, f, args...) tty_debug(tty, f, ##args)
|
||||
@@ -707,6 +711,10 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
|
||||
{
|
||||
struct tty_struct *tty;
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_devpts_hook((struct inode *)file->f_path.dentry->d_inode);
|
||||
+#endif
|
||||
+
|
||||
mutex_lock(&devpts_mutex);
|
||||
tty = devpts_get_priv(file->f_path.dentry);
|
||||
mutex_unlock(&devpts_mutex);
|
||||
```
|
||||
@@ -1,6 +1,6 @@
|
||||
# SukiSU Ultra
|
||||
|
||||
**日本語** | [简体中文](README.md) | [English](README-en.md) | [Türkçe](README-tr.md)
|
||||
[English](../README.md) | [简体中文](../zh/README.md) | **日本語** | [Türkçe](../tr/README.md)
|
||||
|
||||
[KernelSU](https://github.com/tiann/KernelSU) をベースとした Android デバイスの root ソリューション
|
||||
|
||||
@@ -14,11 +14,13 @@
|
||||
## 追加する方法
|
||||
|
||||
メインブランチを使用 (非 GKI のデバイスのビルドは非対応) (susfs を手動で統合が必要)
|
||||
|
||||
```
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
|
||||
非 GKI のデバイスに対応するブランチを使用 (susfs を手動で統合が必要)
|
||||
|
||||
```
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
|
||||
```
|
||||
@@ -36,6 +38,7 @@ curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kern
|
||||
- この方式は (https://github.com/rsuntk/KernelSU) のフック方式を参照してください。
|
||||
|
||||
1. **KPROBES でフック:**
|
||||
|
||||
- 読み込み可能なカーネルモジュールの場合 (LKM)
|
||||
- GKI カーネルのデフォルトとなるフック方式
|
||||
- `CONFIG_KPROBES=y` が必要です
|
||||
@@ -56,11 +59,11 @@ curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kern
|
||||
KPM テンプレートのアドレス: https://github.com/udochina/KPM-Build-Anywhere
|
||||
|
||||
> [!Note]
|
||||
>
|
||||
> 1. `CONFIG_KPM=y` が必要です。
|
||||
> 2. 非 GKI デバイスには `CONFIG_KALLSYMS=y` と `CONFIG_KALLSYMS_ALL=y` も必要です。
|
||||
> 3. いくつかのカーネル `4.19` およびそれ以降のソースコードでは、 `4.19` からバックポートされた `set_memory.h` ヘッダーファイルも必要です。
|
||||
|
||||
|
||||
## ROOT を保持した状態でのシステムアップデートの方法
|
||||
|
||||
- 始めに OTA 後すぐに再起動せずにマネージャーのカーネルのフラッシュ、パッチのインターフェースを開いて`GKI/非 GKI のインストール`を見つけます。フラッシュする AnyKernel3 の zip ファイルを選択し、フラッシュする実行中のスロットと逆のスロットを選択後に再起動をして GKI モードの更新が保持できます (この方法はすべての非 GKI のデバイスが対応している訳ではないので、自分でお試しください。これは非 GKI のデバイスで TWRP を使用する最も安全な方法です)。
|
||||
@@ -101,6 +104,7 @@ KPM テンプレートのアドレス: https://github.com/udochina/KPM-Build-Any
|
||||
1. `その他のリンク`の項目に記載されているリンクを開き、デバイス情報を使用してカスタマイズされたカーネルをビルドし、AnyKernel3 の接頭辞を持つ .zip ファイルをフラッシュします。
|
||||
|
||||
> [!Note]
|
||||
>
|
||||
> - 5.10、5.15、6.1、6.6 などのカーネルバージョンの最初の 2 文字のみを入力する必要があります。
|
||||
> - SoC のコードネームは自分で検索してください。通常は、数字がなく英語表記のみです。
|
||||
> - ブランチと構成ファイルは、OnePlus オープンソースカーネルリポジトリから見つけることができます。
|
||||
@@ -121,8 +125,8 @@ KPM テンプレートのアドレス: https://github.com/udochina/KPM-Build-Any
|
||||
## ライセンス
|
||||
|
||||
- 「kernel」のディレクトリ内のファイルは [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) のライセンスに基づいています。
|
||||
|
||||
- アニメキャラクターの絵文字を含む `ic_launcher(?!.*alt.*).*` の画像は、[五十根大虾仁](https://space.bilibili.com/370927)が著作権を所有しています。画像に含まれるブランドの知的財産権は[明风 OuO](https://space.bilibili.com/274939213)が所有しています。ベクトル化は @MiRinChan が行っています。これらのファイルを使用する前に[クリエイティブコモンズ 表示 - 非営利 - 継承 4.0 国際](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt)に準拠することに加え、これらの芸術的コンテンツを使用するためには 2 名の著者の許可に従う必要があります。
|
||||
- アニメキャラクター画像とスタンプを含むこれらのファイルの `ic_launcher(?!.*alt.*).*` は[怡子曰曰](https://space.bilibili.com/10545509)によって著作権保護されており、画像の Brand Intellectual Property は[明风 OuO](https://space.bilibili.com/274939213)によって所有され、ベクター化は @MiRinChan によって行われています。 これらのファイルを使用する前に、[Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt)を遵守することに加えて、アートコンテンツを使用するために前の 2 人の作者から許可を得る必要があります。
|
||||
- 上記のファイルまたはディレクトリを除き、その他のすべての部分は[GPL-3.0 以降](https://www.gnu.org/licenses/gpl-3.0.html)です。
|
||||
|
||||
## スポンサーシップの一覧
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# SukiSU Ultra
|
||||
|
||||
**Türkçe** | [简体中文](README.md) | [English](README-en.md) | [日本語](README-ja.md)
|
||||
[English](../README.md) | [简体中文](../zh/README.md) | [日本語](../ja/README.md) | **Türkçe**
|
||||
|
||||
[KernelSU](https://github.com/tiann/KernelSU) tabanlı Android cihaz root çözümü
|
||||
|
||||
@@ -28,7 +28,7 @@ curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kern
|
||||
|
||||
## susfs Nasıl Entegre Edilir
|
||||
|
||||
1. Doğrudan susfs-main veya susfs-* dalını kullanın, susfs entegrasyonuna gerek yok
|
||||
1. Doğrudan susfs-main veya susfs-\* dalını kullanın, susfs entegrasyonuna gerek yok
|
||||
|
||||
```
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-main
|
||||
@@ -121,7 +121,7 @@ Lütfen **tümünü** https://kernelsu.org/zh_CN/guide/installation.html adresin
|
||||
## Lisans
|
||||
|
||||
- `kernel` dizinindeki dosyalar [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) lisansı altındadır.
|
||||
- Anime karakter ifadeleri içeren `ic_launcher(?!.*alt.*).*` dosyalarının görüntüleri [五十根大虾仁](https://space.bilibili.com/370927) tarafından telif hakkıyla korunmaktadır, görüntülerdeki Marka Fikri Mülkiyeti [明风 OuO](https://space.bilibili.com/274939213)'ye aittir ve vektörleştirme @MiRinChan tarafından yapılmıştır. Bu dosyaları kullanmadan önce, [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt) ile uyumlu olmanın yanı sıra, bu sanatsal içerikleri kullanmak için iki yazarın yetkilendirmesine de uymanız gerekir.
|
||||
- Anime karakter ifadeleri içeren `ic_launcher(?!.*alt.*).*` dosyalarının görüntüleri [怡子曰曰](https://space.bilibili.com/10545509) tarafından telif hakkıyla korunmaktadır, görüntülerdeki Marka Fikri Mülkiyeti [明风 OuO](https://space.bilibili.com/274939213)'ye aittir ve vektörleştirme @MiRinChan tarafından yapılmıştır. Bu dosyaları kullanmadan önce, [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt) ile uyumlu olmanın yanı sıra, bu sanatsal içerikleri kullanmak için iki yazarın yetkilendirmesine de uymanız gerekir.
|
||||
- Yukarıda belirtilen dosyalar veya dizinler hariç, diğer tüm parçalar [GPL-3.0 veya üzeri](https://www.gnu.org/licenses/gpl-3.0.html)'dir.
|
||||
|
||||
## Afdian Bağlantısı
|
||||
65
docs/zakomonochrome-128.svg
Normal file
65
docs/zakomonochrome-128.svg
Normal file
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="128"
|
||||
height="128"
|
||||
viewBox="0 0 128 128"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
sodipodi:docname="zakomonochrome-128.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#999999"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="px"
|
||||
inkscape:zoom="2.6185048"
|
||||
inkscape:cx="59.957881"
|
||||
inkscape:cy="71.032903"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:window-height="696"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs1" />
|
||||
<g
|
||||
inkscape:label="图层 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;paint-order:fill markers stroke;fill-opacity:1"
|
||||
id="rect1"
|
||||
width="128"
|
||||
height="128"
|
||||
x="0"
|
||||
y="0"
|
||||
rx="7.772471"
|
||||
ry="7.772471" />
|
||||
<path
|
||||
id="path101"
|
||||
style="fill:#ffffff;fill-opacity:0.734285;stroke:#000000;stroke-width:4.27504;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 42.510282,81.796052 c 0,0 -7.224141,-5.638356 -10.043315,-9.338525 M 14.847106,81.97224 25.41902,71.576535 m 0.17619,-6.695549 2.819179,19.910444 M 11.675534,73.338532 38.281518,71.047931 M 43.567475,62.7666 34.40515,62.942814 M 34.22896,62.590425 33.524162,48.494537 m -18.500855,1.58577 17.972249,-1.409582 m -11.8053,-5.462154 0.352397,18.853251"
|
||||
inkscape:label="杂" />
|
||||
<path
|
||||
id="path111"
|
||||
style="fill:#ffffff;fill-opacity:0.734285;stroke:#000000;stroke-width:3.94824;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="M 55.912937,82.876745 79.671596,81.412163 M 59.330273,75.391135 74.952411,74.089291 m -9.43837,-14.157553 1.139102,14.645756 m -8.299247,-7.160159 16.273048,-1.464569 m 0.650926,8.136525 0.325472,-14.808482 m -0.162747,0.162739 -17.900363,0.976379 m 0,-0.162738 1.952774,14.645756 m 12.042061,-21.154974 1.464576,-6.346492 m 0,-0.650928 -12.042063,0.650928 m -0.650918,6.509218 0.325459,-8.787441"
|
||||
inkscape:label="鱼" />
|
||||
<path
|
||||
d="m 95.08569,51.121163 c -1.90515,0.116064 -3.64694,0.97349 -4.86738,2.391307 -1.34538,1.56738 -1.91476,3.733159 -1.59523,6.070852 0.40842,2.982962 2.1502,6.17135 5.13887,9.411078 0.63424,0.68546 1.08109,1.129773 1.98202,1.967071 1.58321,1.469144 3.01507,2.634638 4.9875,4.052454 0.70392,0.50905 2.09253,1.453525 2.61627,1.781734 l 0.15133,0.09594 0.22103,-0.140663 c 0.80481,-0.515755 2.23909,-1.504852 3.08956,-2.130057 3.21689,-2.364488 5.79232,-4.737902 7.70228,-7.100167 3.09676,-3.831409 4.4133,-7.562359 3.80549,-10.773058 -0.42043,-2.210414 -1.82588,-4.039057 -3.81992,-4.967887 -0.85767,-0.399664 -1.69132,-0.607312 -2.6355,-0.656431 -1.22285,-0.0647 -2.42648,0.178619 -3.57485,0.721182 -1.95561,0.922124 -3.58927,2.719503 -4.61752,5.081755 -0.072,0.165235 -0.1394,0.310355 -0.14895,0.319295 -0.0312,0.02902 -0.0648,-0.02679 -0.19458,-0.330457 -0.30752,-0.714476 -0.91055,-1.752718 -1.38382,-2.377871 -0.4853,-0.645282 -1.2661,-1.431214 -1.84749,-1.862143 -1.50155,-1.114153 -3.26013,-1.658924 -5.00914,-1.553996 z"
|
||||
id="path1-4"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.00231605" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
101
docs/zh/README.md
Normal file
101
docs/zh/README.md
Normal file
@@ -0,0 +1,101 @@
|
||||
<img align='right' src='zakomonochrome-128.svg' width='100px' alt="logo">
|
||||
|
||||
# SukiSU Ultra
|
||||
|
||||
[English](../README.md) | **简体中文** | [日本語](../ja/README.md) | [Türkçe](../tr/README.md)
|
||||
|
||||
一个 Android 上基于内核的 root 方案,由 [`tiann/KernelSU`](https://github.com/tiann/KernelSU) 分叉而来,添加了一些有趣的变更。
|
||||
|
||||
[](https://github.com/tiann/KernelSU/releases/latest)
|
||||
[](https://t.me/Sukiksu)
|
||||
[](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
||||
[](/LICENSE)
|
||||
|
||||
## 特性
|
||||
|
||||
1. 基于内核的 `su` 和权限管理。
|
||||
2. 基于 [Magic Mount](https://github.com/5ec1cff/KernelSU) 的模块系统。
|
||||
3. [App Profile](https://kernelsu.org/zh_CN/guide/app-profile.html): 把 Root 权限关进笼子里。
|
||||
4. 支持 non-GKI 与 GKI 1.0。
|
||||
5. KPM 支持
|
||||
6. 可调整管理器外观,可自定义 susfs 配置。
|
||||
|
||||
## 兼容状态
|
||||
|
||||
- KernelSU 官方支持 GKI 2.0 的设备(内核版本 5.10 以上)。
|
||||
|
||||
- 旧内核也是兼容的(最低 4.14+),不过需要自己编译内核。
|
||||
|
||||
- 通过更多的反向移植,KernelSU 可以支持 3.x 内核(3.4-3.18)。
|
||||
|
||||
- 目前支持架构 : `arm64-v8a`、`armeabi-v7a (bare)`、`X86_64`。
|
||||
|
||||
## 安装指导
|
||||
|
||||
查看 [`guide/installation.md`](guide/installation.md)
|
||||
|
||||
## 集成指导
|
||||
|
||||
查看 [`guide/how-to-integrate.md`](guide/how-to-integrate.md)
|
||||
|
||||
## 参与翻译
|
||||
|
||||
要将 SukiSU 翻译成您的语言,或完善现有的翻译,请使用 [Crowdin](https://crowdin.com/project/SukiSU-Ultra).
|
||||
|
||||
## KPM 支持
|
||||
|
||||
- 基于 KernelPatch 开发,移除了与 KernelSU 重复的功能。
|
||||
- 正在进行(WIP):通过集成附加功能来扩展 APatch 兼容性,以确保跨不同实现的兼容性。
|
||||
|
||||
**开源仓库**: [https://github.com/ShirkNeko/SukiSU_KernelPatch_patch](https://github.com/ShirkNeko/SukiSU_KernelPatch_patch)
|
||||
|
||||
**KPM 模板**: [https://github.com/udochina/KPM-Build-Anywhere](https://github.com/udochina/KPM-Build-Anywhere)
|
||||
|
||||
> [!Note]
|
||||
>
|
||||
> 1. 需要 `CONFIG_KPM=y`
|
||||
> 2. Non-GKI 设备需要 `CONFIG_KALLSYMS=y` and `CONFIG_KALLSYMS_ALL=y`
|
||||
> 3. 对于低于 `4.19` 的内核,需要从 `4.19` 的 `set_memory.h` 进行反向移植。
|
||||
|
||||
## 故障排除
|
||||
|
||||
1. 卸载管理器后系统卡住?
|
||||
卸载 _com.sony.playmemories.mobile_
|
||||
|
||||
## 许可证
|
||||
|
||||
- 目录 `kernel` 下所有文件为 [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)。
|
||||
- 有动漫人物图片表情包的这些文件 `ic_launcher(?!.*alt.*).*` 的图像版权为[怡子曰曰](https://space.bilibili.com/10545509)所有,图像中的知识产权由[明风 OuO](https://space.bilibili.com/274939213)所有,矢量化由 @MiRinChan 完成,在使用这些文件之前,除了必须遵守 [Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt) 以外,还需要遵守向前两者索要使用这些艺术内容的授权。
|
||||
- 除上述文件及目录的其他部分均为 [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html)。
|
||||
|
||||
## 赞助
|
||||
|
||||
- [ShirkNeko](https://afdian.com/a/shirkneko) (SukiSU 主要维护者)
|
||||
- [weishu](https://github.com/sponsors/tiann) (KernelSU 作者)
|
||||
|
||||
## ShirkNeko 的赞助列表
|
||||
|
||||
- [Ktouls](https://github.com/Ktouls) 非常感谢你给我带来的支持
|
||||
- [zaoqi123](https://github.com/zaoqi123) 请我喝奶茶也不错
|
||||
- [wswzgdg](https://github.com/wswzgdg) 非常感谢对此项目的支持
|
||||
- [yspbwx2010](https://github.com/yspbwx2010) 非常感谢
|
||||
- [DARKWWEE](https://github.com/DARKWWEE) 感谢老哥的 100 USDT
|
||||
- [Saksham Singla](https://github.com/TypeFlu) 网站的提供以及维护
|
||||
- [OukaroMF](https://github.com/OukaroMF) 网站域名捐赠
|
||||
|
||||
## 鸣谢
|
||||
|
||||
- [KernelSU](https://github.com/tiann/KernelSU): 上游
|
||||
- [MKSU](https://github.com/5ec1cff/KernelSU): 魔法坐骑支持
|
||||
- [RKSU](https://github.com/rsuntk/KernelsU): non-GKI 支持
|
||||
- [susfs](https://gitlab.com/simonpunk/susfs4ksu): 隐藏内核补丁以及用户空间模组的 KernelSU 附件
|
||||
- [KernelPatch](https://github.com/bmax121/KernelPatch): KernelPatch 是内核模块 APatch 实现的关键部分
|
||||
|
||||
<details>
|
||||
<summary>KernelSU 的鸣谢</summary>
|
||||
|
||||
- [kernel-assisted-superuser](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 技巧。
|
||||
</details>
|
||||
97
docs/zh/guide/how-to-integrate.md
Normal file
97
docs/zh/guide/how-to-integrate.md
Normal file
@@ -0,0 +1,97 @@
|
||||
# 集成指导
|
||||
|
||||
SukiSU 可以集成到 GKI 和 non-GKI 内核中,并且已反向移植到 4.14 版本。
|
||||
|
||||
<!-- 应该是 3.4 版本,但 backslashxx 的 syscall manual hook 无法在 SukiSU 中使用-->
|
||||
|
||||
有些 OEM 定制可能导致多达 50% 的内核代码超出内核树代码,而非来自上游 Linux 内核或 ACK。因此,non-GKI 内核的定制特性导致了严重的内核碎片化,而且我们缺乏构建它们的通用方法。因此,我们无法提供 non-GKI 内核的启动映像。
|
||||
|
||||
前提条件:开源的、可启动的内核。
|
||||
|
||||
## Hook 方法
|
||||
|
||||
1. **KPROBES hook:**
|
||||
|
||||
- GKI kernels 的默认 hook 方法。
|
||||
- 需要 `# CONFIG_KSU_MANUAL_HOOK is not set`(未设定) & `CONFIG_KPROBES=y`
|
||||
- 用作可加载的内核模块 (LKM).
|
||||
|
||||
2. **Manual hook:**
|
||||
|
||||
<!-- - backslashxx's syscall manual hook: https://github.com/backslashxx/KernelSU/issues/5 (v1.5 version is not available at the moment, if you want to use it, please use v1.4 version, or standard KernelSU hooks)-->
|
||||
|
||||
- 需要 `CONFIG_KSU_MANUAL_HOOK=y`
|
||||
- 需要 [`guide/how-to-integrate.md`](how-to-integrate.md)
|
||||
- 需要 [https://github.com/~](https://github.com/tiann/KernelSU/blob/main/website/docs/guide/how-to-integrate-for-non-gki.md#manually-modify-the-kernel-source)
|
||||
|
||||
3. **Tracepoint Hook:**
|
||||
|
||||
- 自 SukiSU commit [49b01aad](https://github.com/SukiSU-Ultra/SukiSU-Ultra/commit/49b01aad74bcca6dba5a8a2e053bb54b648eb124) 引入的 hook 方法
|
||||
- 需要 `CONFIG_KSU_TRACEPOINT_HOOK=y`
|
||||
- 需要 [`guide/tracepoint-hook.md`](tracepoint-hook.md)
|
||||
|
||||
<!-- This part refer to [rsuntk/KernelSU](https://github.com/rsuntk/KernelSU). -->
|
||||
|
||||
如果您能够构建可启动内核,有两种方法可以将 KernelSU 集成到内核源代码中:
|
||||
|
||||
1. 使用 `kprobe` 自动集成
|
||||
2. 手动集成
|
||||
|
||||
## 与 kprobe 集成
|
||||
|
||||
适用:
|
||||
|
||||
- GKI 内核
|
||||
|
||||
不适用:
|
||||
|
||||
- non-GKI 内核
|
||||
|
||||
KernelSU 使用 kprobe 机制来做内核的相关 hook,如果 _kprobe_ 可以在你编译的内核中正常运行,那么推荐用这个方法来集成。
|
||||
|
||||
请参阅此文档 [https://github.com/~](https://github.com/tiann/KernelSU/blob/main/website/docs/guide/how-to-integrate-for-non-gki.md#integrate-with-kprobe)。虽然标题为“适用于 non-GKI”,但仅适用于 GKI。
|
||||
|
||||
替换 KernelSU 添加到内核源代码树的步骤的执行命令为:
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
|
||||
## 手动修改内核源代码
|
||||
|
||||
适用:
|
||||
|
||||
- GKI 内核
|
||||
- non-GKI 内核
|
||||
|
||||
请参考此文档 [https://github.com/~ (non-GKI 内核集成)](https://github.com/tiann/KernelSU/blob/main/website/docs/guide/how-to-integrate-for-non-gki.md#manually-modify-the-kernel-source) 和 [https://github.com/~ (GKI 内核构建)](https://kernelsu.org/zh_CN/guide/how-to-build.html) 进行手动集成。虽然第一个链接的标题是“适用于 non-GKI”,但它也适用于 GKI。两者都可以正常工作。
|
||||
|
||||
还有另一种集成方法,但是仍在开发中。
|
||||
|
||||
<!-- 这是 backslashxx 的syscall manual hook,但目前无法使用。 -->
|
||||
|
||||
将 KernelSU(SukiSU)添加到内核源代码树的步骤的运行命令将被替换为:
|
||||
|
||||
### GKI 内核
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
||||
```
|
||||
|
||||
### non-GKI 内核
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
|
||||
```
|
||||
|
||||
### 带有 susfs 的 GKI / non-GKI 内核(实验)
|
||||
|
||||
```sh
|
||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-{{branch}}
|
||||
```
|
||||
|
||||
分支:
|
||||
|
||||
- `main` (susfs-main)
|
||||
- `test` (susfs-test)
|
||||
- 版本号 (例如: susfs-1.5.7, 你需要在 [分支](https://github.com/SukiSU-Ultra/SukiSU-Ultra/branches) 里找到它)
|
||||
34
docs/zh/guide/installation.md
Normal file
34
docs/zh/guide/installation.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# 安装指导
|
||||
|
||||
您可以前往 [KernelSU 文档 - 安装](https://kernelsu.org/guide/installation.html) 获取有关如何安装的参考,这里只是额外的说明。
|
||||
|
||||
## 通过加载可加载内核模块 (LKM) 进行安装
|
||||
|
||||
请参阅 [KernelSU 文档 - LKM 安装](https://kernelsu.org/guide/installation.html#lkm-installation)
|
||||
|
||||
从 **Android™**(商标,意为获得 Google 移动服务的许可)12 开始,搭载内核版本 5.10 或更高版本的设备必须搭载 GKI 内核。因此你或许可以使用 LKM 模式。
|
||||
|
||||
## 通过安装内核进行安装
|
||||
|
||||
请参阅 [KernelSU 文档 - GKI 模式安装](https://kernelsu.org/guide/installation.html#gki-mode-installation)
|
||||
|
||||
我们提供预编译的内核供您使用:
|
||||
|
||||
- [ShirkNeko 内核](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)(添加了 ZRAM 压缩算法补丁、susfs 文件和 KPM 文件。适用于很多设备。)
|
||||
- [MiRinFork 内核](https://github.com/MiRinFork/GKI_SukiSU_SUSFS)(添加了 susfs 文件和 KPM 文件。最接近 GKI 的内核,适用于大多数设备。)
|
||||
|
||||
虽然某些设备可以使用 LKM 模式安装,但无法使用 GKI 内核将其安装到设备上;因此,需要手动修改内核进行编译。例如:
|
||||
|
||||
- 欧珀(一加、真我)
|
||||
- 魅族
|
||||
|
||||
此外,我们还为您的 OnePlus 设备提供预编译的内核:
|
||||
|
||||
- [ShirkNeko/Action_OnePlus_MKSU_SUSFS](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)(添加 ZRAM 压缩算法补丁、susfs 和 KPM。)
|
||||
|
||||
使用上面的链接,Fork 到 GitHub Action,填写构建参数,进行编译,最后将 zip 文件以 AnyKernel3 后缀上传到 GitHub Action。
|
||||
|
||||
> [!Note]
|
||||
>
|
||||
> - 使用时,您只需填写版本号的前两部分,例如 `5.10`、`6.1`...
|
||||
> - 使用前请确保您了解处理器名称、内核版本等信息。
|
||||
270
docs/zh/guide/tracepoint-hook.md
Normal file
270
docs/zh/guide/tracepoint-hook.md
Normal file
@@ -0,0 +1,270 @@
|
||||
# Tracepoint Hook 集成
|
||||
|
||||
## 介绍
|
||||
|
||||
自 commit [49b01aad](https://github.com/SukiSU-Ultra/SukiSU-Ultra/commit/49b01aad74bcca6dba5a8a2e053bb54b648eb124) 起,SukiSU 引入了 Tracepoint Hook
|
||||
|
||||
该 Hook 理论上相比于 Kprobes Hook,性能开销更小,但次于 Manual Hook / Syscall Hook
|
||||
|
||||
> [!NOTE]
|
||||
> 本教程参考了 [backslashxx/KernelSU#5](https://github.com/backslashxx/KernelSU/issues/5) 的 syscall hook v1.4 版本钩子,以及原版 KernelSU 的 [Manual Hook](https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source)
|
||||
|
||||
## Guide
|
||||
|
||||
### execve 钩子(`exec.c`)
|
||||
|
||||
一般需要修改 `fs/exec.c` 的 `do_execve` 和 `compat_do_execve` 方法
|
||||
|
||||
```patch
|
||||
--- a/fs/exec.c
|
||||
+++ b/fs/exec.c
|
||||
@@ -78,6 +78,10 @@
|
||||
#include <trace/hooks/sched.h>
|
||||
#endif
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(task_rename);
|
||||
|
||||
static int bprm_creds_from_file(struct linux_binprm *bprm);
|
||||
@@ -2037,6 +2041,9 @@ static int do_execve(struct filename *filename,
|
||||
{
|
||||
struct user_arg_ptr argv = { .ptr.native = __argv };
|
||||
struct user_arg_ptr envp = { .ptr.native = __envp };
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_execveat_hook((int *)AT_FDCWD, &filename, &argv, &envp, 0);
|
||||
+#endif
|
||||
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
|
||||
}
|
||||
|
||||
@@ -2064,6 +2071,9 @@ static int compat_do_execve(struct filename *filename,
|
||||
.is_compat = true,
|
||||
.ptr.compat = __envp,
|
||||
};
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_execveat_sucompat_hook((int *)AT_FDCWD, &filename, NULL, NULL, NULL); /* 32-bit su */
|
||||
+#endif
|
||||
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
|
||||
}
|
||||
```
|
||||
|
||||
### faccessat 钩子 (`open.c`)
|
||||
|
||||
一般需要修改 `/fs/open.c` 的 `do_faccessat` 方法
|
||||
|
||||
```patch
|
||||
--- a/fs/open.c
|
||||
+++ b/fs/open.c
|
||||
@@ -37,6 +37,10 @@
|
||||
#include "internal.h"
|
||||
#include <trace/hooks/syscall_check.h>
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
int do_truncate(struct user_namespace *mnt_userns, struct dentry *dentry,
|
||||
loff_t length, unsigned int time_attrs, struct file *filp)
|
||||
{
|
||||
@@ -468,6 +472,9 @@ static long do_faccessat(int dfd, const char __user *filename, int mode, int fla
|
||||
|
||||
SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
|
||||
{
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_faccessat_hook(&dfd, &filename, &mode, NULL);
|
||||
+#endif
|
||||
return do_faccessat(dfd, filename, mode, 0);
|
||||
}
|
||||
```
|
||||
|
||||
如果没有 `do_faccessat` 方法,可以找 `faccessat` 的 SYSCALL 定义(对于早于 4.17 的内核)
|
||||
|
||||
```patch
|
||||
--- a/fs/open.c
|
||||
+++ b/fs/open.c
|
||||
@@ -31,6 +31,9 @@
|
||||
#include <linux/ima.h>
|
||||
#include <linux/dnotify.h>
|
||||
#include <linux/compat.h>
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
|
||||
#include "internal.h"
|
||||
|
||||
@@ -369,6 +372,9 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
|
||||
int res;
|
||||
unsigned int lookup_flags = LOOKUP_FOLLOW;
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_faccessat_hook(&dfd, &filename, &mode, NULL);
|
||||
+#endif
|
||||
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
|
||||
return -EINVAL;
|
||||
```
|
||||
|
||||
### sys_read 钩子 ( `read_write.c` )
|
||||
|
||||
需要修改 `fs/read_write.c` 的 `sys_read` 方法(4.19 及以上)
|
||||
|
||||
```patch
|
||||
--- a/fs/read_write.c
|
||||
+++ b/fs/read_write.c
|
||||
@@ -25,6 +25,10 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
const struct file_operations generic_ro_fops = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read_iter = generic_file_read_iter,
|
||||
@@ -630,6 +634,9 @@ ssize_t ksys_read(unsigned int fd, char __user *buf, size_t count)
|
||||
|
||||
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
|
||||
{
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_sys_read_hook(fd, &buf, &count);
|
||||
+#endif
|
||||
return ksys_read(fd, buf, count);
|
||||
}
|
||||
```
|
||||
|
||||
或者是 `read` 的 SYSCALL 定义(4.14 及以下)
|
||||
|
||||
```patch
|
||||
--- a/fs/read_write.c
|
||||
+++ b/fs/read_write.c
|
||||
@@ -25,6 +25,11 @@
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
+
|
||||
const struct file_operations generic_ro_fops = {
|
||||
.llseek = generic_file_llseek,
|
||||
.read_iter = generic_file_read_iter,
|
||||
@@ -575,6 +580,9 @@ SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
|
||||
|
||||
if (f.file) {
|
||||
loff_t pos = file_pos_read(f.file);
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_sys_read_hook(fd, &buf, &count);
|
||||
+#endif
|
||||
ret = vfs_read(f.file, buf, count, &pos);
|
||||
if (ret >= 0)
|
||||
file_pos_write(f.file, pos);
|
||||
```
|
||||
|
||||
### fstatat 钩子 ( `stat.c` )
|
||||
|
||||
需要修改 `stat.c` 的 `newfstatat` SYSCALL 定义
|
||||
|
||||
如果需要 32 位支持,还需要修改 `statat64` SYSCALL 定义
|
||||
|
||||
```patch
|
||||
--- a/fs/stat.c
|
||||
+++ b/fs/stat.c
|
||||
@@ -24,6 +24,10 @@
|
||||
#include "internal.h"
|
||||
#include "mount.h"
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
/**
|
||||
* generic_fillattr - Fill in the basic attributes from the inode struct
|
||||
* @mnt_userns: user namespace of the mount the inode was found from
|
||||
@@ -408,6 +412,10 @@ SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename,
|
||||
struct kstat stat;
|
||||
int error;
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_stat_hook(&dfd, &filename, &flag);
|
||||
+#endif
|
||||
+
|
||||
error = vfs_fstatat(dfd, filename, &stat, flag);
|
||||
if (error)
|
||||
return error;
|
||||
@@ -559,6 +567,10 @@ SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,
|
||||
struct kstat stat;
|
||||
int error;
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_stat_hook(&dfd, &filename, &flag); /* 32-bit su support */
|
||||
+#endif
|
||||
+
|
||||
error = vfs_fstatat(dfd, filename, &stat, flag);
|
||||
if (error)
|
||||
return error;
|
||||
```
|
||||
|
||||
### input 钩子 (`input.c` ,用于进入KSU系的内置安全模式)
|
||||
|
||||
需要修改 `drivers/input/input.c` 的 `input_event` 方法,而不是 `input_handle_event`
|
||||
|
||||
```patch
|
||||
--- a/drivers/input/input.c
|
||||
+++ b/drivers/input/input.c
|
||||
@@ -26,6 +26,10 @@
|
||||
#include "input-compat.h"
|
||||
#include "input-poller.h"
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
|
||||
MODULE_DESCRIPTION("Input core");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -451,6 +455,10 @@ void input_event(struct input_dev *dev,
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_input_hook(&type, &code, &value);
|
||||
+#endif
|
||||
+
|
||||
if (is_event_supported(type, dev->evbit, EV_MAX)) {
|
||||
|
||||
spin_lock_irqsave(&dev->event_lock, flags);
|
||||
```
|
||||
|
||||
### devpts 钩子 (`pty.c`)
|
||||
|
||||
需要修改 `drivers/tty/pty.c` 的 `pts_unix98_lookup` 方法
|
||||
|
||||
```patch
|
||||
--- a/drivers/tty/pty.c
|
||||
+++ b/drivers/tty/pty.c
|
||||
@@ -31,6 +31,10 @@
|
||||
#include <linux/compat.h>
|
||||
#include "tty.h"
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+#include <../../drivers/kernelsu/ksu_trace.h>
|
||||
+#endif
|
||||
+
|
||||
#undef TTY_DEBUG_HANGUP
|
||||
#ifdef TTY_DEBUG_HANGUP
|
||||
# define tty_debug_hangup(tty, f, args...) tty_debug(tty, f, ##args)
|
||||
@@ -707,6 +711,10 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
|
||||
{
|
||||
struct tty_struct *tty;
|
||||
|
||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
+ trace_ksu_trace_devpts_hook((struct inode *)file->f_path.dentry->d_inode);
|
||||
+#endif
|
||||
+
|
||||
mutex_lock(&devpts_mutex);
|
||||
tty = devpts_get_priv(file->f_path.dentry);
|
||||
mutex_unlock(&devpts_mutex);
|
||||
```
|
||||
65
docs/zh/zakomonochrome-128.svg
Normal file
65
docs/zh/zakomonochrome-128.svg
Normal file
@@ -0,0 +1,65 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="128"
|
||||
height="128"
|
||||
viewBox="0 0 128 128"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:version="1.4.2 (ebf0e940d0, 2025-05-08)"
|
||||
sodipodi:docname="zakomonochrome-128.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#999999"
|
||||
borderopacity="1"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="px"
|
||||
inkscape:zoom="2.6185048"
|
||||
inkscape:cx="59.957881"
|
||||
inkscape:cy="71.032903"
|
||||
inkscape:window-width="1280"
|
||||
inkscape:window-height="696"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1" />
|
||||
<defs
|
||||
id="defs1" />
|
||||
<g
|
||||
inkscape:label="图层 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1">
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;paint-order:fill markers stroke;fill-opacity:1"
|
||||
id="rect1"
|
||||
width="128"
|
||||
height="128"
|
||||
x="0"
|
||||
y="0"
|
||||
rx="7.772471"
|
||||
ry="7.772471" />
|
||||
<path
|
||||
id="path101"
|
||||
style="fill:#ffffff;fill-opacity:0.734285;stroke:#000000;stroke-width:4.27504;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="m 42.510282,81.796052 c 0,0 -7.224141,-5.638356 -10.043315,-9.338525 M 14.847106,81.97224 25.41902,71.576535 m 0.17619,-6.695549 2.819179,19.910444 M 11.675534,73.338532 38.281518,71.047931 M 43.567475,62.7666 34.40515,62.942814 M 34.22896,62.590425 33.524162,48.494537 m -18.500855,1.58577 17.972249,-1.409582 m -11.8053,-5.462154 0.352397,18.853251"
|
||||
inkscape:label="杂" />
|
||||
<path
|
||||
id="path111"
|
||||
style="fill:#ffffff;fill-opacity:0.734285;stroke:#000000;stroke-width:3.94824;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
||||
d="M 55.912937,82.876745 79.671596,81.412163 M 59.330273,75.391135 74.952411,74.089291 m -9.43837,-14.157553 1.139102,14.645756 m -8.299247,-7.160159 16.273048,-1.464569 m 0.650926,8.136525 0.325472,-14.808482 m -0.162747,0.162739 -17.900363,0.976379 m 0,-0.162738 1.952774,14.645756 m 12.042061,-21.154974 1.464576,-6.346492 m 0,-0.650928 -12.042063,0.650928 m -0.650918,6.509218 0.325459,-8.787441"
|
||||
inkscape:label="鱼" />
|
||||
<path
|
||||
d="m 95.08569,51.121163 c -1.90515,0.116064 -3.64694,0.97349 -4.86738,2.391307 -1.34538,1.56738 -1.91476,3.733159 -1.59523,6.070852 0.40842,2.982962 2.1502,6.17135 5.13887,9.411078 0.63424,0.68546 1.08109,1.129773 1.98202,1.967071 1.58321,1.469144 3.01507,2.634638 4.9875,4.052454 0.70392,0.50905 2.09253,1.453525 2.61627,1.781734 l 0.15133,0.09594 0.22103,-0.140663 c 0.80481,-0.515755 2.23909,-1.504852 3.08956,-2.130057 3.21689,-2.364488 5.79232,-4.737902 7.70228,-7.100167 3.09676,-3.831409 4.4133,-7.562359 3.80549,-10.773058 -0.42043,-2.210414 -1.82588,-4.039057 -3.81992,-4.967887 -0.85767,-0.399664 -1.69132,-0.607312 -2.6355,-0.656431 -1.22285,-0.0647 -2.42648,0.178619 -3.57485,0.721182 -1.95561,0.922124 -3.58927,2.719503 -4.61752,5.081755 -0.072,0.165235 -0.1394,0.310355 -0.14895,0.319295 -0.0312,0.02902 -0.0648,-0.02679 -0.19458,-0.330457 -0.30752,-0.714476 -0.91055,-1.752718 -1.38382,-2.377871 -0.4853,-0.645282 -1.2661,-1.431214 -1.84749,-1.862143 -1.50155,-1.114153 -3.26013,-1.658924 -5.00914,-1.553996 z"
|
||||
id="path1-4"
|
||||
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.00231605" />
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
@@ -26,4 +26,32 @@ config KPM
|
||||
but it may affect system stability.
|
||||
select KALLSYMS
|
||||
select KALLSYMS_ALL
|
||||
|
||||
choice
|
||||
prompt "KernelSU hook type"
|
||||
depends on KSU
|
||||
default KSU_KPROBES_HOOK
|
||||
help
|
||||
Hook type for KernelSU
|
||||
|
||||
config KSU_KPROBES_HOOK
|
||||
bool "Hook KernelSU with Kprobes"
|
||||
depends on KPROBES
|
||||
help
|
||||
If enabled, Hook required KernelSU syscalls with Kernel-probe.
|
||||
|
||||
config KSU_TRACEPOINT_HOOK
|
||||
bool "Hook KernelSU with Tracepoint"
|
||||
depends on TRACEPOINTS
|
||||
help
|
||||
If enabled, Hook required KernelSU syscalls with Tracepoint.
|
||||
|
||||
config KSU_MANUAL_HOOK
|
||||
bool "Hook KernelSU manually"
|
||||
depends on KSU != m
|
||||
help
|
||||
If enabled, Hook required KernelSU syscalls with manually-patched function.
|
||||
|
||||
endchoice
|
||||
|
||||
endmenu
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
kernelsu-objs := ksu.o
|
||||
kernelsu-objs += allowlist.o
|
||||
kernelsu-objs += dynamic_manager.o
|
||||
kernelsu-objs += apk_sign.o
|
||||
kernelsu-objs += sucompat.o
|
||||
kernelsu-objs += throne_tracker.o
|
||||
@@ -8,6 +9,10 @@ kernelsu-objs += ksud.o
|
||||
kernelsu-objs += embed_ksud.o
|
||||
kernelsu-objs += kernel_compat.o
|
||||
|
||||
ifeq ($(CONFIG_KSU_TRACEPOINT_HOOK), y)
|
||||
kernelsu-objs += ksu_trace.o
|
||||
endif
|
||||
|
||||
kernelsu-objs += selinux/selinux.o
|
||||
kernelsu-objs += selinux/sepolicy.o
|
||||
kernelsu-objs += selinux/rules.o
|
||||
@@ -15,6 +20,7 @@ ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
|
||||
ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h
|
||||
|
||||
obj-$(CONFIG_KSU) += kernelsu.o
|
||||
obj-$(CONFIG_KSU_TRACEPOINT_HOOK) += ksu_trace_export.o
|
||||
|
||||
obj-$(CONFIG_KPM) += kpm/
|
||||
|
||||
@@ -22,7 +28,7 @@ obj-$(CONFIG_KPM) += kpm/
|
||||
REPO_OWNER := SukiSU-Ultra
|
||||
REPO_NAME := SukiSU-Ultra
|
||||
REPO_BRANCH := main
|
||||
KSU_VERSION_API := 3.1.7
|
||||
KSU_VERSION_API := 3.1.9
|
||||
|
||||
GIT_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git
|
||||
CURL_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin curl
|
||||
@@ -70,12 +76,15 @@ endif
|
||||
ccflags-y += -DKSU_VERSION=$(KSU_VERSION)
|
||||
ccflags-y += -DKSU_VERSION_FULL=\"$(KSU_VERSION_FULL)\"
|
||||
|
||||
ifndef KSU_EXPECTED_SIZE
|
||||
KSU_EXPECTED_SIZE := 0x35c
|
||||
# Custom Signs
|
||||
ifdef KSU_EXPECTED_SIZE
|
||||
ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)
|
||||
$(info -- Custom KernelSU Manager signature size: $(KSU_EXPECTED_SIZE))
|
||||
endif
|
||||
|
||||
ifndef KSU_EXPECTED_HASH
|
||||
KSU_EXPECTED_HASH := 947ae944f3de4ed4c21a7e4f7953ecf351bfa2b36239da37a34111ad29993eef
|
||||
ifdef KSU_EXPECTED_HASH
|
||||
ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\"
|
||||
$(info -- Custom KernelSU Manager signature hash: $(KSU_EXPECTED_HASH))
|
||||
endif
|
||||
|
||||
ifdef KSU_MANAGER_PACKAGE
|
||||
@@ -83,9 +92,16 @@ ccflags-y += -DKSU_MANAGER_PACKAGE=\"$(KSU_MANAGER_PACKAGE)\"
|
||||
$(info -- SukiSU Manager package name: $(KSU_MANAGER_PACKAGE))
|
||||
endif
|
||||
|
||||
$(info -- SukiSU Manager signature size: $(KSU_EXPECTED_SIZE))
|
||||
$(info -- SukiSU Manager signature hash: $(KSU_EXPECTED_HASH))
|
||||
$(info -- Supported Unofficial Manager: 5ec1cff (GKI) ShirkNeko udochina (GKI and KPM))
|
||||
|
||||
ifeq ($(strip $(CONFIG_KSU_KPROBES_HOOK)),y)
|
||||
$(info -- SukiSU: CONFIG_KSU_KPROBES_HOOK)
|
||||
else ifeq ($(strip $(CONFIG_KSU_TRACEPOINT_HOOK)),y)
|
||||
$(info -- SukiSU: CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
else ifeq ($(strip $(CONFIG_KSU_MANUAL_HOOK)),y)
|
||||
$(info -- SukiSU: CONFIG_KSU_MANUAL_HOOK)
|
||||
endif
|
||||
|
||||
KERNEL_VERSION := $(VERSION).$(PATCHLEVEL)
|
||||
KERNEL_TYPE := Non-GKI
|
||||
# Check for GKI 2.0 (5.10+ or 6.x+)
|
||||
@@ -107,10 +123,6 @@ else
|
||||
$(info -- KPM is disabled)
|
||||
endif
|
||||
|
||||
|
||||
ccflags-y += -DEXPECTED_SIZE=$(KSU_EXPECTED_SIZE)
|
||||
ccflags-y += -DEXPECTED_HASH=\"$(KSU_EXPECTED_HASH)\"
|
||||
|
||||
ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat
|
||||
ccflags-y += -Wno-declaration-after-statement -Wno-unused-function
|
||||
|
||||
|
||||
@@ -15,15 +15,27 @@
|
||||
#endif
|
||||
|
||||
#include "apk_sign.h"
|
||||
#include "dynamic_manager.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "kernel_compat.h"
|
||||
|
||||
#include "manager_sign.h"
|
||||
|
||||
struct sdesc {
|
||||
struct shash_desc shash;
|
||||
char ctx[];
|
||||
};
|
||||
|
||||
static struct apk_sign_key {
|
||||
unsigned size;
|
||||
const char *sha256;
|
||||
} apk_sign_keys[] = {
|
||||
{EXPECTED_SIZE_SHIRKNEKO, EXPECTED_HASH_SHIRKNEKO}, // ShirkNeko/SukiSU
|
||||
{EXPECTED_SIZE_OTHER, EXPECTED_HASH_OTHER}, // Dynamic Sign
|
||||
#ifdef EXPECTED_SIZE
|
||||
{EXPECTED_SIZE, EXPECTED_HASH}, // Custom
|
||||
#endif
|
||||
};
|
||||
|
||||
static struct sdesc *init_sdesc(struct crypto_shash *alg)
|
||||
{
|
||||
struct sdesc *sdesc;
|
||||
@@ -70,10 +82,12 @@ static int ksu_sha256(const unsigned char *data, unsigned int datalen,
|
||||
crypto_free_shash(alg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset,
|
||||
unsigned expected_size, const char *expected_sha256)
|
||||
static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset, int *matched_index)
|
||||
{
|
||||
int i;
|
||||
struct apk_sign_key sign_key;
|
||||
bool signature_valid = false;
|
||||
|
||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer-sequence length
|
||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signer length
|
||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // signed data length
|
||||
@@ -89,7 +103,20 @@ static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset,
|
||||
ksu_kernel_read_compat(fp, size4, 0x4, pos); // certificate length
|
||||
*offset += 0x4 * 2;
|
||||
|
||||
if (*size4 == expected_size) {
|
||||
for (i = 0; i < ARRAY_SIZE(apk_sign_keys); i++) {
|
||||
sign_key = apk_sign_keys[i];
|
||||
|
||||
if (i == 1) { // Dynamic Sign indexing
|
||||
unsigned int size;
|
||||
const char *hash;
|
||||
if (ksu_get_dynamic_manager_config(&size, &hash)) {
|
||||
sign_key.size = size;
|
||||
sign_key.sha256 = hash;
|
||||
}
|
||||
}
|
||||
|
||||
if (*size4 != sign_key.size)
|
||||
continue;
|
||||
*offset += *size4;
|
||||
|
||||
#define CERT_MAX_LENGTH 1024
|
||||
@@ -109,13 +136,17 @@ static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset,
|
||||
hash_str[SHA256_DIGEST_SIZE * 2] = '\0';
|
||||
|
||||
bin2hex(hash_str, digest, SHA256_DIGEST_SIZE);
|
||||
pr_info("sha256: %s, expected: %s\n", hash_str,
|
||||
expected_sha256);
|
||||
if (strcmp(expected_sha256, hash_str) == 0) {
|
||||
return true;
|
||||
pr_info("sha256: %s, expected: %s, index: %d\n", hash_str, sign_key.sha256, i);
|
||||
|
||||
if (strcmp(sign_key.sha256, hash_str) == 0) {
|
||||
signature_valid = true;
|
||||
if (matched_index) {
|
||||
*matched_index = i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return signature_valid;
|
||||
}
|
||||
|
||||
struct zip_entry_header {
|
||||
@@ -155,8 +186,7 @@ static bool has_v1_signature_file(struct file *fp)
|
||||
fileName[header.file_name_length] = '\0';
|
||||
|
||||
// Check if the entry matches META-INF/MANIFEST.MF
|
||||
if (strncmp(MANIFEST, fileName, sizeof(MANIFEST) - 1) ==
|
||||
0) {
|
||||
if (strncmp(MANIFEST, fileName, sizeof(MANIFEST) - 1) == 0) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
@@ -171,21 +201,17 @@ static bool has_v1_signature_file(struct file *fp)
|
||||
return false;
|
||||
}
|
||||
|
||||
static __always_inline bool check_v2_signature(char *path,
|
||||
unsigned expected_size,
|
||||
const char *expected_sha256)
|
||||
static __always_inline bool check_v2_signature(char *path, bool check_multi_manager, int *signature_index)
|
||||
{
|
||||
unsigned char buffer[0x11] = { 0 };
|
||||
u32 size4;
|
||||
u64 size8, size_of_block;
|
||||
|
||||
loff_t pos;
|
||||
|
||||
bool v2_signing_valid = false;
|
||||
int v2_signing_blocks = 0;
|
||||
bool v3_signing_exist = false;
|
||||
bool v3_1_signing_exist = false;
|
||||
|
||||
int matched_index = -1;
|
||||
int i;
|
||||
struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0);
|
||||
if (IS_ERR(fp)) {
|
||||
@@ -193,6 +219,12 @@ static __always_inline bool check_v2_signature(char *path,
|
||||
return false;
|
||||
}
|
||||
|
||||
// If you want to check for multi-manager APK signing, but dynamic managering is not enabled, skip
|
||||
if (check_multi_manager && !ksu_is_dynamic_manager_enabled()) {
|
||||
filp_close(fp, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// disable inotify for this file
|
||||
fp->f_mode |= FMODE_NONOTIFY;
|
||||
|
||||
@@ -244,9 +276,10 @@ static __always_inline bool check_v2_signature(char *path,
|
||||
offset = 4;
|
||||
if (id == 0x7109871au) {
|
||||
v2_signing_blocks++;
|
||||
v2_signing_valid =
|
||||
check_block(fp, &size4, &pos, &offset,
|
||||
expected_size, expected_sha256);
|
||||
bool result = check_block(fp, &size4, &pos, &offset, &matched_index);
|
||||
if (result) {
|
||||
v2_signing_valid = true;
|
||||
}
|
||||
} else if (id == 0xf05368c0u) {
|
||||
// http://aospxref.com/android-14.0.0_r2/xref/frameworks/base/core/java/android/util/apk/ApkSignatureSchemeV3Verifier.java#73
|
||||
v3_signing_exist = true;
|
||||
@@ -287,7 +320,24 @@ clean:
|
||||
return false;
|
||||
}
|
||||
|
||||
return v2_signing_valid;
|
||||
if (v2_signing_valid) {
|
||||
if (signature_index) {
|
||||
*signature_index = matched_index;
|
||||
}
|
||||
|
||||
if (check_multi_manager) {
|
||||
// 0: ShirkNeko/SukiSU, 1: Dynamic Sign
|
||||
if (matched_index == 0 || matched_index == 1) {
|
||||
pr_info("Multi-manager APK detected (dynamic_manager enabled): signature_index=%d\n", matched_index);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else {
|
||||
// Common manager check: any valid signature will do
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
@@ -316,5 +366,10 @@ module_param_cb(ksu_debug_manager_uid, &expected_size_ops,
|
||||
|
||||
bool is_manager_apk(char *path)
|
||||
{
|
||||
return check_v2_signature(path, EXPECTED_SIZE, EXPECTED_HASH);
|
||||
return check_v2_signature(path, false, NULL);
|
||||
}
|
||||
|
||||
bool ksu_is_dynamic_manager_apk(char *path, int *signature_index)
|
||||
{
|
||||
return check_v2_signature(path, true, signature_index);
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
#define __KSU_H_APK_V2_SIGN
|
||||
|
||||
#include <linux/types.h>
|
||||
#include "ksu.h"
|
||||
|
||||
bool is_manager_apk(char *path);
|
||||
|
||||
|
||||
@@ -42,10 +42,10 @@
|
||||
#include "manager.h"
|
||||
#include "selinux/selinux.h"
|
||||
#include "throne_tracker.h"
|
||||
#include "throne_tracker.h"
|
||||
#include "kernel_compat.h"
|
||||
|
||||
#include "kpm/kpm.h"
|
||||
#include "dynamic_manager.h"
|
||||
|
||||
static bool ksu_module_mounted = false;
|
||||
|
||||
@@ -250,6 +250,16 @@ static void nuke_ext4_sysfs() {
|
||||
static inline void nuke_ext4_sysfs() { }
|
||||
#endif
|
||||
|
||||
static bool is_system_bin_su()
|
||||
{
|
||||
// YES in_execve becomes 0 when it succeeds.
|
||||
if (!current->mm || current->in_execve)
|
||||
return false;
|
||||
|
||||
// quick af check
|
||||
return (current->mm->exe_file && !strcmp(current->mm->exe_file->f_path.dentry->d_name.name, "su"));
|
||||
}
|
||||
|
||||
int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5)
|
||||
{
|
||||
@@ -272,7 +282,8 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
bool from_root = 0 == current_uid().val;
|
||||
bool from_manager = is_manager();
|
||||
|
||||
if (!from_root && !from_manager) {
|
||||
if (!from_root && !from_manager
|
||||
&& !(is_allow_su() && is_system_bin_su())) {
|
||||
// only root or manager can access this interface
|
||||
return 0;
|
||||
}
|
||||
@@ -334,6 +345,57 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allow the root manager to configure dynamic manageratures
|
||||
if (arg2 == CMD_DYNAMIC_MANAGER) {
|
||||
if (!from_root && !from_manager) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dynamic_manager_user_config config;
|
||||
|
||||
if (copy_from_user(&config, (void __user *)arg3, sizeof(config))) {
|
||||
pr_err("copy dynamic manager config failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ret = ksu_handle_dynamic_manager(&config);
|
||||
|
||||
if (ret == 0 && config.operation == DYNAMIC_MANAGER_OP_GET) {
|
||||
if (copy_to_user((void __user *)arg3, &config, sizeof(config))) {
|
||||
pr_err("copy dynamic manager config back failed\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("dynamic_manager: prctl reply error\n");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Allow root manager to get active managers
|
||||
if (arg2 == CMD_GET_MANAGERS) {
|
||||
if (!from_root && !from_manager) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct manager_list_info manager_info;
|
||||
int ret = ksu_get_active_managers(&manager_info);
|
||||
|
||||
if (ret == 0) {
|
||||
if (copy_to_user((void __user *)arg3, &manager_info, sizeof(manager_info))) {
|
||||
pr_err("copy manager list failed\n");
|
||||
return 0;
|
||||
}
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("get_managers: prctl reply error\n");
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_REPORT_EVENT) {
|
||||
if (!from_root) {
|
||||
return 0;
|
||||
@@ -345,6 +407,9 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
post_fs_data_lock = true;
|
||||
pr_info("post-fs-data triggered\n");
|
||||
on_post_fs_data();
|
||||
// Initializing Dynamic Signatures
|
||||
ksu_dynamic_manager_init();
|
||||
pr_info("Dynamic sign config loaded during post-fs-data\n");
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -433,6 +498,30 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_ENABLE_SU) {
|
||||
bool enabled = (arg3 != 0);
|
||||
if (enabled == ksu_su_compat_enabled) {
|
||||
pr_info("cmd enable su but no need to change.\n");
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {// return the reply_ok directly
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
ksu_sucompat_init();
|
||||
} else {
|
||||
ksu_sucompat_exit();
|
||||
}
|
||||
ksu_su_compat_enabled = enabled;
|
||||
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KPM
|
||||
// ADD: 添加KPM模块控制
|
||||
if(sukisu_is_kpm_control_code(arg2)) {
|
||||
@@ -453,6 +542,28 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Checking hook usage
|
||||
if (arg2 == CMD_HOOK_TYPE) {
|
||||
const char *hook_type = "Kprobes";
|
||||
#if defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
||||
hook_type = "Tracepoint";
|
||||
#elif defined(CONFIG_KSU_MANUAL_HOOK)
|
||||
hook_type = "Manual";
|
||||
#endif
|
||||
|
||||
size_t len = strlen(hook_type) + 1;
|
||||
if (copy_to_user((void __user *)arg3, hook_type, len)) {
|
||||
pr_err("hook_type: copy_to_user failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("hook_type: prctl reply error\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// all other cmds are for 'root manager'
|
||||
if (!from_manager) {
|
||||
return 0;
|
||||
@@ -507,30 +618,6 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (arg2 == CMD_ENABLE_SU) {
|
||||
bool enabled = (arg3 != 0);
|
||||
if (enabled == ksu_su_compat_enabled) {
|
||||
pr_info("cmd enable su but no need to change.\n");
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {// return the reply_ok directly
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
ksu_sucompat_init();
|
||||
} else {
|
||||
ksu_sucompat_exit();
|
||||
}
|
||||
ksu_su_compat_enabled = enabled;
|
||||
|
||||
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
|
||||
pr_err("prctl reply error, cmd: %lu\n", arg2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -743,6 +830,19 @@ static int ksu_task_fix_setuid(struct cred *new, const struct cred *old,
|
||||
}
|
||||
|
||||
#ifndef MODULE
|
||||
|
||||
extern int __ksu_handle_devpts(struct inode *inode);
|
||||
static int ksu_inode_permission(struct inode *inode, int mask)
|
||||
{
|
||||
if (unlikely(inode->i_sb && inode->i_sb->s_magic == DEVPTS_SUPER_MAGIC)) {
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
pr_info("%s: devpts inode accessed with mask: %x\n", __func__, mask);
|
||||
#endif
|
||||
__ksu_handle_devpts(inode);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct security_hook_list ksu_hooks[] = {
|
||||
LSM_HOOK_INIT(task_prctl, ksu_task_prctl),
|
||||
LSM_HOOK_INIT(inode_rename, ksu_inode_rename),
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define __KSU_H_KSU_CORE
|
||||
|
||||
#include <linux/init.h>
|
||||
#include "apk_sign.h"
|
||||
|
||||
void __init ksu_core_init(void);
|
||||
void ksu_core_exit(void);
|
||||
|
||||
505
kernel/dynamic_manager.c
Normal file
505
kernel/dynamic_manager.c
Normal file
@@ -0,0 +1,505 @@
|
||||
#include <linux/err.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/version.h>
|
||||
#include <linux/workqueue.h>
|
||||
#ifdef CONFIG_KSU_DEBUG
|
||||
#include <linux/moduleparam.h>
|
||||
#endif
|
||||
#include <crypto/hash.h>
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
|
||||
#include <crypto/sha2.h>
|
||||
#else
|
||||
#include <crypto/sha.h>
|
||||
#endif
|
||||
|
||||
#include "dynamic_manager.h"
|
||||
#include "klog.h" // IWYU pragma: keep
|
||||
#include "kernel_compat.h"
|
||||
#include "manager.h"
|
||||
|
||||
#define MAX_MANAGERS 2
|
||||
|
||||
// Dynamic sign configuration
|
||||
static struct dynamic_manager_config dynamic_manager = {
|
||||
.size = 0x300,
|
||||
.hash = "0000000000000000000000000000000000000000000000000000000000000000",
|
||||
.is_set = 0
|
||||
};
|
||||
|
||||
// Multi-manager state
|
||||
static struct manager_info active_managers[MAX_MANAGERS];
|
||||
static DEFINE_SPINLOCK(managers_lock);
|
||||
static DEFINE_SPINLOCK(dynamic_manager_lock);
|
||||
|
||||
// Work queues for persistent storage
|
||||
static struct work_struct ksu_save_dynamic_manager_work;
|
||||
static struct work_struct ksu_load_dynamic_manager_work;
|
||||
static struct work_struct ksu_clear_dynamic_manager_work;
|
||||
|
||||
bool ksu_is_dynamic_manager_enabled(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
bool enabled;
|
||||
|
||||
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||
enabled = dynamic_manager.is_set;
|
||||
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||
|
||||
return enabled;
|
||||
}
|
||||
|
||||
void ksu_add_manager(uid_t uid, int signature_index)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
if (!ksu_is_dynamic_manager_enabled()) {
|
||||
pr_info("Dynamic sign not enabled, skipping multi-manager add\n");
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&managers_lock, flags);
|
||||
|
||||
// Check if manager already exists and update
|
||||
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||
if (active_managers[i].is_active && active_managers[i].uid == uid) {
|
||||
active_managers[i].signature_index = signature_index;
|
||||
spin_unlock_irqrestore(&managers_lock, flags);
|
||||
pr_info("Updated manager uid=%d, signature_index=%d\n", uid, signature_index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Find free slot for new manager
|
||||
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||
if (!active_managers[i].is_active) {
|
||||
active_managers[i].uid = uid;
|
||||
active_managers[i].signature_index = signature_index;
|
||||
active_managers[i].is_active = true;
|
||||
spin_unlock_irqrestore(&managers_lock, flags);
|
||||
pr_info("Added manager uid=%d, signature_index=%d\n", uid, signature_index);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&managers_lock, flags);
|
||||
pr_warn("Failed to add manager, no free slots\n");
|
||||
}
|
||||
|
||||
void ksu_remove_manager(uid_t uid)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
if (!ksu_is_dynamic_manager_enabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&managers_lock, flags);
|
||||
|
||||
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||
if (active_managers[i].is_active && active_managers[i].uid == uid) {
|
||||
active_managers[i].is_active = false;
|
||||
pr_info("Removed manager uid=%d\n", uid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&managers_lock, flags);
|
||||
}
|
||||
|
||||
bool ksu_is_any_manager(uid_t uid)
|
||||
{
|
||||
unsigned long flags;
|
||||
bool is_manager = false;
|
||||
int i;
|
||||
|
||||
if (!ksu_is_dynamic_manager_enabled()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&managers_lock, flags);
|
||||
|
||||
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||
if (active_managers[i].is_active && active_managers[i].uid == uid) {
|
||||
is_manager = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&managers_lock, flags);
|
||||
return is_manager;
|
||||
}
|
||||
|
||||
int ksu_get_manager_signature_index(uid_t uid)
|
||||
{
|
||||
unsigned long flags;
|
||||
int signature_index = -1;
|
||||
int i;
|
||||
|
||||
// Check traditional manager first
|
||||
if (ksu_manager_uid != KSU_INVALID_UID && uid == ksu_manager_uid) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (!ksu_is_dynamic_manager_enabled()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&managers_lock, flags);
|
||||
|
||||
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||
if (active_managers[i].is_active && active_managers[i].uid == uid) {
|
||||
signature_index = active_managers[i].signature_index;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&managers_lock, flags);
|
||||
return signature_index;
|
||||
}
|
||||
|
||||
static void clear_dynamic_manager(void)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
spin_lock_irqsave(&managers_lock, flags);
|
||||
|
||||
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||
if (active_managers[i].is_active) {
|
||||
pr_info("Clearing dynamic manager uid=%d (signature_index=%d) for rescan\n",
|
||||
active_managers[i].uid, active_managers[i].signature_index);
|
||||
active_managers[i].is_active = false;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&managers_lock, flags);
|
||||
}
|
||||
|
||||
int ksu_get_active_managers(struct manager_list_info *info)
|
||||
{
|
||||
unsigned long flags;
|
||||
int i, count = 0;
|
||||
|
||||
if (!info) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
// Add traditional manager first
|
||||
if (ksu_manager_uid != KSU_INVALID_UID && count < 2) {
|
||||
info->managers[count].uid = ksu_manager_uid;
|
||||
info->managers[count].signature_index = 0;
|
||||
count++;
|
||||
}
|
||||
|
||||
// Add dynamic managers
|
||||
if (ksu_is_dynamic_manager_enabled()) {
|
||||
spin_lock_irqsave(&managers_lock, flags);
|
||||
|
||||
for (i = 0; i < MAX_MANAGERS && count < 2; i++) {
|
||||
if (active_managers[i].is_active) {
|
||||
info->managers[count].uid = active_managers[i].uid;
|
||||
info->managers[count].signature_index = active_managers[i].signature_index;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&managers_lock, flags);
|
||||
}
|
||||
|
||||
info->count = count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void do_save_dynamic_manager(struct work_struct *work)
|
||||
{
|
||||
u32 magic = DYNAMIC_MANAGER_FILE_MAGIC;
|
||||
u32 version = DYNAMIC_MANAGER_FILE_VERSION;
|
||||
struct dynamic_manager_config config_to_save;
|
||||
loff_t off = 0;
|
||||
unsigned long flags;
|
||||
struct file *fp;
|
||||
|
||||
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||
config_to_save = dynamic_manager;
|
||||
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||
|
||||
if (!config_to_save.is_set) {
|
||||
pr_info("Dynamic sign config not set, skipping save\n");
|
||||
return;
|
||||
}
|
||||
|
||||
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("save_dynamic_manager create file failed: %ld\n", PTR_ERR(fp));
|
||||
return;
|
||||
}
|
||||
|
||||
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
|
||||
pr_err("save_dynamic_manager write magic failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) != sizeof(version)) {
|
||||
pr_err("save_dynamic_manager write version failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (ksu_kernel_write_compat(fp, &config_to_save, sizeof(config_to_save), &off) != sizeof(config_to_save)) {
|
||||
pr_err("save_dynamic_manager write config failed.\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
pr_info("Dynamic sign config saved successfully\n");
|
||||
|
||||
exit:
|
||||
filp_close(fp, 0);
|
||||
}
|
||||
|
||||
static void do_load_dynamic_manager(struct work_struct *work)
|
||||
{
|
||||
loff_t off = 0;
|
||||
ssize_t ret = 0;
|
||||
struct file *fp = NULL;
|
||||
u32 magic;
|
||||
u32 version;
|
||||
struct dynamic_manager_config loaded_config;
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_RDONLY, 0);
|
||||
if (IS_ERR(fp)) {
|
||||
if (PTR_ERR(fp) == -ENOENT) {
|
||||
pr_info("No saved dynamic manager config found\n");
|
||||
} else {
|
||||
pr_err("load_dynamic_manager open file failed: %ld\n", PTR_ERR(fp));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
|
||||
magic != DYNAMIC_MANAGER_FILE_MAGIC) {
|
||||
pr_err("dynamic manager file invalid magic: %x!\n", magic);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) != sizeof(version)) {
|
||||
pr_err("dynamic manager read version failed\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
pr_info("dynamic manager file version: %d\n", version);
|
||||
|
||||
ret = ksu_kernel_read_compat(fp, &loaded_config, sizeof(loaded_config), &off);
|
||||
if (ret <= 0) {
|
||||
pr_info("load_dynamic_manager read err: %zd\n", ret);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (ret != sizeof(loaded_config)) {
|
||||
pr_err("load_dynamic_manager read incomplete config: %zd/%zu\n", ret, sizeof(loaded_config));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (loaded_config.size < 0x100 || loaded_config.size > 0x1000) {
|
||||
pr_err("Invalid saved config size: 0x%x\n", loaded_config.size);
|
||||
goto exit;
|
||||
}
|
||||
|
||||
if (strlen(loaded_config.hash) != 64) {
|
||||
pr_err("Invalid saved config hash length: %zu\n", strlen(loaded_config.hash));
|
||||
goto exit;
|
||||
}
|
||||
|
||||
// Validate hash format
|
||||
for (i = 0; i < 64; i++) {
|
||||
char c = loaded_config.hash[i];
|
||||
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) {
|
||||
pr_err("Invalid saved config hash character at position %d: %c\n", i, c);
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||
dynamic_manager = loaded_config;
|
||||
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||
|
||||
pr_info("Dynamic sign config loaded: size=0x%x, hash=%.16s...\n",
|
||||
loaded_config.size, loaded_config.hash);
|
||||
|
||||
exit:
|
||||
filp_close(fp, 0);
|
||||
}
|
||||
|
||||
static bool persistent_dynamic_manager(void)
|
||||
{
|
||||
return ksu_queue_work(&ksu_save_dynamic_manager_work);
|
||||
}
|
||||
|
||||
static void do_clear_dynamic_manager(struct work_struct *work)
|
||||
{
|
||||
loff_t off = 0;
|
||||
struct file *fp;
|
||||
char zero_buffer[512];
|
||||
|
||||
memset(zero_buffer, 0, sizeof(zero_buffer));
|
||||
|
||||
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
|
||||
if (IS_ERR(fp)) {
|
||||
pr_err("clear_dynamic_manager create file failed: %ld\n", PTR_ERR(fp));
|
||||
return;
|
||||
}
|
||||
|
||||
// Write null bytes to overwrite the file content
|
||||
if (ksu_kernel_write_compat(fp, zero_buffer, sizeof(zero_buffer), &off) != sizeof(zero_buffer)) {
|
||||
pr_err("clear_dynamic_manager write null bytes failed.\n");
|
||||
} else {
|
||||
pr_info("Dynamic sign config file cleared successfully\n");
|
||||
}
|
||||
|
||||
filp_close(fp, 0);
|
||||
}
|
||||
|
||||
static bool clear_dynamic_manager_file(void)
|
||||
{
|
||||
return ksu_queue_work(&ksu_clear_dynamic_manager_work);
|
||||
}
|
||||
|
||||
int ksu_handle_dynamic_manager(struct dynamic_manager_user_config *config)
|
||||
{
|
||||
unsigned long flags;
|
||||
int ret = 0;
|
||||
int i;
|
||||
|
||||
if (!config) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
switch (config->operation) {
|
||||
case DYNAMIC_MANAGER_OP_SET:
|
||||
if (config->size < 0x100 || config->size > 0x1000) {
|
||||
pr_err("invalid size: 0x%x\n", config->size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (strlen(config->hash) != 64) {
|
||||
pr_err("invalid hash length: %zu\n", strlen(config->hash));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
// Validate hash format
|
||||
for (i = 0; i < 64; i++) {
|
||||
char c = config->hash[i];
|
||||
if (!((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f'))) {
|
||||
pr_err("invalid hash character at position %d: %c\n", i, c);
|
||||
return -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||
dynamic_manager.size = config->size;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||
strscpy(dynamic_manager.hash, config->hash, sizeof(dynamic_manager.hash));
|
||||
#else
|
||||
strlcpy(dynamic_manager.hash, config->hash, sizeof(dynamic_manager.hash));
|
||||
#endif
|
||||
dynamic_manager.is_set = 1;
|
||||
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||
|
||||
persistent_dynamic_manager();
|
||||
pr_info("dynamic manager updated: size=0x%x, hash=%.16s... (multi-manager enabled)\n",
|
||||
config->size, config->hash);
|
||||
break;
|
||||
|
||||
case DYNAMIC_MANAGER_OP_GET:
|
||||
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||
if (dynamic_manager.is_set) {
|
||||
config->size = dynamic_manager.size;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
|
||||
strscpy(config->hash, dynamic_manager.hash, sizeof(config->hash));
|
||||
#else
|
||||
strlcpy(config->hash, dynamic_manager.hash, sizeof(config->hash));
|
||||
#endif
|
||||
ret = 0;
|
||||
} else {
|
||||
ret = -ENODATA;
|
||||
}
|
||||
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||
break;
|
||||
|
||||
case DYNAMIC_MANAGER_OP_CLEAR:
|
||||
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||
dynamic_manager.size = 0x300;
|
||||
strcpy(dynamic_manager.hash, "0000000000000000000000000000000000000000000000000000000000000000");
|
||||
dynamic_manager.is_set = 0;
|
||||
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||
|
||||
// Clear only dynamic managers, preserve default manager
|
||||
clear_dynamic_manager();
|
||||
|
||||
// Clear file using the same method as save
|
||||
clear_dynamic_manager_file();
|
||||
|
||||
pr_info("Dynamic sign config cleared (multi-manager disabled)\n");
|
||||
break;
|
||||
|
||||
default:
|
||||
pr_err("Invalid dynamic manager operation: %d\n", config->operation);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ksu_load_dynamic_manager(void)
|
||||
{
|
||||
return ksu_queue_work(&ksu_load_dynamic_manager_work);
|
||||
}
|
||||
|
||||
void ksu_dynamic_manager_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
INIT_WORK(&ksu_save_dynamic_manager_work, do_save_dynamic_manager);
|
||||
INIT_WORK(&ksu_load_dynamic_manager_work, do_load_dynamic_manager);
|
||||
INIT_WORK(&ksu_clear_dynamic_manager_work, do_clear_dynamic_manager);
|
||||
|
||||
// Initialize manager slots
|
||||
for (i = 0; i < MAX_MANAGERS; i++) {
|
||||
active_managers[i].is_active = false;
|
||||
}
|
||||
|
||||
ksu_load_dynamic_manager();
|
||||
|
||||
pr_info("Dynamic sign initialized with conditional multi-manager support\n");
|
||||
}
|
||||
|
||||
void ksu_dynamic_manager_exit(void)
|
||||
{
|
||||
clear_dynamic_manager();
|
||||
|
||||
// Save current config before exit
|
||||
do_save_dynamic_manager(NULL);
|
||||
pr_info("Dynamic sign exited with persistent storage\n");
|
||||
}
|
||||
|
||||
// Get dynamic manager configuration for signature verification
|
||||
bool ksu_get_dynamic_manager_config(unsigned int *size, const char **hash)
|
||||
{
|
||||
unsigned long flags;
|
||||
bool valid = false;
|
||||
|
||||
spin_lock_irqsave(&dynamic_manager_lock, flags);
|
||||
if (dynamic_manager.is_set) {
|
||||
if (size) *size = dynamic_manager.size;
|
||||
if (hash) *hash = dynamic_manager.hash;
|
||||
valid = true;
|
||||
}
|
||||
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
|
||||
|
||||
return valid;
|
||||
}
|
||||
43
kernel/dynamic_manager.h
Normal file
43
kernel/dynamic_manager.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef __KSU_H_DYNAMIC_MANAGER
|
||||
#define __KSU_H_DYNAMIC_MANAGER
|
||||
|
||||
#include <linux/types.h>
|
||||
#include "ksu.h"
|
||||
|
||||
#define DYNAMIC_MANAGER_FILE_MAGIC 0x7f445347 // 'DSG', u32
|
||||
#define DYNAMIC_MANAGER_FILE_VERSION 1 // u32
|
||||
#define KERNEL_SU_DYNAMIC_MANAGER "/data/adb/ksu/.dynamic_manager"
|
||||
|
||||
struct dynamic_manager_config {
|
||||
unsigned int size;
|
||||
char hash[65];
|
||||
int is_set;
|
||||
};
|
||||
|
||||
struct manager_info {
|
||||
uid_t uid;
|
||||
int signature_index;
|
||||
bool is_active;
|
||||
};
|
||||
|
||||
// Dynamic sign operations
|
||||
void ksu_dynamic_manager_init(void);
|
||||
void ksu_dynamic_manager_exit(void);
|
||||
int ksu_handle_dynamic_manager(struct dynamic_manager_user_config *config);
|
||||
bool ksu_load_dynamic_manager(void);
|
||||
bool ksu_is_dynamic_manager_enabled(void);
|
||||
|
||||
// Multi-manager operations
|
||||
void ksu_add_manager(uid_t uid, int signature_index);
|
||||
void ksu_remove_manager(uid_t uid);
|
||||
bool ksu_is_any_manager(uid_t uid);
|
||||
int ksu_get_manager_signature_index(uid_t uid);
|
||||
int ksu_get_active_managers(struct manager_list_info *info);
|
||||
|
||||
// Multi-manager APK verification
|
||||
bool ksu_is_dynamic_manager_apk(char *path, int *signature_index);
|
||||
|
||||
// Configuration access for signature verification
|
||||
bool ksu_get_dynamic_manager_config(unsigned int *size, const char **hash);
|
||||
|
||||
#endif
|
||||
@@ -63,7 +63,7 @@ void sukisu_kpm_load_module_path(const char* path, const char* args, void* ptr,
|
||||
int res = -1;
|
||||
printk("KPM: Stub function called (sukisu_kpm_load_module_path). path=%s args=%s ptr=%p\n", path, args, ptr);
|
||||
__asm__ volatile("nop"); // 精确控制循环不被优化
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user faild.");
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user failed.");
|
||||
}
|
||||
|
||||
noinline
|
||||
@@ -73,7 +73,7 @@ void sukisu_kpm_unload_module(const char* name, void* ptr, void __user* result)
|
||||
int res = -1;
|
||||
printk("KPM: Stub function called (sukisu_kpm_unload_module). name=%s ptr=%p\n", name, ptr);
|
||||
__asm__ volatile("nop"); // 精确控制循环不被优化
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user faild.");
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user failed.");
|
||||
}
|
||||
|
||||
noinline
|
||||
@@ -83,7 +83,7 @@ void sukisu_kpm_num(void __user* result) {
|
||||
int res = 0;
|
||||
printk("KPM: Stub function called (sukisu_kpm_num).\n");
|
||||
__asm__ volatile("nop"); // 精确控制循环不被优化
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user faild.");
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user failed.");
|
||||
}
|
||||
|
||||
noinline
|
||||
@@ -93,7 +93,7 @@ void sukisu_kpm_info(const char* name, void __user* out, void __user* result) {
|
||||
int res = -1;
|
||||
printk("KPM: Stub function called (sukisu_kpm_info). name=%s buffer=%p\n", name, out);
|
||||
__asm__ volatile("nop"); // 精确控制循环不被优化
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user faild.");
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user failed.");
|
||||
}
|
||||
|
||||
noinline
|
||||
@@ -102,7 +102,7 @@ void sukisu_kpm_list(void __user* out, unsigned int bufferSize, void __user* res
|
||||
// This is a KPM module stub.
|
||||
int res = -1;
|
||||
printk("KPM: Stub function called (sukisu_kpm_list). buffer=%p size=%d\n", out, bufferSize);
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user faild.");
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user failed.");
|
||||
}
|
||||
|
||||
noinline
|
||||
@@ -112,7 +112,7 @@ void sukisu_kpm_control(void __user* name, void __user* args, void __user* resul
|
||||
int res = -1;
|
||||
printk("KPM: Stub function called (sukisu_kpm_control). name=%p args=%p\n", name, args);
|
||||
__asm__ volatile("nop"); // 精确控制循环不被优化
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user faild.");
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user failed.");
|
||||
}
|
||||
|
||||
noinline
|
||||
@@ -120,7 +120,7 @@ NO_OPTIMIZE
|
||||
void sukisu_kpm_version(void __user* out, unsigned int bufferSize, void __user* result) {
|
||||
int res = -1;
|
||||
printk("KPM: Stub function called (sukisu_kpm_version). buffer=%p size=%d\n", out, bufferSize);
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user faild.");
|
||||
if(copy_to_user(result, &res, sizeof(res)) < 1) printk("KPM: Copy to user failed.");
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(sukisu_kpm_load_module_path);
|
||||
|
||||
16
kernel/ksu.c
16
kernel/ksu.c
@@ -36,6 +36,10 @@ extern void ksu_sucompat_init();
|
||||
extern void ksu_sucompat_exit();
|
||||
extern void ksu_ksud_init();
|
||||
extern void ksu_ksud_exit();
|
||||
#ifdef CONFIG_KSU_TRACEPOINT_HOOK
|
||||
extern void ksu_trace_register();
|
||||
extern void ksu_trace_unregister();
|
||||
#endif
|
||||
|
||||
int __init kernelsu_init(void)
|
||||
{
|
||||
@@ -56,13 +60,17 @@ int __init kernelsu_init(void)
|
||||
ksu_allowlist_init();
|
||||
|
||||
ksu_throne_tracker_init();
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
ksu_sucompat_init();
|
||||
ksu_ksud_init();
|
||||
#else
|
||||
pr_alert("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html");
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KSU_TRACEPOINT_HOOK
|
||||
ksu_trace_register();
|
||||
#endif
|
||||
|
||||
#ifdef MODULE
|
||||
#ifndef CONFIG_KSU_DEBUG
|
||||
kobject_del(&THIS_MODULE->mkobj.kobj);
|
||||
@@ -79,11 +87,15 @@ void kernelsu_exit(void)
|
||||
|
||||
destroy_workqueue(ksu_workqueue);
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
ksu_ksud_exit();
|
||||
ksu_sucompat_exit();
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KSU_TRACEPOINT_HOOK
|
||||
ksu_trace_unregister();
|
||||
#endif
|
||||
|
||||
ksu_core_exit();
|
||||
}
|
||||
|
||||
|
||||
23
kernel/ksu.h
23
kernel/ksu.h
@@ -24,9 +24,12 @@
|
||||
#define CMD_IS_SU_ENABLED 14
|
||||
#define CMD_ENABLE_SU 15
|
||||
|
||||
#define CMD_GET_FULL_VERSION 30
|
||||
#define CMD_GET_FULL_VERSION 0xC0FFEE1A
|
||||
|
||||
#define CMD_ENABLE_KPM 100
|
||||
#define CMD_HOOK_TYPE 101
|
||||
#define CMD_DYNAMIC_MANAGER 103
|
||||
#define CMD_GET_MANAGERS 104
|
||||
|
||||
#define EVENT_POST_FS_DATA 1
|
||||
#define EVENT_BOOT_COMPLETED 2
|
||||
@@ -44,6 +47,24 @@
|
||||
#endif
|
||||
#define KSU_FULL_VERSION_STRING 255
|
||||
|
||||
#define DYNAMIC_MANAGER_OP_SET 0
|
||||
#define DYNAMIC_MANAGER_OP_GET 1
|
||||
#define DYNAMIC_MANAGER_OP_CLEAR 2
|
||||
|
||||
struct dynamic_manager_user_config {
|
||||
unsigned int operation;
|
||||
unsigned int size;
|
||||
char hash[65];
|
||||
};
|
||||
|
||||
struct manager_list_info {
|
||||
int count;
|
||||
struct {
|
||||
uid_t uid;
|
||||
int signature_index;
|
||||
} managers[2];
|
||||
};
|
||||
|
||||
struct root_profile {
|
||||
int32_t uid;
|
||||
int32_t gid;
|
||||
|
||||
90
kernel/ksu_trace.c
Normal file
90
kernel/ksu_trace.c
Normal file
@@ -0,0 +1,90 @@
|
||||
#include "ksu_trace.h"
|
||||
|
||||
|
||||
// extern kernelsu functions
|
||||
extern bool ksu_execveat_hook __read_mostly;
|
||||
extern bool ksu_vfs_read_hook __read_mostly;
|
||||
extern bool ksu_input_hook __read_mostly;
|
||||
extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv, void *envp, int *flags);
|
||||
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr, void *argv, void *envp, int *flags);
|
||||
extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode, int *flags);
|
||||
extern int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr, size_t *count_ptr);
|
||||
extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
|
||||
extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
|
||||
extern int ksu_handle_devpts(struct inode*);
|
||||
// end kernelsu functions
|
||||
|
||||
|
||||
// tracepoint callback functions
|
||||
void ksu_trace_execveat_hook_callback(void *data, int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags)
|
||||
{
|
||||
if (unlikely(ksu_execveat_hook))
|
||||
ksu_handle_execveat(fd, filename_ptr, argv, envp, flags);
|
||||
else
|
||||
ksu_handle_execveat_sucompat(fd, filename_ptr, NULL, NULL, NULL);
|
||||
}
|
||||
|
||||
void ksu_trace_execveat_sucompat_hook_callback(void *data, int *fd, struct filename **filename_ptr,
|
||||
void *argv, void *envp, int *flags)
|
||||
{
|
||||
if (!ksu_execveat_hook)
|
||||
ksu_handle_execveat_sucompat(fd, filename_ptr, argv, envp, flags);
|
||||
}
|
||||
|
||||
void ksu_trace_faccessat_hook_callback(void *data, int *dfd, const char __user **filename_user,
|
||||
int *mode, int *flags)
|
||||
{
|
||||
ksu_handle_faccessat(dfd, filename_user, mode, flags);
|
||||
}
|
||||
|
||||
void ksu_trace_sys_read_hook_callback(void *data, unsigned int fd, char __user **buf_ptr,
|
||||
size_t *count_ptr)
|
||||
{
|
||||
if (unlikely(ksu_vfs_read_hook))
|
||||
ksu_handle_sys_read(fd, buf_ptr, count_ptr);
|
||||
}
|
||||
|
||||
void ksu_trace_stat_hook_callback(void *data, int *dfd, const char __user **filename_user,
|
||||
int *flags)
|
||||
{
|
||||
ksu_handle_stat(dfd, filename_user, flags);
|
||||
}
|
||||
|
||||
void ksu_trace_input_hook_callback(void *data, unsigned int *type, unsigned int *code,
|
||||
int *value)
|
||||
{
|
||||
if (unlikely(ksu_input_hook))
|
||||
ksu_handle_input_handle_event(type, code, value);
|
||||
}
|
||||
|
||||
void ksu_trace_devpts_hook_callback(void *data, struct inode *inode)
|
||||
{
|
||||
ksu_handle_devpts(inode);
|
||||
}
|
||||
// end tracepoint callback functions
|
||||
|
||||
|
||||
// register tracepoint callback functions
|
||||
void ksu_trace_register(void)
|
||||
{
|
||||
register_trace_ksu_trace_execveat_hook(ksu_trace_execveat_hook_callback, NULL);
|
||||
register_trace_ksu_trace_execveat_sucompat_hook(ksu_trace_execveat_sucompat_hook_callback, NULL);
|
||||
register_trace_ksu_trace_faccessat_hook(ksu_trace_faccessat_hook_callback, NULL);
|
||||
register_trace_ksu_trace_sys_read_hook(ksu_trace_sys_read_hook_callback, NULL);
|
||||
register_trace_ksu_trace_stat_hook(ksu_trace_stat_hook_callback, NULL);
|
||||
register_trace_ksu_trace_input_hook(ksu_trace_input_hook_callback, NULL);
|
||||
register_trace_ksu_trace_devpts_hook(ksu_trace_devpts_hook_callback, NULL);
|
||||
}
|
||||
|
||||
// unregister tracepoint callback functions
|
||||
void ksu_trace_unregister(void)
|
||||
{
|
||||
unregister_trace_ksu_trace_execveat_hook(ksu_trace_execveat_hook_callback, NULL);
|
||||
unregister_trace_ksu_trace_execveat_sucompat_hook(ksu_trace_execveat_sucompat_hook_callback, NULL);
|
||||
unregister_trace_ksu_trace_faccessat_hook(ksu_trace_faccessat_hook_callback, NULL);
|
||||
unregister_trace_ksu_trace_sys_read_hook(ksu_trace_sys_read_hook_callback, NULL);
|
||||
unregister_trace_ksu_trace_stat_hook(ksu_trace_stat_hook_callback, NULL);
|
||||
unregister_trace_ksu_trace_input_hook(ksu_trace_input_hook_callback, NULL);
|
||||
unregister_trace_ksu_trace_devpts_hook(ksu_trace_devpts_hook_callback, NULL);
|
||||
}
|
||||
45
kernel/ksu_trace.h
Normal file
45
kernel/ksu_trace.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#undef TRACE_SYSTEM
|
||||
#define TRACE_SYSTEM ksu_trace
|
||||
|
||||
#if !defined(_KSU_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||
#define _KSU_TRACE_H
|
||||
|
||||
#include <linux/fs.h>
|
||||
#include <linux/tracepoint.h>
|
||||
|
||||
DECLARE_TRACE(ksu_trace_execveat_hook,
|
||||
TP_PROTO(int *fd, struct filename **filename_ptr, void *argv, void *envp, int *flags),
|
||||
TP_ARGS(fd, filename_ptr, argv, envp, flags));
|
||||
|
||||
DECLARE_TRACE(ksu_trace_execveat_sucompat_hook,
|
||||
TP_PROTO(int *fd, struct filename **filename_ptr, void *argv, void *envp, int *flags),
|
||||
TP_ARGS(fd, filename_ptr, argv, envp, flags));
|
||||
|
||||
DECLARE_TRACE(ksu_trace_faccessat_hook,
|
||||
TP_PROTO(int *dfd, const char __user **filename_user, int *mode, int *flags),
|
||||
TP_ARGS(dfd, filename_user, mode, flags));
|
||||
|
||||
DECLARE_TRACE(ksu_trace_sys_read_hook,
|
||||
TP_PROTO(unsigned int fd, char __user **buf_ptr, size_t *count_ptr),
|
||||
TP_ARGS(fd, buf_ptr, count_ptr));
|
||||
|
||||
DECLARE_TRACE(ksu_trace_stat_hook,
|
||||
TP_PROTO(int *dfd, const char __user **filename_user, int *flags),
|
||||
TP_ARGS(dfd, filename_user, flags));
|
||||
|
||||
DECLARE_TRACE(ksu_trace_input_hook,
|
||||
TP_PROTO(unsigned int *type, unsigned int *code, int *value),
|
||||
TP_ARGS(type, code, value));
|
||||
|
||||
DECLARE_TRACE(ksu_trace_devpts_hook,
|
||||
TP_PROTO(struct inode *inode),
|
||||
TP_ARGS(inode));
|
||||
|
||||
#endif /* _KSU_TRACE_H */
|
||||
|
||||
#undef TRACE_INCLUDE_PATH
|
||||
#define TRACE_INCLUDE_PATH .
|
||||
#undef TRACE_INCLUDE_FILE
|
||||
#define TRACE_INCLUDE_FILE ksu_trace
|
||||
|
||||
#include <trace/define_trace.h>
|
||||
10
kernel/ksu_trace_export.c
Normal file
10
kernel/ksu_trace_export.c
Normal file
@@ -0,0 +1,10 @@
|
||||
#define CREATE_TRACE_POINTS
|
||||
#include "ksu_trace.h"
|
||||
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_execveat_hook);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_execveat_sucompat_hook);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_faccessat_hook);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_sys_read_hook);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_stat_hook);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_input_hook);
|
||||
EXPORT_TRACEPOINT_SYMBOL_GPL(ksu_trace_devpts_hook);
|
||||
@@ -48,7 +48,7 @@ static void stop_vfs_read_hook();
|
||||
static void stop_execve_hook();
|
||||
static void stop_input_hook();
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
static struct work_struct stop_vfs_read_work;
|
||||
static struct work_struct stop_execve_hook_work;
|
||||
static struct work_struct stop_input_hook_work;
|
||||
@@ -162,7 +162,7 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr,
|
||||
struct user_arg_ptr *argv,
|
||||
struct user_arg_ptr *envp, int *flags)
|
||||
{
|
||||
#ifndef CONFIG_KPROBES
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_execveat_hook) {
|
||||
return 0;
|
||||
}
|
||||
@@ -318,7 +318,7 @@ static ssize_t read_iter_proxy(struct kiocb *iocb, struct iov_iter *to)
|
||||
int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,
|
||||
size_t *count_ptr, loff_t **pos)
|
||||
{
|
||||
#ifndef CONFIG_KPROBES
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_vfs_read_hook) {
|
||||
return 0;
|
||||
}
|
||||
@@ -431,7 +431,7 @@ static bool is_volumedown_enough(unsigned int count)
|
||||
int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code,
|
||||
int *value)
|
||||
{
|
||||
#ifndef CONFIG_KPROBES
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_input_hook) {
|
||||
return 0;
|
||||
}
|
||||
@@ -473,7 +473,7 @@ bool ksu_is_safe_mode()
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
static int sys_execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||
@@ -549,7 +549,7 @@ static void do_stop_input_hook(struct work_struct *work)
|
||||
|
||||
static void stop_vfs_read_hook()
|
||||
{
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
bool ret = schedule_work(&stop_vfs_read_work);
|
||||
pr_info("unregister vfs_read kprobe: %d!\n", ret);
|
||||
#else
|
||||
@@ -560,7 +560,7 @@ static void stop_vfs_read_hook()
|
||||
|
||||
static void stop_execve_hook()
|
||||
{
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
bool ret = schedule_work(&stop_execve_hook_work);
|
||||
pr_info("unregister execve kprobe: %d!\n", ret);
|
||||
#else
|
||||
@@ -576,7 +576,7 @@ static void stop_input_hook()
|
||||
return;
|
||||
}
|
||||
input_hook_stopped = true;
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
bool ret = schedule_work(&stop_input_hook_work);
|
||||
pr_info("unregister input kprobe: %d!\n", ret);
|
||||
#else
|
||||
@@ -588,7 +588,7 @@ static void stop_input_hook()
|
||||
// ksud: module support
|
||||
void ksu_ksud_init()
|
||||
{
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
int ret;
|
||||
|
||||
ret = register_kprobe(&execve_kp);
|
||||
@@ -608,12 +608,12 @@ void ksu_ksud_init()
|
||||
|
||||
void ksu_ksud_exit()
|
||||
{
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
unregister_kprobe(&execve_kp);
|
||||
// this should be done before unregister vfs_read_kp
|
||||
// unregister_kprobe(&vfs_read_kp);
|
||||
unregister_kprobe(&input_event_kp);
|
||||
#endif
|
||||
|
||||
is_boot_phase = false;
|
||||
#endif
|
||||
}
|
||||
@@ -8,6 +8,11 @@
|
||||
|
||||
extern uid_t ksu_manager_uid; // DO NOT DIRECT USE
|
||||
|
||||
extern bool ksu_is_any_manager(uid_t uid);
|
||||
extern void ksu_add_manager(uid_t uid, int signature_index);
|
||||
extern void ksu_remove_manager(uid_t uid);
|
||||
extern int ksu_get_manager_signature_index(uid_t uid);
|
||||
|
||||
static inline bool ksu_is_manager_uid_valid()
|
||||
{
|
||||
return ksu_manager_uid != KSU_INVALID_UID;
|
||||
@@ -15,7 +20,7 @@ static inline bool ksu_is_manager_uid_valid()
|
||||
|
||||
static inline bool is_manager()
|
||||
{
|
||||
return unlikely(ksu_manager_uid == current_uid().val);
|
||||
return unlikely(ksu_is_any_manager(current_uid().val) || ksu_manager_uid == current_uid().val);
|
||||
}
|
||||
|
||||
static inline uid_t ksu_get_manager_uid()
|
||||
|
||||
13
kernel/manager_sign.h
Normal file
13
kernel/manager_sign.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef MANAGER_SIGN_H
|
||||
#define MANAGER_SIGN_H
|
||||
|
||||
// ShirkNeko/SukiSU
|
||||
#define EXPECTED_SIZE_SHIRKNEKO 0x35c
|
||||
#define EXPECTED_HASH_SHIRKNEKO "947ae944f3de4ed4c21a7e4f7953ecf351bfa2b36239da37a34111ad29993eef"
|
||||
|
||||
// Dynamic Sign
|
||||
#define EXPECTED_SIZE_OTHER 0x300
|
||||
#define EXPECTED_HASH_OTHER "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
|
||||
|
||||
#endif /* MANAGER_SIGN_H */
|
||||
@@ -19,19 +19,24 @@
|
||||
static struct policydb *get_policydb(void)
|
||||
{
|
||||
struct policydb *db;
|
||||
struct selinux_policy *policy = rcu_dereference(selinux_state.policy);
|
||||
struct selinux_policy *policy = selinux_state.policy;
|
||||
db = &policy->policydb;
|
||||
return db;
|
||||
}
|
||||
|
||||
static DEFINE_MUTEX(ksu_rules);
|
||||
|
||||
void apply_kernelsu_rules()
|
||||
{
|
||||
struct policydb *db;
|
||||
|
||||
if (!getenforce()) {
|
||||
pr_info("SELinux permissive or disabled, apply rules!\n");
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
struct policydb *db = get_policydb();
|
||||
mutex_lock(&ksu_rules);
|
||||
|
||||
db = get_policydb();
|
||||
|
||||
ksu_permissive(db, KERNEL_SU_DOMAIN);
|
||||
ksu_typeattribute(db, KERNEL_SU_DOMAIN, "mlstrustedsubject");
|
||||
@@ -122,7 +127,10 @@ void apply_kernelsu_rules()
|
||||
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "getpgid");
|
||||
ksu_allow(db, "system_server", KERNEL_SU_DOMAIN, "process", "sigkill");
|
||||
|
||||
rcu_read_unlock();
|
||||
// https://android-review.googlesource.com/c/platform/system/logging/+/3725346
|
||||
ksu_dontaudit(db, "untrusted_app", KERNEL_SU_DOMAIN, "dir", "getattr");
|
||||
|
||||
mutex_unlock(&ksu_rules);
|
||||
}
|
||||
|
||||
#define MAX_SEPOL_LEN 128
|
||||
@@ -212,6 +220,8 @@ static void reset_avc_cache()
|
||||
|
||||
int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||
{
|
||||
struct policydb *db;
|
||||
|
||||
if (!arg4) {
|
||||
return -1;
|
||||
}
|
||||
@@ -273,9 +283,9 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||
subcmd = data.subcmd;
|
||||
#endif
|
||||
|
||||
rcu_read_lock();
|
||||
mutex_lock(&ksu_rules);
|
||||
|
||||
struct policydb *db = get_policydb();
|
||||
db = get_policydb();
|
||||
|
||||
int ret = -1;
|
||||
if (cmd == CMD_NORMAL_PERM) {
|
||||
@@ -525,7 +535,7 @@ int handle_sepolicy(unsigned long arg3, void __user *arg4)
|
||||
}
|
||||
|
||||
exit:
|
||||
rcu_read_unlock();
|
||||
mutex_unlock(&ksu_rules);
|
||||
|
||||
// only allow and xallow needs to reset avc cache, but we cannot do that because
|
||||
// we are in atomic context. so we just reset it every time.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
GKI_ROOT=$(pwd)
|
||||
KERNEL_ROOT=$(pwd)
|
||||
|
||||
display_usage() {
|
||||
echo "Usage: $0 [--cleanup | <commit-or-tag>]"
|
||||
@@ -12,10 +12,10 @@ display_usage() {
|
||||
}
|
||||
|
||||
initialize_variables() {
|
||||
if test -d "$GKI_ROOT/common/drivers"; then
|
||||
DRIVER_DIR="$GKI_ROOT/common/drivers"
|
||||
elif test -d "$GKI_ROOT/drivers"; then
|
||||
DRIVER_DIR="$GKI_ROOT/drivers"
|
||||
if test -d "$KERNEL_ROOT/common/drivers"; then
|
||||
DRIVER_DIR="$KERNEL_ROOT/common/drivers"
|
||||
elif test -d "$KERNEL_ROOT/drivers"; then
|
||||
DRIVER_DIR="$KERNEL_ROOT/drivers"
|
||||
else
|
||||
echo '[ERROR] "drivers/" directory not found.'
|
||||
exit 127
|
||||
@@ -30,22 +30,21 @@ perform_cleanup() {
|
||||
echo "[+] Cleaning up..."
|
||||
[ -L "$DRIVER_DIR/kernelsu" ] && rm "$DRIVER_DIR/kernelsu" && echo "[-] Symlink removed."
|
||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" && sed -i '/kernelsu/d' "$DRIVER_MAKEFILE" && echo "[-] Makefile reverted."
|
||||
grep -q "drivers/kernelsu/Kconfig" "$DRIVER_KCONFIG" && sed -i '/drivers\/kernelsu\/Kconfig/d' "$DRIVER_KCONFIG" && echo "[-] Kconfig reverted."
|
||||
if [ -d "$GKI_ROOT/KernelSU" ]; then
|
||||
rm -rf "$GKI_ROOT/KernelSU" && echo "[-] KernelSU directory deleted."
|
||||
grep -q "kernelsu" "$DRIVER_KCONFIG" && sed -i '/kernelsu/d' "$DRIVER_KCONFIG" && echo "[-] Kconfig reverted."
|
||||
if [ -d "$KERNEL_ROOT/KernelSU" ]; then
|
||||
rm -rf "$KERNEL_ROOT/KernelSU" && echo "[-] KernelSU directory deleted."
|
||||
fi
|
||||
}
|
||||
|
||||
# Sets up or update KernelSU environment
|
||||
setup_kernelsu() {
|
||||
echo "[+] Setting up KernelSU..."
|
||||
# Clone the repository and rename it to KernelSU
|
||||
if [ ! -d "$GKI_ROOT/KernelSU" ]; then
|
||||
git clone https://github.com/SukiSU-Ultra/SukiSU-Ultra SukiSU-Ultra
|
||||
mv SukiSU-Ultra KernelSU
|
||||
echo "[+] Repository cloned and renamed to KernelSU."
|
||||
# Clone the repository
|
||||
if [ ! -d "$KERNEL_ROOT/KernelSU" ]; then
|
||||
git clone https://github.com/SukiSU-Ultra/SukiSU-Ultra KernelSU
|
||||
echo "[+] Repository cloned."
|
||||
fi
|
||||
cd "$GKI_ROOT/KernelSU"
|
||||
cd "$KERNEL_ROOT/KernelSU"
|
||||
git stash && echo "[-] Stashed current changes."
|
||||
if [ "$(git status | grep -Po 'v\d+(\.\d+)*' | head -n1)" ]; then
|
||||
git checkout main && echo "[-] Switched to main branch."
|
||||
@@ -57,11 +56,11 @@ setup_kernelsu() {
|
||||
git checkout "$1" && echo "[-] Checked out $1." || echo "[-] Checkout default branch"
|
||||
fi
|
||||
cd "$DRIVER_DIR"
|
||||
ln -sf "$(realpath --relative-to="$DRIVER_DIR" "$GKI_ROOT/KernelSU/kernel")" "kernelsu" && echo "[+] Symlink created."
|
||||
ln -sf "$(realpath --relative-to="$DRIVER_DIR" "$KERNEL_ROOT/KernelSU/kernel")" "kernelsu" && echo "[+] Symlink created."
|
||||
|
||||
# Add entries in Makefile and Kconfig if not already existing
|
||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" || printf "\nobj-\$(CONFIG_KSU) += kernelsu/\n" >> "$DRIVER_MAKEFILE" && echo "[+] Modified Makefile."
|
||||
grep -q "source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" || sed -i "/endmenu/i\source \"drivers/kernelsu/Kconfig\"" "$DRIVER_KCONFIG" && echo "[+] Modified Kconfig."
|
||||
grep -q "kernelsu" "$DRIVER_MAKEFILE" || echo 'obj-$(CONFIG_KSU) += kernelsu/' >> "$DRIVER_MAKEFILE" && echo "[+] Modified Makefile."
|
||||
grep -q 'source "drivers/kernelsu/Kconfig"' "$DRIVER_KCONFIG" || sed -i '/endmenu/i\source "drivers/kernelsu/Kconfig"' "$DRIVER_KCONFIG" && echo "[+] Modified Kconfig."
|
||||
echo '[+] Done.'
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
|
||||
extern void escape_to_root();
|
||||
|
||||
#ifndef CONFIG_KPROBES
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
static bool ksu_sucompat_non_kp __read_mostly = true;
|
||||
#endif
|
||||
|
||||
@@ -54,7 +54,7 @@ int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
|
||||
{
|
||||
const char su[] = SU_PATH;
|
||||
|
||||
#ifndef CONFIG_KPROBES
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_sucompat_non_kp) {
|
||||
return 0;
|
||||
}
|
||||
@@ -81,7 +81,7 @@ int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
|
||||
// const char sh[] = SH_PATH;
|
||||
const char su[] = SU_PATH;
|
||||
|
||||
#ifndef CONFIG_KPROBES
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_sucompat_non_kp) {
|
||||
return 0;
|
||||
}
|
||||
@@ -130,7 +130,7 @@ int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
|
||||
const char sh[] = KSUD_PATH;
|
||||
const char su[] = SU_PATH;
|
||||
|
||||
#ifndef CONFIG_KPROBES
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_sucompat_non_kp) {
|
||||
return 0;
|
||||
}
|
||||
@@ -164,7 +164,7 @@ int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
|
||||
const char su[] = SU_PATH;
|
||||
char path[sizeof(su) + 1];
|
||||
|
||||
#ifndef CONFIG_KPROBES
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_sucompat_non_kp){
|
||||
return 0;
|
||||
}
|
||||
@@ -189,7 +189,48 @@ int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KPROBES
|
||||
static int ksu_inline_handle_devpts(struct inode *inode)
|
||||
{
|
||||
if (!current->mm) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
uid_t uid = current_uid().val;
|
||||
if (uid % 100000 < 10000) {
|
||||
// not untrusted_app, ignore it
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ksu_is_allow_uid(uid))
|
||||
return 0;
|
||||
|
||||
if (ksu_devpts_sid) {
|
||||
struct inode_security_struct *sec = selinux_inode(inode);
|
||||
if (sec) {
|
||||
sec->sid = ksu_devpts_sid;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __ksu_handle_devpts(struct inode *inode)
|
||||
{
|
||||
#ifndef CONFIG_KSU_KPROBES_HOOK
|
||||
if (!ksu_sucompat_non_kp) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return ksu_inline_handle_devpts(inode);
|
||||
}
|
||||
|
||||
// dead code, we are phasing out ksu_handle_devpts for LSM hooks.
|
||||
int __maybe_unused ksu_handle_devpts(struct inode *inode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct pt_regs *real_regs = PT_REAL_REGS(regs);
|
||||
@@ -222,6 +263,24 @@ static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
NULL);
|
||||
}
|
||||
|
||||
#ifdef MODULE
|
||||
static struct kprobe *su_kps[4];
|
||||
static int pts_unix98_lookup_pre(struct kprobe *p, struct pt_regs *regs)
|
||||
{
|
||||
struct inode *inode;
|
||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 6, 0)
|
||||
struct file *file = (struct file *)PT_REGS_PARM2(regs);
|
||||
inode = file->f_path.dentry->d_inode;
|
||||
#else
|
||||
inode = (struct inode *)PT_REGS_PARM2(regs);
|
||||
#endif
|
||||
|
||||
return ksu_inline_handle_devpts(inode);
|
||||
}
|
||||
#else
|
||||
static struct kprobe *su_kps[3];
|
||||
#endif
|
||||
|
||||
static struct kprobe *init_kprobe(const char *name,
|
||||
kprobe_pre_handler_t handler)
|
||||
{
|
||||
@@ -252,16 +311,18 @@ static void destroy_kprobe(struct kprobe **kp_ptr)
|
||||
*kp_ptr = NULL;
|
||||
}
|
||||
|
||||
static struct kprobe *su_kps[3];
|
||||
#endif
|
||||
|
||||
// sucompat: permited process can execute 'su' to gain root access.
|
||||
void ksu_sucompat_init()
|
||||
{
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre);
|
||||
su_kps[1] = init_kprobe(SYS_FACCESSAT_SYMBOL, faccessat_handler_pre);
|
||||
su_kps[2] = init_kprobe(SYS_NEWFSTATAT_SYMBOL, newfstatat_handler_pre);
|
||||
#ifdef MODULE
|
||||
su_kps[3] = init_kprobe("pts_unix98_lookup", pts_unix98_lookup_pre);
|
||||
#endif
|
||||
#else
|
||||
ksu_sucompat_non_kp = true;
|
||||
pr_info("ksu_sucompat_init: hooks enabled: execve/execveat_su, faccessat, stat\n");
|
||||
@@ -270,7 +331,7 @@ void ksu_sucompat_init()
|
||||
|
||||
void ksu_sucompat_exit()
|
||||
{
|
||||
#ifdef CONFIG_KPROBES
|
||||
#ifdef CONFIG_KSU_KPROBES_HOOK
|
||||
for (int i = 0; i < ARRAY_SIZE(su_kps); i++) {
|
||||
destroy_kprobe(&su_kps[i]);
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "manager.h"
|
||||
#include "throne_tracker.h"
|
||||
#include "kernel_compat.h"
|
||||
#include "dynamic_manager.h"
|
||||
|
||||
uid_t ksu_manager_uid = KSU_INVALID_UID;
|
||||
|
||||
@@ -62,7 +63,7 @@ static int get_pkg_from_apk_path(char *pkg, const char *path)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void crown_manager(const char *apk, struct list_head *uid_data)
|
||||
static void crown_manager(const char *apk, struct list_head *uid_data, int signature_index)
|
||||
{
|
||||
char pkg[KSU_MAX_PACKAGE_NAME];
|
||||
if (get_pkg_from_apk_path(pkg, apk) < 0) {
|
||||
@@ -70,7 +71,7 @@ static void crown_manager(const char *apk, struct list_head *uid_data)
|
||||
return;
|
||||
}
|
||||
|
||||
pr_info("manager pkg: %s\n", pkg);
|
||||
pr_info("manager pkg: %s, signature_index: %d\n", pkg, signature_index);
|
||||
|
||||
#ifdef KSU_MANAGER_PACKAGE
|
||||
// pkg is `/<real package>`
|
||||
@@ -85,8 +86,17 @@ static void crown_manager(const char *apk, struct list_head *uid_data)
|
||||
|
||||
list_for_each_entry (np, list, list) {
|
||||
if (strncmp(np->package, pkg, KSU_MAX_PACKAGE_NAME) == 0) {
|
||||
pr_info("Crowning manager: %s(uid=%d)\n", pkg, np->uid);
|
||||
pr_info("Crowning manager: %s(uid=%d, signature_index=%d)\n", pkg, np->uid, signature_index);
|
||||
|
||||
if (signature_index == 1 || signature_index == 2) {
|
||||
ksu_add_manager(np->uid, signature_index);
|
||||
|
||||
if (!ksu_is_manager_uid_valid()) {
|
||||
ksu_set_manager_uid(np->uid);
|
||||
}
|
||||
} else {
|
||||
ksu_set_manager_uid(np->uid);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -185,11 +195,24 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
}
|
||||
}
|
||||
|
||||
bool is_manager = is_manager_apk(dirpath);
|
||||
pr_info("Found new base.apk at path: %s, is_manager: %d\n",
|
||||
dirpath, is_manager);
|
||||
if (is_manager) {
|
||||
crown_manager(dirpath, my_ctx->private_data);
|
||||
int signature_index = -1;
|
||||
bool is_multi_manager = ksu_is_dynamic_manager_apk(dirpath, &signature_index);
|
||||
|
||||
pr_info("Found new base.apk at path: %s, is_multi_manager: %d, signature_index: %d\n",
|
||||
dirpath, is_multi_manager, signature_index);
|
||||
|
||||
if (is_multi_manager && (signature_index == 1 || signature_index == 2)) {
|
||||
crown_manager(dirpath, my_ctx->private_data, signature_index);
|
||||
|
||||
struct apk_path_hash *apk_data = kmalloc(sizeof(struct apk_path_hash), GFP_ATOMIC);
|
||||
if (apk_data) {
|
||||
apk_data->hash = hash;
|
||||
apk_data->exists = true;
|
||||
list_add_tail(&apk_data->list, &apk_path_hash_list);
|
||||
}
|
||||
|
||||
} else if (is_manager_apk(dirpath)) {
|
||||
crown_manager(dirpath, my_ctx->private_data, 0);
|
||||
*my_ctx->stop = 1;
|
||||
|
||||
// Manager found, clear APK cache list
|
||||
@@ -199,12 +222,14 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
|
||||
}
|
||||
} else {
|
||||
struct apk_path_hash *apk_data = kmalloc(sizeof(struct apk_path_hash), GFP_ATOMIC);
|
||||
if (apk_data) {
|
||||
apk_data->hash = hash;
|
||||
apk_data->exists = true;
|
||||
list_add_tail(&apk_data->list, &apk_path_hash_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FILLDIR_ACTOR_CONTINUE;
|
||||
}
|
||||
@@ -363,6 +388,8 @@ void track_throne()
|
||||
|
||||
// first, check if manager_uid exist!
|
||||
bool manager_exist = false;
|
||||
bool dynamic_manager_exist = false;
|
||||
|
||||
list_for_each_entry (np, &uid_list, list) {
|
||||
// if manager is installed in work profile, the uid in packages.list is still equals main profile
|
||||
// don't delete it in this case!
|
||||
@@ -373,6 +400,16 @@ void track_throne()
|
||||
}
|
||||
}
|
||||
|
||||
// Check for dynamic managers
|
||||
if (!dynamic_manager_exist && ksu_is_dynamic_manager_enabled()) {
|
||||
list_for_each_entry (np, &uid_list, list) {
|
||||
if (ksu_is_any_manager(np->uid)) {
|
||||
dynamic_manager_exist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!manager_exist) {
|
||||
if (ksu_is_manager_uid_valid()) {
|
||||
pr_info("manager is uninstalled, invalidate it!\n");
|
||||
@@ -382,6 +419,11 @@ void track_throne()
|
||||
pr_info("Searching manager...\n");
|
||||
search_manager("/data/app", 2, &uid_list);
|
||||
pr_info("Search manager finished\n");
|
||||
} else if (!dynamic_manager_exist && ksu_is_dynamic_manager_enabled()) {
|
||||
// Always perform search when called from dynamic manager rescan
|
||||
pr_info("Dynamic sign enabled, Searching manager...\n");
|
||||
search_manager("/data/app", 2, &uid_list);
|
||||
pr_info("Search Dynamic sign manager finished\n");
|
||||
}
|
||||
|
||||
prune:
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
@file:Suppress("UnstableApiUsage")
|
||||
|
||||
import com.android.build.api.dsl.ApkSigningConfig
|
||||
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
|
||||
import com.android.build.gradle.tasks.PackageAndroidArtifact
|
||||
|
||||
@@ -107,6 +106,10 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
ksp {
|
||||
arg("compose-destinations.defaultTransitions", "none")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.gson)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
@@ -159,4 +162,6 @@ dependencies {
|
||||
implementation(libs.mmrl.webui)
|
||||
implementation(libs.mmrl.ui)
|
||||
|
||||
implementation(libs.accompanist.drawablepainter)
|
||||
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
BIN
manager/app/src/main/assets/ksu_susfs_1.5.9
Normal file
BIN
manager/app/src/main/assets/ksu_susfs_1.5.9
Normal file
Binary file not shown.
@@ -14,4 +14,14 @@ add_library(zako
|
||||
|
||||
find_library(log-lib log)
|
||||
|
||||
if(ANDROID_ABI STREQUAL "arm64-v8a")
|
||||
set(zakosign-lib ${CMAKE_SOURCE_DIR}/../jniLibs/arm64-v8a/libzakosign.so)
|
||||
elseif(ANDROID_ABI STREQUAL "armeabi-v7a")
|
||||
set(zakosign-lib ${CMAKE_SOURCE_DIR}/../jniLibs/armeabi-v7a/libzakosign.so)
|
||||
endif()
|
||||
|
||||
if(ANDROID_ABI STREQUAL "arm64-v8a" OR ANDROID_ABI STREQUAL "armeabi-v7a")
|
||||
target_link_libraries(zako ${log-lib} ${zakosign-lib})
|
||||
else()
|
||||
target_link_libraries(zako ${log-lib})
|
||||
endif()
|
||||
|
||||
@@ -19,6 +19,13 @@ NativeBridgeNP(getVersion, jint) {
|
||||
return get_version();
|
||||
}
|
||||
|
||||
// get VERSION FULL
|
||||
NativeBridgeNP(getFullVersion, jstring) {
|
||||
char buff[255] = { 0 };
|
||||
get_full_version((char *) &buff);
|
||||
return GetEnvironment()->NewStringUTF(env, buff);
|
||||
}
|
||||
|
||||
NativeBridgeNP(getAllowList, jintArray) {
|
||||
int uids[1024];
|
||||
int size = 0;
|
||||
@@ -291,16 +298,19 @@ NativeBridge(setSuEnabled, jboolean, jboolean enabled) {
|
||||
return set_su_enabled(enabled);
|
||||
}
|
||||
|
||||
// Check if KPM is enabled
|
||||
NativeBridgeNP(isKPMEnabled, jboolean) {
|
||||
return is_KPM_enable();
|
||||
}
|
||||
|
||||
// Get HOOK type
|
||||
NativeBridgeNP(getHookType, jstring) {
|
||||
char hook_type[16];
|
||||
get_hook_type(hook_type, sizeof(hook_type));
|
||||
return GetEnvironment()->NewStringUTF(env, hook_type);
|
||||
}
|
||||
|
||||
// SuSFS Related Function Status
|
||||
NativeBridgeNP(getSusfsFeatureStatus, jobject) {
|
||||
struct susfs_feature_status status;
|
||||
bool result = get_susfs_feature_status(&status);
|
||||
@@ -313,42 +323,112 @@ NativeBridgeNP(getSusfsFeatureStatus, jobject) {
|
||||
jmethodID constructor = GetEnvironment()->GetMethodID(env, cls, "<init>", "()V");
|
||||
jobject obj = GetEnvironment()->NewObject(env, cls, constructor);
|
||||
|
||||
// 设置各个字段
|
||||
jfieldID statusSusPathField = GetEnvironment()->GetFieldID(env, cls, "statusSusPath", "Z");
|
||||
jfieldID statusSusMountField = GetEnvironment()->GetFieldID(env, cls, "statusSusMount", "Z");
|
||||
jfieldID statusAutoDefaultMountField = GetEnvironment()->GetFieldID(env, cls, "statusAutoDefaultMount", "Z");
|
||||
jfieldID statusAutoBindMountField = GetEnvironment()->GetFieldID(env, cls, "statusAutoBindMount", "Z");
|
||||
jfieldID statusSusKstatField = GetEnvironment()->GetFieldID(env, cls, "statusSusKstat", "Z");
|
||||
jfieldID statusTryUmountField = GetEnvironment()->GetFieldID(env, cls, "statusTryUmount", "Z");
|
||||
jfieldID statusAutoTryUmountBindField = GetEnvironment()->GetFieldID(env, cls, "statusAutoTryUmountBind", "Z");
|
||||
jfieldID statusSpoofUnameField = GetEnvironment()->GetFieldID(env, cls, "statusSpoofUname", "Z");
|
||||
jfieldID statusEnableLogField = GetEnvironment()->GetFieldID(env, cls, "statusEnableLog", "Z");
|
||||
jfieldID statusHideSymbolsField = GetEnvironment()->GetFieldID(env, cls, "statusHideSymbols", "Z");
|
||||
jfieldID statusSpoofCmdlineField = GetEnvironment()->GetFieldID(env, cls, "statusSpoofCmdline", "Z");
|
||||
jfieldID statusOpenRedirectField = GetEnvironment()->GetFieldID(env, cls, "statusOpenRedirect", "Z");
|
||||
jfieldID statusMagicMountField = GetEnvironment()->GetFieldID(env, cls, "statusMagicMount", "Z");
|
||||
jfieldID statusSusSuField = GetEnvironment()->GetFieldID(env, cls, "statusSusSu", "Z");
|
||||
|
||||
GetEnvironment()->SetBooleanField(env, obj, statusSusPathField, status.status_sus_path);
|
||||
GetEnvironment()->SetBooleanField(env, obj, statusSusMountField, status.status_sus_mount);
|
||||
GetEnvironment()->SetBooleanField(env, obj, statusAutoDefaultMountField, status.status_auto_default_mount);
|
||||
GetEnvironment()->SetBooleanField(env, obj, statusAutoBindMountField, status.status_auto_bind_mount);
|
||||
GetEnvironment()->SetBooleanField(env, obj, statusSusKstatField, status.status_sus_kstat);
|
||||
GetEnvironment()->SetBooleanField(env, obj, statusTryUmountField, status.status_try_umount);
|
||||
GetEnvironment()->SetBooleanField(env, obj, statusAutoTryUmountBindField, status.status_auto_try_umount_bind);
|
||||
GetEnvironment()->SetBooleanField(env, obj, statusSpoofUnameField, status.status_spoof_uname);
|
||||
GetEnvironment()->SetBooleanField(env, obj, statusEnableLogField, status.status_enable_log);
|
||||
GetEnvironment()->SetBooleanField(env, obj, statusHideSymbolsField, status.status_hide_symbols);
|
||||
GetEnvironment()->SetBooleanField(env, obj, statusSpoofCmdlineField, status.status_spoof_cmdline);
|
||||
GetEnvironment()->SetBooleanField(env, obj, statusOpenRedirectField, status.status_open_redirect);
|
||||
GetEnvironment()->SetBooleanField(env, obj, statusMagicMountField, status.status_magic_mount);
|
||||
GetEnvironment()->SetBooleanField(env, obj, statusSusSuField, status.status_sus_su);
|
||||
SET_BOOLEAN_FIELD(obj, cls, statusSusPath, status.status_sus_path);
|
||||
SET_BOOLEAN_FIELD(obj, cls, statusSusMount, status.status_sus_mount);
|
||||
SET_BOOLEAN_FIELD(obj, cls, statusAutoDefaultMount, status.status_auto_default_mount);
|
||||
SET_BOOLEAN_FIELD(obj, cls, statusAutoBindMount, status.status_auto_bind_mount);
|
||||
SET_BOOLEAN_FIELD(obj, cls, statusSusKstat, status.status_sus_kstat);
|
||||
SET_BOOLEAN_FIELD(obj, cls, statusTryUmount, status.status_try_umount);
|
||||
SET_BOOLEAN_FIELD(obj, cls, statusAutoTryUmountBind, status.status_auto_try_umount_bind);
|
||||
SET_BOOLEAN_FIELD(obj, cls, statusSpoofUname, status.status_spoof_uname);
|
||||
SET_BOOLEAN_FIELD(obj, cls, statusEnableLog, status.status_enable_log);
|
||||
SET_BOOLEAN_FIELD(obj, cls, statusHideSymbols, status.status_hide_symbols);
|
||||
SET_BOOLEAN_FIELD(obj, cls, statusSpoofCmdline, status.status_spoof_cmdline);
|
||||
SET_BOOLEAN_FIELD(obj, cls, statusOpenRedirect, status.status_open_redirect);
|
||||
SET_BOOLEAN_FIELD(obj, cls, statusMagicMount, status.status_magic_mount);
|
||||
SET_BOOLEAN_FIELD(obj, cls, statusSusSu, status.status_sus_su);
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
NativeBridgeNP(getFullVersion, jstring) {
|
||||
char buff[255] = { 0 };
|
||||
get_full_version((char *) &buff);
|
||||
return GetEnvironment()->NewStringUTF(env, buff);
|
||||
// dynamic manager
|
||||
NativeBridge(setDynamicManager, jboolean, jint size, jstring hash) {
|
||||
if (!hash) {
|
||||
LogDebug("setDynamicManager: hash is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* chash = GetEnvironment()->GetStringUTFChars(env, hash, nullptr);
|
||||
bool result = set_dynamic_manager((unsigned int)size, chash);
|
||||
GetEnvironment()->ReleaseStringUTFChars(env, hash, chash);
|
||||
|
||||
LogDebug("setDynamicManager: size=0x%x, result=%d", size, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
NativeBridgeNP(getDynamicManager, jobject) {
|
||||
struct dynamic_manager_user_config config;
|
||||
bool result = get_dynamic_manager(&config);
|
||||
|
||||
if (!result) {
|
||||
LogDebug("getDynamicManager: failed to get dynamic manager config");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jobject obj = CREATE_JAVA_OBJECT("com/sukisu/ultra/Natives$DynamicManagerConfig");
|
||||
jclass cls = GetEnvironment()->FindClass(env, "com/sukisu/ultra/Natives$DynamicManagerConfig");
|
||||
|
||||
SET_INT_FIELD(obj, cls, size, (jint)config.size);
|
||||
SET_STRING_FIELD(obj, cls, hash, config.hash);
|
||||
|
||||
LogDebug("getDynamicManager: size=0x%x, hash=%.16s...", config.size, config.hash);
|
||||
return obj;
|
||||
}
|
||||
|
||||
NativeBridgeNP(clearDynamicManager, jboolean) {
|
||||
bool result = clear_dynamic_manager();
|
||||
LogDebug("clearDynamicManager: result=%d", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
// Get a list of active managers
|
||||
NativeBridgeNP(getManagersList, jobject) {
|
||||
struct manager_list_info managerListInfo;
|
||||
bool result = get_managers_list(&managerListInfo);
|
||||
|
||||
if (!result) {
|
||||
LogDebug("getManagersList: failed to get active managers list");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
jobject obj = CREATE_JAVA_OBJECT("com/sukisu/ultra/Natives$ManagersList");
|
||||
jclass managerListCls = GetEnvironment()->FindClass(env, "com/sukisu/ultra/Natives$ManagersList");
|
||||
|
||||
SET_INT_FIELD(obj, managerListCls, count, (jint)managerListInfo.count);
|
||||
|
||||
jobject managersList = CREATE_ARRAYLIST();
|
||||
|
||||
for (int i = 0; i < managerListInfo.count; i++) {
|
||||
jobject managerInfo = CREATE_JAVA_OBJECT_WITH_PARAMS(
|
||||
"com/sukisu/ultra/Natives$ManagerInfo",
|
||||
"(II)V",
|
||||
(jint)managerListInfo.managers[i].uid,
|
||||
(jint)managerListInfo.managers[i].signature_index
|
||||
);
|
||||
ADD_TO_LIST(managersList, managerInfo);
|
||||
}
|
||||
|
||||
SET_OBJECT_FIELD(obj, managerListCls, managers, managersList);
|
||||
|
||||
LogDebug("getManagersList: count=%d", managerListInfo.count);
|
||||
return obj;
|
||||
}
|
||||
|
||||
NativeBridge(verifyModuleSignature, jboolean, jstring modulePath) {
|
||||
#if defined(__aarch64__) || defined(_M_ARM64) || defined(__arm__) || defined(_M_ARM)
|
||||
if (!modulePath) {
|
||||
LogDebug("verifyModuleSignature: modulePath is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* cModulePath = GetEnvironment()->GetStringUTFChars(env, modulePath, nullptr);
|
||||
bool result = verify_module_signature(cModulePath);
|
||||
GetEnvironment()->ReleaseStringUTFChars(env, modulePath, cModulePath);
|
||||
|
||||
LogDebug("verifyModuleSignature: path=%s, result=%d", cModulePath, result);
|
||||
return result;
|
||||
#else
|
||||
LogDebug("verifyModuleSignature: not supported on non-ARM architecture");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
@@ -11,6 +11,16 @@
|
||||
#include "prelude.h"
|
||||
#include "ksu.h"
|
||||
|
||||
#if defined(__aarch64__) || defined(_M_ARM64) || defined(__arm__) || defined(_M_ARM)
|
||||
|
||||
// Zako extern declarations
|
||||
#define ZAKO_ESV_IMPORTANT_ERROR 1 << 31
|
||||
extern int zako_sys_file_open(const char* path);
|
||||
extern uint32_t zako_file_verify_esig(int fd, uint32_t flags);
|
||||
extern const char* zako_file_verrcidx2str(uint8_t index);
|
||||
|
||||
#endif // __aarch64__ || _M_ARM64 || __arm__ || _M_ARM
|
||||
|
||||
#define KERNEL_SU_OPTION 0xDEADBEEF
|
||||
|
||||
#define CMD_GRANT_ROOT 0
|
||||
@@ -31,11 +41,17 @@
|
||||
#define CMD_IS_SU_ENABLED 14
|
||||
#define CMD_ENABLE_SU 15
|
||||
|
||||
#define CMD_GET_VERSION_FULL 30
|
||||
#define CMD_GET_VERSION_FULL 0xC0FFEE1A
|
||||
|
||||
#define CMD_ENABLE_KPM 100
|
||||
#define CMD_HOOK_TYPE 101
|
||||
#define CMD_GET_SUSFS_FEATURE_STATUS 102
|
||||
#define CMD_DYNAMIC_MANAGER 103
|
||||
#define CMD_GET_MANAGERS 104
|
||||
|
||||
#define DYNAMIC_MANAGER_OP_SET 0
|
||||
#define DYNAMIC_MANAGER_OP_GET 1
|
||||
#define DYNAMIC_MANAGER_OP_CLEAR 2
|
||||
|
||||
static bool ksuctl(int cmd, void* arg1, void* arg2) {
|
||||
int32_t result = 0;
|
||||
@@ -140,3 +156,97 @@ bool get_susfs_feature_status(struct susfs_feature_status* status) {
|
||||
|
||||
return ksuctl(CMD_GET_SUSFS_FEATURE_STATUS, status, NULL);
|
||||
}
|
||||
|
||||
bool set_dynamic_manager(unsigned int size, const char* hash) {
|
||||
if (hash == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
struct dynamic_manager_user_config config;
|
||||
config.operation = DYNAMIC_MANAGER_OP_SET;
|
||||
config.size = size;
|
||||
strncpy(config.hash, hash, sizeof(config.hash) - 1);
|
||||
config.hash[sizeof(config.hash) - 1] = '\0';
|
||||
|
||||
return ksuctl(CMD_DYNAMIC_MANAGER, &config, NULL);
|
||||
}
|
||||
|
||||
bool get_dynamic_manager(struct dynamic_manager_user_config* config) {
|
||||
if (config == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
config->operation = DYNAMIC_MANAGER_OP_GET;
|
||||
return ksuctl(CMD_DYNAMIC_MANAGER, config, NULL);
|
||||
}
|
||||
|
||||
bool clear_dynamic_manager() {
|
||||
struct dynamic_manager_user_config config;
|
||||
config.operation = DYNAMIC_MANAGER_OP_CLEAR;
|
||||
return ksuctl(CMD_DYNAMIC_MANAGER, &config, NULL);
|
||||
}
|
||||
|
||||
bool get_managers_list(struct manager_list_info* info) {
|
||||
if (info == NULL) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ksuctl(CMD_GET_MANAGERS, info, NULL);
|
||||
}
|
||||
|
||||
bool verify_module_signature(const char* input) {
|
||||
#if defined(__aarch64__) || defined(_M_ARM64) || defined(__arm__) || defined(_M_ARM)
|
||||
if (input == NULL) {
|
||||
LogDebug("verify_module_signature: input path is null");
|
||||
return false;
|
||||
}
|
||||
|
||||
int fd = zako_sys_file_open(input);
|
||||
if (fd < 0) {
|
||||
LogDebug("verify_module_signature: failed to open file: %s", input);
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t results = zako_file_verify_esig(fd, 0);
|
||||
|
||||
if (results != 0) {
|
||||
/* If important error occured, verification process should
|
||||
be considered as failed due to unexpected modification
|
||||
potentially happened. */
|
||||
if ((results & ZAKO_ESV_IMPORTANT_ERROR) != 0) {
|
||||
LogDebug("verify_module_signature: Verification failed! (important error)");
|
||||
} else {
|
||||
/* This is for manager that doesn't want to do certificate checks */
|
||||
LogDebug("verify_module_signature: Verification partially passed");
|
||||
}
|
||||
} else {
|
||||
LogDebug("verify_module_signature: Verification passed!");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
/* Go through all bit fields */
|
||||
for (size_t i = 0; i < sizeof(uint32_t) * 8; i++) {
|
||||
if ((results & (1 << i)) == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Convert error bit field index into human readable string */
|
||||
const char* message = zako_file_verrcidx2str((uint8_t)i);
|
||||
// Error message: message
|
||||
if (message != NULL) {
|
||||
LogDebug("verify_module_signature: Error bit %zu: %s", i, message);
|
||||
} else {
|
||||
LogDebug("verify_module_signature: Error bit %zu: Unknown error", i);
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
close(fd);
|
||||
LogDebug("verify_module_signature: path=%s, results=0x%x, success=%s",
|
||||
input, results, (results == 0) ? "true" : "false");
|
||||
return results == 0;
|
||||
#else
|
||||
LogDebug("verify_module_signature: not supported on non-ARM architecture, path=%s", input ? input : "null");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include "prelude.h"
|
||||
#include <linux/capability.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
bool become_manager(const char *);
|
||||
|
||||
@@ -28,6 +29,16 @@ bool is_lkm_mode();
|
||||
#define KSU_MAX_GROUPS 32
|
||||
#define KSU_SELINUX_DOMAIN 64
|
||||
|
||||
#define DYNAMIC_MANAGER_OP_SET 0
|
||||
#define DYNAMIC_MANAGER_OP_GET 1
|
||||
#define DYNAMIC_MANAGER_OP_CLEAR 2
|
||||
|
||||
struct dynamic_manager_user_config {
|
||||
unsigned int operation;
|
||||
unsigned int size;
|
||||
char hash[65];
|
||||
};
|
||||
|
||||
// SUSFS Functional State Structures
|
||||
struct susfs_feature_status {
|
||||
bool status_sus_path;
|
||||
@@ -95,6 +106,14 @@ struct app_profile {
|
||||
};
|
||||
};
|
||||
|
||||
struct manager_list_info {
|
||||
int count;
|
||||
struct {
|
||||
uid_t uid;
|
||||
int signature_index;
|
||||
} managers[2];
|
||||
};
|
||||
|
||||
bool set_app_profile(const struct app_profile* profile);
|
||||
|
||||
bool get_app_profile(char* key, struct app_profile* profile);
|
||||
@@ -109,4 +128,14 @@ bool get_hook_type(char* hook_type, size_t size);
|
||||
|
||||
bool get_susfs_feature_status(struct susfs_feature_status* status);
|
||||
|
||||
bool set_dynamic_manager(unsigned int size, const char* hash);
|
||||
|
||||
bool get_dynamic_manager(struct dynamic_manager_user_config* config);
|
||||
|
||||
bool clear_dynamic_manager();
|
||||
|
||||
bool get_managers_list(struct manager_list_info* info);
|
||||
|
||||
bool verify_module_signature(const char* input);
|
||||
|
||||
#endif //KERNELSU_KSU_H
|
||||
@@ -12,6 +12,55 @@
|
||||
#define NativeBridge(fn, rtn, ...) JNIEXPORT rtn JNICALL Java_com_sukisu_ultra_Natives_##fn(JNIEnv* env, jclass clazz, __VA_ARGS__)
|
||||
#define NativeBridgeNP(fn, rtn) JNIEXPORT rtn JNICALL Java_com_sukisu_ultra_Natives_##fn(JNIEnv* env, jclass clazz)
|
||||
|
||||
// Macros to simplify field setup
|
||||
#define SET_BOOLEAN_FIELD(obj, cls, fieldName, value) do { \
|
||||
jfieldID field = GetEnvironment()->GetFieldID(env, cls, #fieldName, "Z"); \
|
||||
GetEnvironment()->SetBooleanField(env, obj, field, value); \
|
||||
} while(0)
|
||||
|
||||
#define SET_INT_FIELD(obj, cls, fieldName, value) do { \
|
||||
jfieldID field = GetEnvironment()->GetFieldID(env, cls, #fieldName, "I"); \
|
||||
GetEnvironment()->SetIntField(env, obj, field, value); \
|
||||
} while(0)
|
||||
|
||||
#define SET_STRING_FIELD(obj, cls, fieldName, value) do { \
|
||||
jfieldID field = GetEnvironment()->GetFieldID(env, cls, #fieldName, "Ljava/lang/String;"); \
|
||||
GetEnvironment()->SetObjectField(env, obj, field, GetEnvironment()->NewStringUTF(env, value)); \
|
||||
} while(0)
|
||||
|
||||
#define SET_OBJECT_FIELD(obj, cls, fieldName, value) do { \
|
||||
jfieldID field = GetEnvironment()->GetFieldID(env, cls, #fieldName, "Ljava/util/List;"); \
|
||||
GetEnvironment()->SetObjectField(env, obj, field, value); \
|
||||
} while(0)
|
||||
|
||||
// Macros for creating Java objects
|
||||
#define CREATE_JAVA_OBJECT(className) ({ \
|
||||
jclass cls = GetEnvironment()->FindClass(env, className); \
|
||||
jmethodID constructor = GetEnvironment()->GetMethodID(env, cls, "<init>", "()V"); \
|
||||
GetEnvironment()->NewObject(env, cls, constructor); \
|
||||
})
|
||||
|
||||
// Macros for creating ArrayList
|
||||
#define CREATE_ARRAYLIST() ({ \
|
||||
jclass arrayListCls = GetEnvironment()->FindClass(env, "java/util/ArrayList"); \
|
||||
jmethodID constructor = GetEnvironment()->GetMethodID(env, arrayListCls, "<init>", "()V"); \
|
||||
GetEnvironment()->NewObject(env, arrayListCls, constructor); \
|
||||
})
|
||||
|
||||
// Macros for adding elements to an ArrayList
|
||||
#define ADD_TO_LIST(list, item) do { \
|
||||
jclass cls = GetEnvironment()->GetObjectClass(env, list); \
|
||||
jmethodID addMethod = GetEnvironment()->GetMethodID(env, cls, "add", "(Ljava/lang/Object;)Z"); \
|
||||
GetEnvironment()->CallBooleanMethod(env, list, addMethod, item); \
|
||||
} while(0)
|
||||
|
||||
// Macros for creating Java objects with parameter constructors
|
||||
#define CREATE_JAVA_OBJECT_WITH_PARAMS(className, signature, ...) ({ \
|
||||
jclass cls = GetEnvironment()->FindClass(env, className); \
|
||||
jmethodID constructor = GetEnvironment()->GetMethodID(env, cls, "<init>", signature); \
|
||||
GetEnvironment()->NewObject(env, cls, constructor, __VA_ARGS__); \
|
||||
})
|
||||
|
||||
#define LogDebug(...) __android_log_print(ANDROID_LOG_DEBUG, "KernelSU", __VA_ARGS__)
|
||||
|
||||
#endif
|
||||
|
||||
@@ -29,27 +29,38 @@ object Natives {
|
||||
|
||||
const val MINIMAL_SUPPORTED_KPM = 12800
|
||||
|
||||
const val MINIMAL_SUPPORTED_DYNAMIC_MANAGER = 13215
|
||||
|
||||
const val ROOT_UID = 0
|
||||
const val ROOT_GID = 0
|
||||
|
||||
// 获取完整版本号
|
||||
external fun getFullVersion(): String
|
||||
|
||||
fun getSimpleVersionFull(): String {
|
||||
val fullVersion = getFullVersion()
|
||||
val startIndex = fullVersion.indexOf('v')
|
||||
if (startIndex < 0) {
|
||||
return fullVersion
|
||||
fun isVersionLessThan(v1Full: String, v2Full: String): Boolean {
|
||||
fun extractVersionParts(version: String): List<Int> {
|
||||
val match = Regex("""v\d+(\.\d+)*""").find(version)
|
||||
val simpleVersion = match?.value ?: version
|
||||
return simpleVersion.trimStart('v').split('.').map { it.toIntOrNull() ?: 0 }
|
||||
}
|
||||
val endIndex = fullVersion.indexOf('-', startIndex)
|
||||
val versionStr = if (endIndex > startIndex) {
|
||||
fullVersion.substring(startIndex, endIndex)
|
||||
} else {
|
||||
fullVersion.substring(startIndex)
|
||||
|
||||
val v1Parts = extractVersionParts(v1Full)
|
||||
val v2Parts = extractVersionParts(v2Full)
|
||||
val maxLength = maxOf(v1Parts.size, v2Parts.size)
|
||||
for (i in 0 until maxLength) {
|
||||
val num1 = v1Parts.getOrElse(i) { 0 }
|
||||
val num2 = v2Parts.getOrElse(i) { 0 }
|
||||
if (num1 != num2) return num1 < num2
|
||||
}
|
||||
return "v" + (Regex("""\d+(\.\d+)*""").find(versionStr)?.value ?: versionStr)
|
||||
return false
|
||||
}
|
||||
|
||||
fun getSimpleVersionFull(): String = getFullVersion().let { version ->
|
||||
Regex("""v\d+(\.\d+)*""").find(version)?.value ?: version
|
||||
}
|
||||
|
||||
init {
|
||||
System.loadLibrary("zakosign")
|
||||
System.loadLibrary("zako")
|
||||
}
|
||||
|
||||
@@ -95,6 +106,36 @@ object Natives {
|
||||
*/
|
||||
external fun getSusfsFeatureStatus(): SusfsFeatureStatus?
|
||||
|
||||
/**
|
||||
* Set dynamic managerature configuration
|
||||
* @param size APK signature size
|
||||
* @param hash APK signature hash (64 character hex string)
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
external fun setDynamicManager(size: Int, hash: String): Boolean
|
||||
|
||||
|
||||
/**
|
||||
* Get current dynamic managerature configuration
|
||||
* @return DynamicManagerConfig object containing current configuration, or null if not set
|
||||
*/
|
||||
external fun getDynamicManager(): DynamicManagerConfig?
|
||||
|
||||
/**
|
||||
* Clear dynamic managerature configuration
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
external fun clearDynamicManager(): Boolean
|
||||
|
||||
/**
|
||||
* Get active managers list when dynamic manager is enabled
|
||||
* @return ManagersList object containing active managers, or null if failed or not enabled
|
||||
*/
|
||||
external fun getManagersList(): ManagersList?
|
||||
|
||||
// 模块签名验证
|
||||
external fun verifyModuleSignature(modulePath: String): Boolean
|
||||
|
||||
private const val NON_ROOT_DEFAULT_PROFILE_KEY = "$"
|
||||
private const val NOBODY_UID = 9999
|
||||
|
||||
@@ -116,14 +157,8 @@ object Natives {
|
||||
}
|
||||
|
||||
fun requireNewKernel(): Boolean {
|
||||
if (version < MINIMAL_SUPPORTED_KERNEL) {
|
||||
return true
|
||||
}
|
||||
val simpleVersionFull = getSimpleVersionFull()
|
||||
if (simpleVersionFull.isEmpty()) {
|
||||
return false
|
||||
}
|
||||
return simpleVersionFull < MINIMAL_SUPPORTED_KERNEL_FULL
|
||||
if (version < MINIMAL_SUPPORTED_KERNEL) return true
|
||||
return isVersionLessThan(getFullVersion(), MINIMAL_SUPPORTED_KERNEL_FULL)
|
||||
}
|
||||
|
||||
@Immutable
|
||||
@@ -147,6 +182,36 @@ object Natives {
|
||||
val statusSusSu: Boolean = false
|
||||
) : Parcelable
|
||||
|
||||
@Immutable
|
||||
@Parcelize
|
||||
@Keep
|
||||
data class DynamicManagerConfig(
|
||||
val size: Int = 0,
|
||||
val hash: String = ""
|
||||
) : Parcelable {
|
||||
|
||||
fun isValid(): Boolean {
|
||||
return size > 0 && hash.length == 64 && hash.all {
|
||||
it in '0'..'9' || it in 'a'..'f' || it in 'A'..'F'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Immutable
|
||||
@Parcelize
|
||||
@Keep
|
||||
data class ManagersList(
|
||||
val count: Int = 0,
|
||||
val managers: List<ManagerInfo> = emptyList()
|
||||
) : Parcelable
|
||||
|
||||
@Immutable
|
||||
@Parcelize
|
||||
@Keep
|
||||
data class ManagerInfo(
|
||||
val uid: Int = 0,
|
||||
val signatureIndex: Int = 0
|
||||
) : Parcelable
|
||||
|
||||
@Immutable
|
||||
@Parcelize
|
||||
|
||||
141
manager/app/src/main/java/com/sukisu/ultra/ui/KsuService.kt
Normal file
141
manager/app/src/main/java/com/sukisu/ultra/ui/KsuService.kt
Normal file
@@ -0,0 +1,141 @@
|
||||
package com.sukisu.ultra.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInfo
|
||||
import android.os.Binder
|
||||
import android.os.IBinder
|
||||
import android.os.IInterface
|
||||
import android.os.Parcel
|
||||
import android.os.UserManager
|
||||
import android.util.Log
|
||||
import com.topjohnwu.superuser.ipc.RootService
|
||||
import rikka.parcelablelist.ParcelableListSlice
|
||||
import java.lang.reflect.Method
|
||||
|
||||
/**
|
||||
* @author ShirkNeko
|
||||
* @date 2025/7/2.
|
||||
*/
|
||||
class KsuService : RootService() {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "KsuService"
|
||||
private const val DESCRIPTOR = "com.sukisu.ultra.IKsuInterface"
|
||||
private const val TRANSACTION_GET_PACKAGES = IBinder.FIRST_CALL_TRANSACTION + 0
|
||||
}
|
||||
|
||||
interface IKsuInterface : IInterface {
|
||||
fun getPackages(flags: Int): ParcelableListSlice<PackageInfo>
|
||||
}
|
||||
|
||||
abstract class Stub : Binder(), IKsuInterface {
|
||||
init {
|
||||
attachInterface(this, DESCRIPTOR)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun asInterface(obj: IBinder?): IKsuInterface? {
|
||||
if (obj == null) return null
|
||||
val iin = obj.queryLocalInterface(DESCRIPTOR)
|
||||
return if (iin != null && iin is IKsuInterface) {
|
||||
iin
|
||||
} else {
|
||||
Proxy(obj)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun asBinder(): IBinder = this
|
||||
|
||||
override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean {
|
||||
val descriptor = DESCRIPTOR
|
||||
when (code) {
|
||||
INTERFACE_TRANSACTION -> {
|
||||
reply?.writeString(descriptor)
|
||||
return true
|
||||
}
|
||||
TRANSACTION_GET_PACKAGES -> {
|
||||
data.enforceInterface(descriptor)
|
||||
val flagsArg = data.readInt()
|
||||
val result = getPackages(flagsArg)
|
||||
reply?.writeNoException()
|
||||
reply?.writeInt(1)
|
||||
result.writeToParcel(reply!!, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE)
|
||||
return true
|
||||
}
|
||||
}
|
||||
return super.onTransact(code, data, reply, flags)
|
||||
}
|
||||
|
||||
private class Proxy(private val mRemote: IBinder) : IKsuInterface {
|
||||
override fun getPackages(flags: Int): ParcelableListSlice<PackageInfo> {
|
||||
val data = Parcel.obtain()
|
||||
val reply = Parcel.obtain()
|
||||
return try {
|
||||
data.writeInterfaceToken(DESCRIPTOR)
|
||||
data.writeInt(flags)
|
||||
mRemote.transact(TRANSACTION_GET_PACKAGES, data, reply, 0)
|
||||
reply.readException()
|
||||
if (reply.readInt() != 0) {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
ParcelableListSlice.CREATOR.createFromParcel(reply) as ParcelableListSlice<PackageInfo>
|
||||
} else {
|
||||
ParcelableListSlice(emptyList<PackageInfo>())
|
||||
}
|
||||
} finally {
|
||||
reply.recycle()
|
||||
data.recycle()
|
||||
}
|
||||
}
|
||||
|
||||
override fun asBinder(): IBinder = mRemote
|
||||
}
|
||||
}
|
||||
|
||||
inner class KsuInterfaceImpl : Stub() {
|
||||
override fun getPackages(flags: Int): ParcelableListSlice<PackageInfo> {
|
||||
val list = getInstalledPackagesAll(flags)
|
||||
Log.i(TAG, "getPackages: ${list.size}")
|
||||
return ParcelableListSlice(list)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent): IBinder {
|
||||
return KsuInterfaceImpl()
|
||||
}
|
||||
|
||||
private fun getUserIds(): List<Int> {
|
||||
val result = mutableListOf<Int>()
|
||||
val um = getSystemService(USER_SERVICE) as UserManager
|
||||
val userProfiles = um.userProfiles
|
||||
for (userProfile in userProfiles) {
|
||||
result.add(userProfile.hashCode())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun getInstalledPackagesAll(flags: Int): ArrayList<PackageInfo> {
|
||||
val packages = ArrayList<PackageInfo>()
|
||||
for (userId in getUserIds()) {
|
||||
Log.i(TAG, "getInstalledPackagesAll: $userId")
|
||||
packages.addAll(getInstalledPackagesAsUser(flags, userId))
|
||||
}
|
||||
return packages
|
||||
}
|
||||
|
||||
private fun getInstalledPackagesAsUser(flags: Int, userId: Int): List<PackageInfo> {
|
||||
return try {
|
||||
val pm = packageManager
|
||||
val getInstalledPackagesAsUser: Method = pm.javaClass.getDeclaredMethod(
|
||||
"getInstalledPackagesAsUser",
|
||||
Int::class.java,
|
||||
Int::class.java
|
||||
)
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
getInstalledPackagesAsUser.invoke(pm, flags, userId) as List<PackageInfo>
|
||||
} catch (e: Throwable) {
|
||||
Log.e(TAG, "err", e)
|
||||
ArrayList()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,20 +7,32 @@ import android.os.Bundle
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.animation.AnimatedContentTransitionScope
|
||||
import androidx.compose.animation.EnterTransition
|
||||
import androidx.compose.animation.ExitTransition
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.scaleOut
|
||||
import androidx.compose.animation.slideInHorizontally
|
||||
import androidx.compose.animation.slideOutHorizontally
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import com.ramcosta.composedestinations.DestinationsNavHost
|
||||
import com.ramcosta.composedestinations.animations.NavHostAnimatedDestinationStyle
|
||||
import com.ramcosta.composedestinations.generated.NavGraphs
|
||||
import com.ramcosta.composedestinations.generated.destinations.ExecuteModuleActionScreenDestination
|
||||
import com.ramcosta.composedestinations.spec.NavHostGraphSpec
|
||||
import io.sukisu.ultra.UltraToolInstall
|
||||
import com.sukisu.ultra.ksuApp
|
||||
import zako.zako.zako.zakoui.activity.util.AppData
|
||||
import com.sukisu.ultra.ui.screen.BottomBarDestination
|
||||
import com.sukisu.ultra.ui.theme.*
|
||||
import zako.zako.zako.zakoui.activity.util.*
|
||||
import zako.zako.zako.zakoui.activity.component.BottomBar
|
||||
@@ -82,6 +94,10 @@ class MainActivity : ComponentActivity() {
|
||||
val snackBarHostState = remember { SnackbarHostState() }
|
||||
val currentDestination = navController.currentBackStackEntryAsState().value?.destination
|
||||
|
||||
val bottomBarRoutes = remember {
|
||||
BottomBarDestination.entries.map { it.direction.route }.toSet()
|
||||
}
|
||||
|
||||
val showBottomBar = when (currentDestination?.route) {
|
||||
ExecuteModuleActionScreenDestination.route -> false
|
||||
else -> true
|
||||
@@ -107,7 +123,47 @@ class MainActivity : ComponentActivity() {
|
||||
modifier = Modifier.padding(innerPadding),
|
||||
navGraph = NavGraphs.root as NavHostGraphSpec,
|
||||
navController = navController,
|
||||
defaultTransitions = NavigationUtils.defaultTransitions()
|
||||
defaultTransitions = object : NavHostAnimatedDestinationStyle() {
|
||||
override val enterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition = {
|
||||
// If the target is a detail page (not a bottom navigation page), slide in from the right
|
||||
if (targetState.destination.route !in bottomBarRoutes) {
|
||||
slideInHorizontally(initialOffsetX = { it })
|
||||
} else {
|
||||
// Otherwise (switching between bottom navigation pages), use fade in
|
||||
fadeIn(animationSpec = tween(340))
|
||||
}
|
||||
}
|
||||
|
||||
override val exitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition = {
|
||||
// If navigating from the home page (bottom navigation page) to a detail page, slide out to the left
|
||||
if (initialState.destination.route in bottomBarRoutes && targetState.destination.route !in bottomBarRoutes) {
|
||||
slideOutHorizontally(targetOffsetX = { -it / 4 }) + fadeOut()
|
||||
} else {
|
||||
// Otherwise (switching between bottom navigation pages), use fade out
|
||||
fadeOut(animationSpec = tween(340))
|
||||
}
|
||||
}
|
||||
|
||||
override val popEnterTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> EnterTransition = {
|
||||
// If returning to the home page (bottom navigation page), slide in from the left
|
||||
if (targetState.destination.route in bottomBarRoutes) {
|
||||
slideInHorizontally(initialOffsetX = { -it / 4 }) + fadeIn()
|
||||
} else {
|
||||
// Otherwise (e.g., returning between multiple detail pages), use default fade in
|
||||
fadeIn(animationSpec = tween(340))
|
||||
}
|
||||
}
|
||||
|
||||
override val popExitTransition: AnimatedContentTransitionScope<NavBackStackEntry>.() -> ExitTransition = {
|
||||
// If returning from a detail page (not a bottom navigation page), scale down and fade out
|
||||
if (initialState.destination.route !in bottomBarRoutes) {
|
||||
scaleOut(targetScale = 0.9f) + fadeOut()
|
||||
} else {
|
||||
// Otherwise, use default fade out
|
||||
fadeOut(animationSpec = tween(340))
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +99,10 @@ private fun AboutCardContent() {
|
||||
htmlString = stringResource(
|
||||
id = R.string.about_source_code,
|
||||
"<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>",
|
||||
"<b>怡子曰曰</b>",
|
||||
"<b>明风 OuO</b>",
|
||||
"<b><a href=\"https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt\">CC BY-NC-SA 4.0</a></b>"
|
||||
),
|
||||
linkStyles = TextLinkStyles(
|
||||
style = SpanStyle(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,9 @@ import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Apps
|
||||
import androidx.compose.material.icons.filled.Folder
|
||||
import androidx.compose.material.icons.filled.Loop
|
||||
import androidx.compose.material.icons.filled.PlayArrow
|
||||
import androidx.compose.material.icons.filled.Security
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
@@ -31,6 +33,8 @@ import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
@@ -38,14 +42,9 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.sukisu.ultra.R
|
||||
import com.sukisu.ultra.ui.screen.extensions.AddKstatPathItemCard
|
||||
import com.sukisu.ultra.ui.screen.extensions.EmptyStateCard
|
||||
import com.sukisu.ultra.ui.screen.extensions.FeatureStatusCard
|
||||
import com.sukisu.ultra.ui.screen.extensions.KstatConfigItemCard
|
||||
import com.sukisu.ultra.ui.screen.extensions.PathItemCard
|
||||
import com.sukisu.ultra.ui.screen.extensions.SusMountHidingControlCard
|
||||
import com.sukisu.ultra.ui.util.SuSFSManager
|
||||
import com.sukisu.ultra.ui.util.SuSFSManager.isSusVersion_1_5_8
|
||||
import com.sukisu.ultra.ui.util.SuSFSManager.isSusVersion158
|
||||
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
|
||||
|
||||
/**
|
||||
* SUS路径内容组件
|
||||
@@ -55,22 +54,118 @@ fun SusPathsContent(
|
||||
susPaths: Set<String>,
|
||||
isLoading: Boolean,
|
||||
onAddPath: () -> Unit,
|
||||
onAddAppPath: () -> Unit,
|
||||
onRemovePath: (String) -> Unit,
|
||||
onEditPath: ((String) -> Unit)? = null
|
||||
onEditPath: ((String) -> Unit)? = null,
|
||||
forceRefreshApps: Boolean = false
|
||||
) {
|
||||
val superUserApps = SuperUserViewModel.apps
|
||||
val superUserIsRefreshing = remember { SuperUserViewModel().isRefreshing }
|
||||
|
||||
LaunchedEffect(superUserIsRefreshing, superUserApps.size) {
|
||||
if (!superUserIsRefreshing && superUserApps.isNotEmpty()) {
|
||||
AppInfoCache.clearCache()
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(forceRefreshApps) {
|
||||
if (forceRefreshApps) {
|
||||
AppInfoCache.clearCache()
|
||||
}
|
||||
}
|
||||
|
||||
val (appPathGroups, otherPaths) = remember(susPaths) {
|
||||
val appPathRegex = Regex(".*/Android/data/([^/]+)/?.*")
|
||||
val uidPathRegex = Regex("/sys/fs/cgroup/uid_([0-9]+)")
|
||||
val appPathMap = mutableMapOf<String, MutableList<String>>()
|
||||
val uidToPackageMap = mutableMapOf<String, String>()
|
||||
val others = mutableListOf<String>()
|
||||
|
||||
// 构建UID到包名的映射
|
||||
SuperUserViewModel.apps.forEach { app ->
|
||||
try {
|
||||
val uid = app.packageInfo.applicationInfo?.uid
|
||||
uidToPackageMap[uid.toString()] = app.packageName
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
|
||||
susPaths.forEach { path ->
|
||||
val appDataMatch = appPathRegex.find(path)
|
||||
val uidMatch = uidPathRegex.find(path)
|
||||
|
||||
when {
|
||||
appDataMatch != null -> {
|
||||
val packageName = appDataMatch.groupValues[1]
|
||||
appPathMap.getOrPut(packageName) { mutableListOf() }.add(path)
|
||||
}
|
||||
uidMatch != null -> {
|
||||
val uid = uidMatch.groupValues[1]
|
||||
val packageName = uidToPackageMap[uid]
|
||||
if (packageName != null) {
|
||||
appPathMap.getOrPut(packageName) { mutableListOf() }.add(path)
|
||||
} else {
|
||||
others.add(path)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
others.add(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val sortedAppGroups = appPathMap.toList()
|
||||
.sortedBy { it.first }
|
||||
.map { (packageName, paths) -> packageName to paths.sorted() }
|
||||
|
||||
Pair(sortedAppGroups, others.sorted())
|
||||
}
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
if (susPaths.isEmpty()) {
|
||||
// 应用路径分组
|
||||
if (appPathGroups.isNotEmpty()) {
|
||||
item {
|
||||
EmptyStateCard(
|
||||
message = stringResource(R.string.susfs_no_paths_configured)
|
||||
SectionHeader(
|
||||
title = stringResource(R.string.app_paths_section),
|
||||
subtitle = null,
|
||||
icon = Icons.Default.Apps,
|
||||
count = appPathGroups.size
|
||||
)
|
||||
}
|
||||
} else {
|
||||
items(susPaths.toList()) { path ->
|
||||
|
||||
items(appPathGroups) { (packageName, paths) ->
|
||||
AppPathGroupCard(
|
||||
packageName = packageName,
|
||||
paths = paths,
|
||||
onDeleteGroup = {
|
||||
paths.forEach { path -> onRemovePath(path) }
|
||||
},
|
||||
onEditGroup = if (onEditPath != null) {
|
||||
{
|
||||
onEditPath(paths.first())
|
||||
}
|
||||
} else null,
|
||||
isLoading = isLoading
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 其他路径
|
||||
if (otherPaths.isNotEmpty()) {
|
||||
item {
|
||||
SectionHeader(
|
||||
title = stringResource(R.string.other_paths_section),
|
||||
subtitle = null,
|
||||
icon = Icons.Default.Folder,
|
||||
count = otherPaths.size
|
||||
)
|
||||
}
|
||||
|
||||
items(otherPaths) { path ->
|
||||
PathItemCard(
|
||||
path = path,
|
||||
icon = Icons.Default.Folder,
|
||||
@@ -81,7 +176,14 @@ fun SusPathsContent(
|
||||
}
|
||||
}
|
||||
|
||||
// 添加普通长按钮
|
||||
if (susPaths.isEmpty()) {
|
||||
item {
|
||||
EmptyStateCard(
|
||||
message = stringResource(R.string.susfs_no_paths_configured)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
@@ -103,7 +205,128 @@ fun SusPathsContent(
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(text = stringResource(R.string.add))
|
||||
Text(text = stringResource(R.string.add_custom_path))
|
||||
}
|
||||
|
||||
Button(
|
||||
onClick = onAddAppPath,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.height(48.dp),
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Apps,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(text = stringResource(R.string.add_app_path))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SUS循环路径内容组件
|
||||
*/
|
||||
@Composable
|
||||
fun SusLoopPathsContent(
|
||||
susLoopPaths: Set<String>,
|
||||
isLoading: Boolean,
|
||||
onAddLoopPath: () -> Unit,
|
||||
onRemoveLoopPath: (String) -> Unit,
|
||||
onEditLoopPath: ((String) -> Unit)? = null
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
// 说明卡片
|
||||
item {
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.4f)
|
||||
),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.sus_loop_paths_description_title),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.sus_loop_paths_description_text),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.susfs_loop_path_restriction_warning),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.secondary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (susLoopPaths.isEmpty()) {
|
||||
item {
|
||||
EmptyStateCard(
|
||||
message = stringResource(R.string.susfs_no_loop_paths_configured)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
item {
|
||||
SectionHeader(
|
||||
title = stringResource(R.string.loop_paths_section),
|
||||
subtitle = null,
|
||||
icon = Icons.Default.Loop,
|
||||
count = susLoopPaths.size
|
||||
)
|
||||
}
|
||||
|
||||
items(susLoopPaths.toList()) { path ->
|
||||
PathItemCard(
|
||||
path = path,
|
||||
icon = Icons.Default.Loop,
|
||||
onDelete = { onRemoveLoopPath(path) },
|
||||
onEdit = if (onEditLoopPath != null) { { onEditLoopPath(path) } } else null,
|
||||
isLoading = isLoading
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 16.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Button(
|
||||
onClick = onAddLoopPath,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.height(48.dp),
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Add,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(text = stringResource(R.string.add_loop_path))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -118,7 +341,7 @@ fun SusPathsContent(
|
||||
fun SusMountsContent(
|
||||
susMounts: Set<String>,
|
||||
hideSusMountsForAllProcs: Boolean,
|
||||
isSusVersion_1_5_8: Boolean,
|
||||
isSusVersion158: Boolean,
|
||||
isLoading: Boolean,
|
||||
onAddMount: () -> Unit,
|
||||
onRemoveMount: (String) -> Unit,
|
||||
@@ -130,7 +353,7 @@ fun SusMountsContent(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
if (isSusVersion_1_5_8) {
|
||||
if (isSusVersion158) {
|
||||
item {
|
||||
SusMountHidingControlCard(
|
||||
hideSusMountsForAllProcs = hideSusMountsForAllProcs,
|
||||
@@ -158,7 +381,6 @@ fun SusMountsContent(
|
||||
}
|
||||
}
|
||||
|
||||
// 添加普通长按钮
|
||||
item {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
@@ -204,11 +426,10 @@ fun TryUmountContent(
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
if (isSusVersion_1_5_8()) {
|
||||
if (isSusVersion158()) {
|
||||
item {
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -289,7 +510,6 @@ fun TryUmountContent(
|
||||
}
|
||||
}
|
||||
|
||||
// 添加普通长按钮
|
||||
item {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
@@ -359,7 +579,6 @@ fun KstatConfigContent(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
// 说明卡片
|
||||
item {
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -402,7 +621,6 @@ fun KstatConfigContent(
|
||||
}
|
||||
}
|
||||
|
||||
// 静态Kstat配置列表
|
||||
if (kstatConfigs.isNotEmpty()) {
|
||||
item {
|
||||
Text(
|
||||
@@ -421,7 +639,6 @@ fun KstatConfigContent(
|
||||
}
|
||||
}
|
||||
|
||||
// Add Kstat路径列表
|
||||
if (addKstatPaths.isNotEmpty()) {
|
||||
item {
|
||||
Text(
|
||||
@@ -442,7 +659,6 @@ fun KstatConfigContent(
|
||||
}
|
||||
}
|
||||
|
||||
// 空状态显示
|
||||
if (kstatConfigs.isEmpty() && addKstatPaths.isEmpty()) {
|
||||
item {
|
||||
EmptyStateCard(
|
||||
@@ -451,7 +667,6 @@ fun KstatConfigContent(
|
||||
}
|
||||
}
|
||||
|
||||
// 添加普通长按钮
|
||||
item {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
@@ -515,7 +730,6 @@ fun PathSettingsContent(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
// Android Data路径设置
|
||||
item {
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -550,7 +764,6 @@ fun PathSettingsContent(
|
||||
}
|
||||
}
|
||||
|
||||
// SD卡路径设置
|
||||
item {
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -599,7 +812,6 @@ fun EnabledFeaturesContent(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
// 说明卡片
|
||||
item {
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
|
||||
@@ -70,11 +70,15 @@ data class ModuleInstallStatus(
|
||||
val totalModules: Int = 0,
|
||||
val currentModule: Int = 0,
|
||||
val currentModuleName: String = "",
|
||||
val failedModules: MutableList<String> = mutableListOf()
|
||||
val failedModules: MutableList<String> = mutableListOf(),
|
||||
val verifiedModules: MutableList<String> = mutableListOf() // 添加已验证模块列表
|
||||
)
|
||||
|
||||
private var moduleInstallStatus = mutableStateOf(ModuleInstallStatus())
|
||||
|
||||
// 存储模块URI和验证状态的映射
|
||||
private var moduleVerificationMap = mutableMapOf<Uri, Boolean>()
|
||||
|
||||
fun setFlashingStatus(status: FlashingStatus) {
|
||||
currentFlashingStatus.value = status
|
||||
}
|
||||
@@ -83,7 +87,8 @@ fun updateModuleInstallStatus(
|
||||
totalModules: Int? = null,
|
||||
currentModule: Int? = null,
|
||||
currentModuleName: String? = null,
|
||||
failedModule: String? = null
|
||||
failedModule: String? = null,
|
||||
verifiedModule: String? = null
|
||||
) {
|
||||
val current = moduleInstallStatus.value
|
||||
moduleInstallStatus.value = current.copy(
|
||||
@@ -99,6 +104,18 @@ fun updateModuleInstallStatus(
|
||||
failedModules = updatedFailedModules
|
||||
)
|
||||
}
|
||||
|
||||
if (verifiedModule != null) {
|
||||
val updatedVerifiedModules = current.verifiedModules.toMutableList()
|
||||
updatedVerifiedModules.add(verifiedModule)
|
||||
moduleInstallStatus.value = moduleInstallStatus.value.copy(
|
||||
verifiedModules = updatedVerifiedModules
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun setModuleVerificationStatus(uri: Uri, isVerified: Boolean) {
|
||||
moduleVerificationMap[uri] = isVerified
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@@ -112,6 +129,10 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
var showFloatAction by rememberSaveable { mutableStateOf(false) }
|
||||
// 添加状态跟踪是否已经完成刷写
|
||||
var hasFlashCompleted by rememberSaveable { mutableStateOf(false) }
|
||||
var hasExecuted by rememberSaveable { mutableStateOf(false) }
|
||||
// 更新模块状态管理
|
||||
var hasUpdateExecuted by rememberSaveable { mutableStateOf(false) }
|
||||
var hasUpdateCompleted by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
val scope = rememberCoroutineScope()
|
||||
@@ -129,24 +150,88 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
|
||||
// 重置状态
|
||||
LaunchedEffect(flashIt) {
|
||||
if (flashIt is FlashIt.FlashModules && flashIt.currentIndex == 0) {
|
||||
when (flashIt) {
|
||||
is FlashIt.FlashModules -> {
|
||||
if (flashIt.currentIndex == 0) {
|
||||
moduleInstallStatus.value = ModuleInstallStatus(
|
||||
totalModules = flashIt.uris.size,
|
||||
currentModule = 1
|
||||
)
|
||||
hasFlashCompleted = false
|
||||
} else if (flashIt !is FlashIt.FlashModules) {
|
||||
hasExecuted = false
|
||||
moduleVerificationMap.clear()
|
||||
}
|
||||
}
|
||||
is FlashIt.FlashModuleUpdate -> {
|
||||
hasUpdateCompleted = false
|
||||
hasUpdateExecuted = false
|
||||
}
|
||||
else -> {
|
||||
hasFlashCompleted = false
|
||||
hasExecuted = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 只有在未完成刷写时才执行刷写操作
|
||||
LaunchedEffect(flashIt, hasFlashCompleted) {
|
||||
// 如果已经完成刷写或者已有文本内容,则不再执行
|
||||
if (hasFlashCompleted || text.isNotEmpty()) {
|
||||
// 处理更新模块安装
|
||||
LaunchedEffect(flashIt) {
|
||||
if (flashIt !is FlashIt.FlashModuleUpdate) return@LaunchedEffect
|
||||
if (hasUpdateExecuted || hasUpdateCompleted || text.isNotEmpty()) {
|
||||
return@LaunchedEffect
|
||||
}
|
||||
|
||||
hasUpdateExecuted = true
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
setFlashingStatus(FlashingStatus.FLASHING)
|
||||
|
||||
try {
|
||||
logContent.append(text).append("\n")
|
||||
} catch (_: Exception) {
|
||||
logContent.append(text).append("\n")
|
||||
}
|
||||
|
||||
flashModuleUpdate(flashIt.uri, onFinish = { showReboot, code ->
|
||||
if (code != 0) {
|
||||
text += "$errorCodeString $code.\n$checkLogString\n"
|
||||
setFlashingStatus(FlashingStatus.FAILED)
|
||||
} else {
|
||||
setFlashingStatus(FlashingStatus.SUCCESS)
|
||||
|
||||
// 处理模块更新成功后的验证标志
|
||||
val isVerified = moduleVerificationMap[flashIt.uri] ?: false
|
||||
ModuleOperationUtils.handleModuleUpdate(context, flashIt.uri, isVerified)
|
||||
|
||||
viewModel.markNeedRefresh()
|
||||
}
|
||||
if (showReboot) {
|
||||
text += "\n\n\n"
|
||||
showFloatAction = true
|
||||
}
|
||||
hasUpdateCompleted = true
|
||||
}, onStdout = {
|
||||
tempText = "$it\n"
|
||||
if (tempText.startsWith("[H[J")) { // clear command
|
||||
text = tempText.substring(6)
|
||||
} else {
|
||||
text += tempText
|
||||
}
|
||||
logContent.append(it).append("\n")
|
||||
}, onStderr = {
|
||||
logContent.append(it).append("\n")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 安装但排除更新模块
|
||||
LaunchedEffect(flashIt) {
|
||||
if (flashIt is FlashIt.FlashModuleUpdate) return@LaunchedEffect
|
||||
if (hasExecuted || hasFlashCompleted || text.isNotEmpty()) {
|
||||
return@LaunchedEffect
|
||||
}
|
||||
|
||||
hasExecuted = true
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
setFlashingStatus(FlashingStatus.FLASHING)
|
||||
|
||||
@@ -177,6 +262,28 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
}
|
||||
} else {
|
||||
setFlashingStatus(FlashingStatus.SUCCESS)
|
||||
|
||||
// 处理模块安装成功后的验证标志
|
||||
when (flashIt) {
|
||||
is FlashIt.FlashModule -> {
|
||||
val isVerified = moduleVerificationMap[flashIt.uri] ?: false
|
||||
ModuleOperationUtils.handleModuleInstallSuccess(context, flashIt.uri, isVerified)
|
||||
if (isVerified) {
|
||||
updateModuleInstallStatus(verifiedModule = moduleInstallStatus.value.currentModuleName)
|
||||
}
|
||||
}
|
||||
is FlashIt.FlashModules -> {
|
||||
val currentUri = flashIt.uris[flashIt.currentIndex]
|
||||
val isVerified = moduleVerificationMap[currentUri] ?: false
|
||||
ModuleOperationUtils.handleModuleInstallSuccess(context, currentUri, isVerified)
|
||||
if (isVerified) {
|
||||
updateModuleInstallStatus(verifiedModule = moduleInstallStatus.value.currentModuleName)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
|
||||
viewModel.markNeedRefresh()
|
||||
}
|
||||
if (showReboot) {
|
||||
@@ -210,8 +317,13 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
||||
}
|
||||
|
||||
val onBack: () -> Unit = {
|
||||
if (currentFlashingStatus.value != FlashingStatus.FLASHING) {
|
||||
if (flashIt is FlashIt.FlashModules) {
|
||||
val canGoBack = when (flashIt) {
|
||||
is FlashIt.FlashModuleUpdate -> currentFlashingStatus.value != FlashingStatus.FLASHING
|
||||
else -> currentFlashingStatus.value != FlashingStatus.FLASHING
|
||||
}
|
||||
|
||||
if (canGoBack) {
|
||||
if (flashIt is FlashIt.FlashModules || flashIt is FlashIt.FlashModuleUpdate) {
|
||||
viewModel.markNeedRefresh()
|
||||
viewModel.fetchModuleList()
|
||||
navigator.navigate(ModuleScreenDestination)
|
||||
@@ -360,7 +472,7 @@ fun ModuleInstallProgressBar(
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = if (currentModuleName.isNotEmpty()) currentModuleName else stringResource(R.string.module),
|
||||
text = currentModuleName.ifEmpty { stringResource(R.string.module) },
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
@@ -532,10 +644,21 @@ sealed class FlashIt : Parcelable {
|
||||
data class FlashBoot(val boot: Uri? = null, val lkm: LkmSelection, val ota: Boolean) : FlashIt()
|
||||
data class FlashModule(val uri: Uri) : FlashIt()
|
||||
data class FlashModules(val uris: List<Uri>, val currentIndex: Int = 0) : FlashIt()
|
||||
data class FlashModuleUpdate(val uri: Uri) : FlashIt() // 模块更新
|
||||
data object FlashRestore : FlashIt()
|
||||
data object FlashUninstall : FlashIt()
|
||||
}
|
||||
|
||||
// 模块更新刷写
|
||||
fun flashModuleUpdate(
|
||||
uri: Uri,
|
||||
onFinish: (Boolean, Int) -> Unit,
|
||||
onStdout: (String) -> Unit,
|
||||
onStderr: (String) -> Unit
|
||||
) {
|
||||
flashModule(uri, onFinish, onStdout, onStderr)
|
||||
}
|
||||
|
||||
fun flashIt(
|
||||
flashIt: FlashIt,
|
||||
onFinish: (Boolean, Int) -> Unit,
|
||||
@@ -563,6 +686,9 @@ fun flashIt(
|
||||
|
||||
flashModule(currentUri, onFinish, onStdout, onStderr)
|
||||
}
|
||||
is FlashIt.FlashModuleUpdate -> {
|
||||
onFinish(false, 0)
|
||||
}
|
||||
FlashIt.FlashRestore -> restoreBoot(onFinish, onStdout, onStderr)
|
||||
FlashIt.FlashUninstall -> uninstallPermanently(onFinish, onStdout, onStderr)
|
||||
}
|
||||
|
||||
@@ -192,6 +192,7 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
||||
systemInfo = viewModel.systemInfo,
|
||||
isSimpleMode = viewModel.isSimpleMode,
|
||||
isHideSusfsStatus = viewModel.isHideSusfsStatus,
|
||||
isHideZygiskImplement = viewModel.isHideZygiskImplement,
|
||||
showKpmInfo = viewModel.showKpmInfo,
|
||||
lkmMode = viewModel.systemStatus.lkmMode,
|
||||
)
|
||||
@@ -319,7 +320,8 @@ private fun TopBar(
|
||||
}) {
|
||||
RebootDropdownItem(id = R.string.reboot)
|
||||
|
||||
val pm = LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
|
||||
val pm =
|
||||
LocalContext.current.getSystemService(Context.POWER_SERVICE) as PowerManager?
|
||||
@Suppress("DEPRECATION")
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && pm?.isRebootingUserspaceSupported == true) {
|
||||
RebootDropdownItem(id = R.string.reboot_userspace, reason = "userspace")
|
||||
@@ -343,8 +345,10 @@ private fun StatusCard(
|
||||
onClickInstall: () -> Unit = {}
|
||||
) {
|
||||
ElevatedCard(
|
||||
colors = getCardColors( if (systemStatus.ksuVersion != null)MaterialTheme.colorScheme.secondaryContainer
|
||||
else MaterialTheme.colorScheme.errorContainer),
|
||||
colors = getCardColors(
|
||||
if (systemStatus.ksuVersion != null) MaterialTheme.colorScheme.secondaryContainer
|
||||
else MaterialTheme.colorScheme.errorContainer
|
||||
),
|
||||
elevation = getCardElevation(),
|
||||
) {
|
||||
Row(
|
||||
@@ -433,7 +437,10 @@ 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)
|
||||
|
||||
if (!isHideVersion) {
|
||||
@@ -641,6 +648,7 @@ private fun InfoCard(
|
||||
systemInfo: HomeViewModel.SystemInfo,
|
||||
isSimpleMode: Boolean,
|
||||
isHideSusfsStatus: Boolean,
|
||||
isHideZygiskImplement: Boolean,
|
||||
showKpmInfo: Boolean,
|
||||
lkmMode: Boolean?
|
||||
) {
|
||||
@@ -719,16 +727,65 @@ private fun InfoCard(
|
||||
icon = Icons.Default.SettingsSuggest,
|
||||
)
|
||||
|
||||
if (!isSimpleMode &&
|
||||
(systemInfo.suSFSStatus != "Supported")) {
|
||||
InfoCardItem(
|
||||
stringResource(R.string.home_hook_type),
|
||||
Natives.getHookType(),
|
||||
icon = Icons.Default.Link
|
||||
)
|
||||
}
|
||||
|
||||
// 活跃管理器
|
||||
if (!isSimpleMode && systemInfo.isDynamicSignEnabled && systemInfo.managersList != null) {
|
||||
val signatureMap = systemInfo.managersList.managers.groupBy { it.signatureIndex }
|
||||
|
||||
val managersText = buildString {
|
||||
signatureMap.toSortedMap().forEach { (signatureIndex, managers) ->
|
||||
append(managers.joinToString(", ") { "UID: ${it.uid}" })
|
||||
append(" ")
|
||||
append(
|
||||
when (signatureIndex) {
|
||||
0 -> "(${stringResource(R.string.default_signature)})"
|
||||
1 -> "(${stringResource(R.string.dynamic_managerature)})"
|
||||
else -> if (signatureIndex >= 2) "(${
|
||||
stringResource(
|
||||
R.string.signature_index,
|
||||
signatureIndex
|
||||
)
|
||||
})" else "(${stringResource(R.string.unknown_signature)})"
|
||||
}
|
||||
)
|
||||
append(" | ")
|
||||
}
|
||||
}.trimEnd(' ', '|')
|
||||
|
||||
InfoCardItem(
|
||||
stringResource(R.string.multi_manager_list),
|
||||
managersText.ifEmpty { stringResource(R.string.no_active_manager) },
|
||||
icon = Icons.Default.Group,
|
||||
)
|
||||
}
|
||||
|
||||
InfoCardItem(
|
||||
stringResource(R.string.home_selinux_status),
|
||||
systemInfo.seLinuxStatus,
|
||||
icon = Icons.Default.Security,
|
||||
)
|
||||
|
||||
if (!isHideZygiskImplement && !isSimpleMode && systemInfo.zygiskImplement != "None") {
|
||||
InfoCardItem(
|
||||
stringResource(R.string.home_zygisk_implement),
|
||||
systemInfo.zygiskImplement,
|
||||
icon = Icons.Default.Adb,
|
||||
)
|
||||
}
|
||||
|
||||
if (!isSimpleMode) {
|
||||
// 根据showKpmInfo决定是否显示KPM信息
|
||||
if (lkmMode != true && !showKpmInfo) {
|
||||
val displayVersion = if (systemInfo.kpmVersion.isEmpty() || systemInfo.kpmVersion.startsWith("Error")) {
|
||||
val displayVersion =
|
||||
if (systemInfo.kpmVersion.isEmpty() || systemInfo.kpmVersion.startsWith("Error")) {
|
||||
val statusText = if (Natives.isKPMEnabled()) {
|
||||
stringResource(R.string.kernel_patched)
|
||||
} else {
|
||||
@@ -749,7 +806,8 @@ private fun InfoCard(
|
||||
|
||||
if (!isSimpleMode && !isHideSusfsStatus &&
|
||||
systemInfo.suSFSStatus == "Supported" &&
|
||||
systemInfo.suSFSVersion.isNotEmpty()) {
|
||||
systemInfo.suSFSVersion.isNotEmpty()
|
||||
) {
|
||||
|
||||
val infoText = SuSFSInfoText(systemInfo)
|
||||
|
||||
@@ -778,9 +836,11 @@ private fun SuSFSInfoText(systemInfo: HomeViewModel.SystemInfo): String = buildS
|
||||
append(" ${stringResource(R.string.sus_su_mode)} ${systemInfo.susSUMode}")
|
||||
}
|
||||
}
|
||||
|
||||
Natives.getHookType() == "Manual" -> {
|
||||
append(" (${stringResource(R.string.manual_hook)})")
|
||||
}
|
||||
|
||||
else -> {
|
||||
append(" (${Natives.getHookType()})")
|
||||
}
|
||||
|
||||
@@ -5,11 +5,8 @@ import android.content.Intent
|
||||
import android.util.Log
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
@@ -19,7 +16,6 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
@@ -27,7 +23,6 @@ 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 com.sukisu.ultra.ui.component.*
|
||||
@@ -37,13 +32,10 @@ import com.sukisu.ultra.ui.util.*
|
||||
import java.io.File
|
||||
import androidx.core.content.edit
|
||||
import com.sukisu.ultra.R
|
||||
import java.io.BufferedReader
|
||||
import java.io.FileInputStream
|
||||
import java.io.InputStreamReader
|
||||
import java.net.*
|
||||
import android.app.Activity
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import com.sukisu.ultra.ui.theme.CardConfig.cardElevation
|
||||
|
||||
/**
|
||||
* KPM 管理界面
|
||||
@@ -54,7 +46,6 @@ import com.sukisu.ultra.ui.theme.CardConfig.cardElevation
|
||||
@Destination<RootGraph>
|
||||
@Composable
|
||||
fun KpmScreen(
|
||||
navigator: DestinationsNavigator,
|
||||
viewModel: KpmViewModel = viewModel()
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
@@ -92,18 +83,17 @@ fun KpmScreen(
|
||||
LaunchedEffect(tempFileForInstall) {
|
||||
tempFileForInstall?.let { tempFile ->
|
||||
try {
|
||||
val command = arrayOf("su", "-c", "strings ${tempFile.absolutePath} | grep 'name='")
|
||||
val process = Runtime.getRuntime().exec(command)
|
||||
val inputStream = process.inputStream
|
||||
val reader = BufferedReader(InputStreamReader(inputStream))
|
||||
var line: String?
|
||||
while (reader.readLine().also { line = it } != null) {
|
||||
if (line!!.startsWith("name=")) {
|
||||
val shell = getRootShell()
|
||||
val command = "strings ${tempFile.absolutePath} | grep 'name='"
|
||||
val result = shell.newJob().add(command).to(ArrayList(), null).exec()
|
||||
if (result.isSuccess) {
|
||||
for (line in result.out) {
|
||||
if (line.startsWith("name=")) {
|
||||
moduleName = line.substringAfter("name=").trim()
|
||||
break
|
||||
}
|
||||
}
|
||||
process.waitFor()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("KsuCli", "Failed to get module name: ${e.message}", e)
|
||||
}
|
||||
@@ -434,18 +424,17 @@ private suspend fun handleModuleInstall(
|
||||
) {
|
||||
var moduleId: String? = null
|
||||
try {
|
||||
val command = arrayOf("su", "-c", "strings ${tempFile.absolutePath} | grep 'name='")
|
||||
val process = Runtime.getRuntime().exec(command)
|
||||
val inputStream = process.inputStream
|
||||
val reader = BufferedReader(InputStreamReader(inputStream))
|
||||
var line: String?
|
||||
while (reader.readLine().also { line = it } != null) {
|
||||
if (line!!.startsWith("name=")) {
|
||||
val shell = getRootShell()
|
||||
val command = "strings ${tempFile.absolutePath} | grep 'name='"
|
||||
val result = shell.newJob().add(command).to(ArrayList(), null).exec()
|
||||
if (result.isSuccess) {
|
||||
for (line in result.out) {
|
||||
if (line.startsWith("name=")) {
|
||||
moduleId = line.substringAfter("name=").trim()
|
||||
break
|
||||
}
|
||||
}
|
||||
process.waitFor()
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("KsuCli", "Failed to get module ID from strings command: ${e.message}", e)
|
||||
}
|
||||
@@ -464,8 +453,9 @@ private suspend fun handleModuleInstall(
|
||||
|
||||
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 shell = getRootShell()
|
||||
shell.newJob().add("mkdir -p /data/adb/kpm").exec()
|
||||
shell.newJob().add("cp ${tempFile.absolutePath} $targetPath").exec()
|
||||
}
|
||||
|
||||
val loadResult = loadKpmModule(tempFile.absolutePath)
|
||||
@@ -509,8 +499,9 @@ private suspend fun handleModuleUninstall(
|
||||
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
|
||||
val shell = getRootShell()
|
||||
val result = shell.newJob().add("ls /data/adb/kpm/$moduleFileName").exec()
|
||||
result.isSuccess
|
||||
} catch (e: Exception) {
|
||||
Log.e("KsuCli", "Failed to check module file existence: ${e.message}", e)
|
||||
snackBarHost.showSnackbar(
|
||||
@@ -519,6 +510,7 @@ private suspend fun handleModuleUninstall(
|
||||
)
|
||||
false
|
||||
}
|
||||
|
||||
val confirmResult = confirmDialog.awaitConfirm(
|
||||
title = confirmTitle,
|
||||
content = confirmContent,
|
||||
@@ -539,7 +531,8 @@ private suspend fun handleModuleUninstall(
|
||||
}
|
||||
|
||||
if (fileExists) {
|
||||
Runtime.getRuntime().exec(arrayOf("su", "-c", "rm $moduleFilePath")).waitFor()
|
||||
val shell = getRootShell()
|
||||
shell.newJob().add("rm $moduleFilePath").exec()
|
||||
}
|
||||
|
||||
viewModel.fetchModuleList()
|
||||
@@ -710,29 +703,31 @@ private fun KpmModuleItem(
|
||||
}
|
||||
|
||||
private fun checkStringsCommand(tempFile: File): Int {
|
||||
val command = arrayOf("su", "-c", "strings ${tempFile.absolutePath} | grep -E 'name=|version=|license=|author='")
|
||||
val process = Runtime.getRuntime().exec(command)
|
||||
val inputStream = process.inputStream
|
||||
val reader = BufferedReader(InputStreamReader(inputStream))
|
||||
var line: String?
|
||||
val shell = getRootShell()
|
||||
val command = "strings ${tempFile.absolutePath} | grep -E 'name=|version=|license=|author='"
|
||||
val result = shell.newJob().add(command).to(ArrayList(), null).exec()
|
||||
|
||||
if (!result.isSuccess) {
|
||||
return 0
|
||||
}
|
||||
|
||||
var matchCount = 0
|
||||
val keywords = listOf("name=", "version=", "license=", "author=")
|
||||
var nameExists = false
|
||||
|
||||
while (reader.readLine().also { line = it } != null) {
|
||||
if (!nameExists && line!!.startsWith("name=")) {
|
||||
for (line in result.out) {
|
||||
if (!nameExists && line.startsWith("name=")) {
|
||||
nameExists = true
|
||||
matchCount++
|
||||
} else if (nameExists) {
|
||||
for (keyword in keywords) {
|
||||
if (line!!.startsWith(keyword)) {
|
||||
if (line.startsWith(keyword)) {
|
||||
matchCount++
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
process.waitFor()
|
||||
|
||||
return if (nameExists) matchCount else 0
|
||||
}
|
||||
|
||||
@@ -110,11 +110,18 @@ data class ModuleBottomSheetMenuItem(
|
||||
fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
val viewModel = viewModel<ModuleViewModel>()
|
||||
val context = LocalContext.current
|
||||
val prefs = context.getSharedPreferences("settings",MODE_PRIVATE)
|
||||
val snackBarHost = LocalSnackbarHost.current
|
||||
val scope = rememberCoroutineScope()
|
||||
val confirmDialog = rememberConfirmDialog()
|
||||
var lastClickTime by remember { mutableStateOf(0L) }
|
||||
|
||||
// 签名验证弹窗状态
|
||||
var showSignatureDialog by remember { mutableStateOf(false) }
|
||||
var signatureDialogMessage by remember { mutableStateOf("") }
|
||||
var isForceVerificationFailed by remember { mutableStateOf(false) }
|
||||
var pendingInstallAction by remember { mutableStateOf<(() -> Unit)?>(null) }
|
||||
|
||||
// 初始化缓存系统
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.initializeCache(context)
|
||||
@@ -175,8 +182,47 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
)
|
||||
|
||||
if (confirmResult == ConfirmResult.Confirmed) {
|
||||
// 验证模块签名
|
||||
val forceVerification = prefs.getBoolean("force_signature_verification", false)
|
||||
val verificationResults = mutableMapOf<Uri, Boolean>()
|
||||
|
||||
for (uri in selectedModules) {
|
||||
val isVerified = verifyModuleSignature(context, uri)
|
||||
verificationResults[uri] = isVerified
|
||||
// 存储验证状态
|
||||
setModuleVerificationStatus(uri, isVerified)
|
||||
|
||||
if (forceVerification && !isVerified) {
|
||||
withContext(Dispatchers.Main) {
|
||||
signatureDialogMessage = context.getString(R.string.module_signature_invalid_message)
|
||||
isForceVerificationFailed = true
|
||||
showSignatureDialog = true
|
||||
}
|
||||
return@launch
|
||||
} else if (!isVerified) {
|
||||
withContext(Dispatchers.Main) {
|
||||
signatureDialogMessage = context.getString(R.string.module_signature_verification_failed)
|
||||
isForceVerificationFailed = false
|
||||
pendingInstallAction = {
|
||||
try {
|
||||
navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(selectedModules)))
|
||||
viewModel.markNeedRefresh()
|
||||
} catch (e: Exception) {
|
||||
Log.e("ModuleScreen", "Error navigating to FlashScreen: ${e.message}")
|
||||
scope.launch {
|
||||
snackBarHost.showSnackbar("Error while installing module: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
showSignatureDialog = true
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
|
||||
// 所有模块签名验证通过,直接安装
|
||||
if (verificationResults.all { it.value }) {
|
||||
try {
|
||||
// 批量安装模块
|
||||
navigator.navigate(FlashScreenDestination(FlashIt.FlashModules(selectedModules)))
|
||||
viewModel.markNeedRefresh()
|
||||
} catch (e: Exception) {
|
||||
@@ -184,6 +230,7 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
snackBarHost.showSnackbar("Error while installing module: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val uri = data.data ?: return@launch
|
||||
// 单个安装模块
|
||||
@@ -205,6 +252,28 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
)
|
||||
|
||||
if (confirmResult == ConfirmResult.Confirmed) {
|
||||
// 验证模块签名
|
||||
val forceVerification = prefs.getBoolean("force_signature_verification", false)
|
||||
val isVerified = verifyModuleSignature(context, uri)
|
||||
// 存储验证状态
|
||||
setModuleVerificationStatus(uri, isVerified)
|
||||
|
||||
if (forceVerification && !isVerified) {
|
||||
signatureDialogMessage = context.getString(R.string.module_signature_invalid_message)
|
||||
isForceVerificationFailed = true
|
||||
showSignatureDialog = true
|
||||
return@launch
|
||||
} else if (!isVerified) {
|
||||
signatureDialogMessage = context.getString(R.string.module_signature_verification_failed)
|
||||
isForceVerificationFailed = false
|
||||
pendingInstallAction = {
|
||||
navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(uri)))
|
||||
viewModel.markNeedRefresh()
|
||||
}
|
||||
showSignatureDialog = true
|
||||
return@launch
|
||||
}
|
||||
|
||||
navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(uri)))
|
||||
viewModel.markNeedRefresh()
|
||||
}
|
||||
@@ -219,7 +288,6 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
val backupLauncher = ModuleModify.rememberModuleBackupLauncher(context, snackBarHost)
|
||||
val restoreLauncher = ModuleModify.rememberModuleRestoreLauncher(context, snackBarHost)
|
||||
|
||||
val prefs = context.getSharedPreferences("settings", MODE_PRIVATE)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (viewModel.moduleList.isEmpty() || viewModel.isNeedRefresh) {
|
||||
@@ -352,6 +420,9 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
onInstallModule = {
|
||||
navigator.navigate(FlashScreenDestination(FlashIt.FlashModule(it)))
|
||||
},
|
||||
onUpdateModule = {
|
||||
navigator.navigate(FlashScreenDestination(FlashIt.FlashModuleUpdate(it)))
|
||||
},
|
||||
onClickModule = { id, name, hasWebUi ->
|
||||
val currentTime = System.currentTimeMillis()
|
||||
if (currentTime - lastClickTime < 600) {
|
||||
@@ -447,6 +518,64 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 签名验证弹窗
|
||||
if (showSignatureDialog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showSignatureDialog = false },
|
||||
icon = {
|
||||
Icon(
|
||||
imageVector = Icons.Outlined.Warning,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.error
|
||||
)
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.module_signature_invalid),
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Text(text = signatureDialogMessage)
|
||||
},
|
||||
confirmButton = {
|
||||
if (isForceVerificationFailed) {
|
||||
// 强制验证失败,只显示确定按钮
|
||||
TextButton(
|
||||
onClick = { showSignatureDialog = false }
|
||||
) {
|
||||
Text(stringResource(R.string.confirm))
|
||||
}
|
||||
} else {
|
||||
// 非强制验证失败,显示继续安装按钮
|
||||
TextButton(
|
||||
onClick = {
|
||||
showSignatureDialog = false
|
||||
pendingInstallAction?.invoke()
|
||||
pendingInstallAction = null
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.install))
|
||||
}
|
||||
}
|
||||
},
|
||||
dismissButton = if (!isForceVerificationFailed) {
|
||||
{
|
||||
TextButton(
|
||||
onClick = {
|
||||
showSignatureDialog = false
|
||||
pendingInstallAction = null
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -622,6 +751,7 @@ private fun ModuleList(
|
||||
modifier: Modifier = Modifier,
|
||||
boxModifier: Modifier = Modifier,
|
||||
onInstallModule: (Uri) -> Unit,
|
||||
onUpdateModule: (Uri) -> Unit,
|
||||
onClickModule: (id: String, name: String, hasWebUi: Boolean) -> Unit,
|
||||
context: Context,
|
||||
snackBarHost: SnackbarHostState
|
||||
@@ -709,7 +839,12 @@ private fun ModuleList(
|
||||
downloadUrl,
|
||||
fileName,
|
||||
downloading,
|
||||
onDownloaded = onInstallModule,
|
||||
onDownloaded = { uri ->
|
||||
// 验证更新模块的签名
|
||||
val isVerified = verifyModuleSignature(context, uri)
|
||||
setModuleVerificationStatus(uri, isVerified)
|
||||
onUpdateModule(uri)
|
||||
},
|
||||
onDownloading = {
|
||||
launch(Dispatchers.Main) {
|
||||
Toast.makeText(context, downloading, Toast.LENGTH_SHORT).show()
|
||||
@@ -741,6 +876,8 @@ private fun ModuleList(
|
||||
val success = loadingDialog.withLoading {
|
||||
withContext(Dispatchers.IO) {
|
||||
if (isUninstall) {
|
||||
// 卸载时移除验证标志
|
||||
ModuleOperationUtils.handleModuleUninstall(module.dirId)
|
||||
uninstallModule(module.dirId)
|
||||
} else {
|
||||
restoreModule(module.dirId)
|
||||
@@ -839,11 +976,9 @@ private fun ModuleList(
|
||||
},
|
||||
onCheckChanged = {
|
||||
scope.launch {
|
||||
val success = loadingDialog.withLoading {
|
||||
withContext(Dispatchers.IO) {
|
||||
val success = withContext(Dispatchers.IO) {
|
||||
toggleModule(module.dirId, !module.enabled)
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
viewModel.fetchModuleList()
|
||||
|
||||
@@ -950,6 +1085,10 @@ fun ModuleItem(
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(0.8f)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = module.name,
|
||||
@@ -958,8 +1097,38 @@ fun ModuleItem(
|
||||
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
|
||||
fontFamily = MaterialTheme.typography.titleMedium.fontFamily,
|
||||
textDecoration = textDecoration,
|
||||
modifier = Modifier.weight(1f, false)
|
||||
)
|
||||
|
||||
// 显示验证标签
|
||||
if (module.isVerified) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Verified,
|
||||
contentDescription = stringResource(R.string.module_signature_verified),
|
||||
tint = MaterialTheme.colorScheme.onPrimary,
|
||||
modifier = Modifier.size(12.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(2.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.module_verified),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onPrimary,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
text = "$moduleVersion: ${module.version}",
|
||||
fontSize = MaterialTheme.typography.bodySmall.fontSize,
|
||||
@@ -1185,6 +1354,8 @@ fun ModuleItemPreview() {
|
||||
hasActionScript = false,
|
||||
dirId = "dirId",
|
||||
config = ModuleConfig(),
|
||||
isVerified = true,
|
||||
verificationTimestamp = System.currentTimeMillis()
|
||||
)
|
||||
ModuleItem(EmptyDestinationsNavigator, module, "", {}, {}, {}, {})
|
||||
}
|
||||
@@ -122,11 +122,11 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
|
||||
// 配置卡片
|
||||
KsuIsValid {
|
||||
SettingsGroupCard(
|
||||
title = stringResource(R.string.configuration),
|
||||
content = {
|
||||
// 配置文件模板入口
|
||||
KsuIsValid {
|
||||
SettingItem(
|
||||
icon = Icons.Filled.Fence,
|
||||
title = stringResource(R.string.settings_profile_template),
|
||||
@@ -135,14 +135,12 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
navigator.navigate(AppProfileTemplateScreenDestination)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// 卸载模块开关
|
||||
var umountChecked by rememberSaveable {
|
||||
mutableStateOf(Natives.isDefaultUmountModules())
|
||||
}
|
||||
|
||||
KsuIsValid {
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.FolderDelete,
|
||||
title = stringResource(R.string.settings_umount_modules_default),
|
||||
@@ -154,10 +152,8 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// SU 禁用开关
|
||||
KsuIsValid {
|
||||
if (Natives.version >= Natives.MINIMAL_SUPPORTED_SU_COMPAT) {
|
||||
var isSuDisabled by rememberSaveable {
|
||||
mutableStateOf(!Natives.isSuEnabled())
|
||||
@@ -175,9 +171,23 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
)
|
||||
}
|
||||
// 强制签名验证开关
|
||||
var forceSignatureVerification by rememberSaveable {
|
||||
mutableStateOf(prefs.getBoolean("force_signature_verification", false))
|
||||
}
|
||||
SwitchItem(
|
||||
icon = Icons.Filled.Security,
|
||||
title = stringResource(R.string.module_signature_verification),
|
||||
summary = stringResource(R.string.module_signature_verification_summary),
|
||||
checked = forceSignatureVerification,
|
||||
onCheckedChange = { enabled ->
|
||||
prefs.edit { putBoolean("force_signature_verification", enabled) }
|
||||
forceSignatureVerification = enabled
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// 应用设置卡片
|
||||
SettingsGroupCard(
|
||||
|
||||
@@ -68,6 +68,7 @@ import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import com.sukisu.ultra.R
|
||||
import com.sukisu.ultra.ui.component.AddAppPathDialog
|
||||
import com.sukisu.ultra.ui.component.AddKstatStaticallyDialog
|
||||
import com.sukisu.ultra.ui.component.AddPathDialog
|
||||
import com.sukisu.ultra.ui.component.AddTryUmountDialog
|
||||
@@ -77,12 +78,15 @@ import com.sukisu.ultra.ui.component.KstatConfigContent
|
||||
import com.sukisu.ultra.ui.component.PathSettingsContent
|
||||
import com.sukisu.ultra.ui.component.SusMountsContent
|
||||
import com.sukisu.ultra.ui.component.SusPathsContent
|
||||
import com.sukisu.ultra.ui.component.SusLoopPathsContent
|
||||
import com.sukisu.ultra.ui.component.TryUmountContent
|
||||
import com.sukisu.ultra.ui.theme.CardConfig
|
||||
import com.sukisu.ultra.ui.util.SuSFSManager
|
||||
import com.sukisu.ultra.ui.util.SuSFSManager.isSusVersion_1_5_8
|
||||
import com.sukisu.ultra.ui.util.SuSFSManager.isSusVersion158
|
||||
import com.sukisu.ultra.ui.util.SuSFSManager.isSusVersion159
|
||||
import com.sukisu.ultra.ui.util.isAbDevice
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
@@ -92,6 +96,7 @@ import java.util.*
|
||||
enum class SuSFSTab(val displayNameRes: Int) {
|
||||
BASIC_SETTINGS(R.string.susfs_tab_basic_settings),
|
||||
SUS_PATHS(R.string.susfs_tab_sus_paths),
|
||||
SUS_LOOP_PATHS(R.string.susfs_tab_sus_loop_paths),
|
||||
SUS_MOUNTS(R.string.susfs_tab_sus_mounts),
|
||||
TRY_UMOUNT(R.string.susfs_tab_try_umount),
|
||||
KSTAT_CONFIG(R.string.susfs_tab_kstat_config),
|
||||
@@ -99,11 +104,11 @@ enum class SuSFSTab(val displayNameRes: Int) {
|
||||
ENABLED_FEATURES(R.string.susfs_tab_enabled_features);
|
||||
|
||||
companion object {
|
||||
fun getAllTabs(isSusVersion_1_5_8: Boolean): List<SuSFSTab> {
|
||||
return if (isSusVersion_1_5_8) {
|
||||
entries.toList()
|
||||
} else {
|
||||
entries.filter { it != PATH_SETTINGS }
|
||||
fun getAllTabs(isSusVersion158: Boolean, isSusVersion159: Boolean): List<SuSFSTab> {
|
||||
return when {
|
||||
isSusVersion159 -> entries.toList()
|
||||
isSusVersion158 -> entries.filter { it != SUS_LOOP_PATHS }
|
||||
else -> entries.filter { it != PATH_SETTINGS && it != SUS_LOOP_PATHS }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,6 +136,7 @@ fun SuSFSConfigScreen(
|
||||
var executeInPostFsData by remember { mutableStateOf(false) }
|
||||
var enableHideBl by remember { mutableStateOf(true) }
|
||||
var enableCleanupResidue by remember { mutableStateOf(false) }
|
||||
var enableAvcLogSpoofing by remember { mutableStateOf(false) }
|
||||
|
||||
// 槽位信息相关状态
|
||||
var slotInfoList by remember { mutableStateOf(emptyList<SuSFSManager.SlotInfo>()) }
|
||||
@@ -140,6 +146,7 @@ fun SuSFSConfigScreen(
|
||||
|
||||
// 路径管理相关状态
|
||||
var susPaths by remember { mutableStateOf(emptySet<String>()) }
|
||||
var susLoopPaths by remember { mutableStateOf(emptySet<String>()) }
|
||||
var susMounts by remember { mutableStateOf(emptySet<String>()) }
|
||||
var tryUmounts by remember { mutableStateOf(emptySet<String>()) }
|
||||
var androidDataPath by remember { mutableStateOf("") }
|
||||
@@ -158,8 +165,13 @@ fun SuSFSConfigScreen(
|
||||
var enabledFeatures by remember { mutableStateOf(emptyList<SuSFSManager.EnabledFeature>()) }
|
||||
var isLoadingFeatures by remember { mutableStateOf(false) }
|
||||
|
||||
// 应用列表相关状态
|
||||
var installedApps by remember { mutableStateOf(emptyList<SuSFSManager.AppInfo>()) }
|
||||
|
||||
// 对话框状态
|
||||
var showAddPathDialog by remember { mutableStateOf(false) }
|
||||
var showAddLoopPathDialog by remember { mutableStateOf(false) }
|
||||
var showAddAppPathDialog by remember { mutableStateOf(false) }
|
||||
var showAddMountDialog by remember { mutableStateOf(false) }
|
||||
var showAddUmountDialog by remember { mutableStateOf(false) }
|
||||
var showRunUmountDialog by remember { mutableStateOf(false) }
|
||||
@@ -168,6 +180,7 @@ fun SuSFSConfigScreen(
|
||||
|
||||
// 编辑状态
|
||||
var editingPath by remember { mutableStateOf<String?>(null) }
|
||||
var editingLoopPath by remember { mutableStateOf<String?>(null) }
|
||||
var editingMount by remember { mutableStateOf<String?>(null) }
|
||||
var editingUmount by remember { mutableStateOf<String?>(null) }
|
||||
var editingKstatConfig by remember { mutableStateOf<String?>(null) }
|
||||
@@ -175,6 +188,7 @@ fun SuSFSConfigScreen(
|
||||
|
||||
// 重置确认对话框状态
|
||||
var showResetPathsDialog by remember { mutableStateOf(false) }
|
||||
var showResetLoopPathsDialog by remember { mutableStateOf(false) }
|
||||
var showResetMountsDialog by remember { mutableStateOf(false) }
|
||||
var showResetUmountsDialog by remember { mutableStateOf(false) }
|
||||
var showResetKstatDialog by remember { mutableStateOf(false) }
|
||||
@@ -186,7 +200,9 @@ fun SuSFSConfigScreen(
|
||||
var selectedBackupFile by remember { mutableStateOf<String?>(null) }
|
||||
var backupInfo by remember { mutableStateOf<SuSFSManager.BackupData?>(null) }
|
||||
|
||||
val allTabs = SuSFSTab.getAllTabs(isSusVersion_1_5_8())
|
||||
var isNavigating by remember { mutableStateOf(false) }
|
||||
|
||||
val allTabs = SuSFSTab.getAllTabs(isSusVersion158(), isSusVersion159())
|
||||
|
||||
// 实时判断是否可以启用开机自启动
|
||||
val canEnableAutoStart by remember {
|
||||
@@ -200,21 +216,22 @@ fun SuSFSConfigScreen(
|
||||
contract = ActivityResultContracts.CreateDocument("application/json")
|
||||
) { uri ->
|
||||
uri?.let { fileUri ->
|
||||
val fileName = SuSFSManager.getRecommendedBackupPath(context)
|
||||
val fileName = SuSFSManager.getDefaultBackupFileName()
|
||||
val tempFile = File(context.cacheDir, fileName)
|
||||
coroutineScope.launch {
|
||||
isLoading = true
|
||||
val success = SuSFSManager.createBackup(context, fileName)
|
||||
val success = SuSFSManager.createBackup(context, tempFile.absolutePath)
|
||||
if (success) {
|
||||
// 复制到用户选择的位置
|
||||
try {
|
||||
context.contentResolver.openOutputStream(fileUri)?.use { outputStream ->
|
||||
java.io.File(fileName).inputStream().use { inputStream ->
|
||||
tempFile.inputStream().use { inputStream ->
|
||||
inputStream.copyTo(outputStream)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
tempFile.delete()
|
||||
}
|
||||
isLoading = false
|
||||
showBackupDialog = false
|
||||
@@ -228,8 +245,7 @@ fun SuSFSConfigScreen(
|
||||
uri?.let { fileUri ->
|
||||
coroutineScope.launch {
|
||||
try {
|
||||
// 复制到临时文件
|
||||
val tempFile = java.io.File(context.cacheDir, "temp_restore.susfs_backup")
|
||||
val tempFile = File(context.cacheDir, "temp_restore.susfs_backup")
|
||||
context.contentResolver.openInputStream(fileUri)?.use { inputStream ->
|
||||
tempFile.outputStream().use { outputStream ->
|
||||
inputStream.copyTo(outputStream)
|
||||
@@ -242,8 +258,6 @@ fun SuSFSConfigScreen(
|
||||
selectedBackupFile = tempFile.absolutePath
|
||||
backupInfo = backup
|
||||
showRestoreConfirmDialog = true
|
||||
} else {
|
||||
// 显示错误消息
|
||||
}
|
||||
tempFile.deleteOnExit()
|
||||
} catch (e: Exception) {
|
||||
@@ -263,6 +277,13 @@ fun SuSFSConfigScreen(
|
||||
}
|
||||
}
|
||||
|
||||
// 加载应用列表
|
||||
fun loadInstalledApps() {
|
||||
coroutineScope.launch {
|
||||
installedApps = SuSFSManager.getInstalledApps()
|
||||
}
|
||||
}
|
||||
|
||||
// 加载槽位信息
|
||||
fun loadSlotInfo() {
|
||||
coroutineScope.launch {
|
||||
@@ -280,6 +301,7 @@ fun SuSFSConfigScreen(
|
||||
autoStartEnabled = SuSFSManager.isAutoStartEnabled(context)
|
||||
executeInPostFsData = SuSFSManager.getExecuteInPostFsData(context)
|
||||
susPaths = SuSFSManager.getSusPaths(context)
|
||||
susLoopPaths = SuSFSManager.getSusLoopPaths(context)
|
||||
susMounts = SuSFSManager.getSusMounts(context)
|
||||
tryUmounts = SuSFSManager.getTryUmounts(context)
|
||||
androidDataPath = SuSFSManager.getAndroidDataPath(context)
|
||||
@@ -290,6 +312,7 @@ fun SuSFSConfigScreen(
|
||||
enableHideBl = SuSFSManager.getEnableHideBl(context)
|
||||
enableCleanupResidue = SuSFSManager.getEnableCleanupResidue(context)
|
||||
umountForZygoteIsoService = SuSFSManager.getUmountForZygoteIsoService(context)
|
||||
enableAvcLogSpoofing = SuSFSManager.getEnableAvcLogSpoofing(context)
|
||||
|
||||
loadSlotInfo()
|
||||
}
|
||||
@@ -449,6 +472,7 @@ fun SuSFSConfigScreen(
|
||||
autoStartEnabled = SuSFSManager.isAutoStartEnabled(context)
|
||||
executeInPostFsData = SuSFSManager.getExecuteInPostFsData(context)
|
||||
susPaths = SuSFSManager.getSusPaths(context)
|
||||
susLoopPaths = SuSFSManager.getSusLoopPaths(context)
|
||||
susMounts = SuSFSManager.getSusMounts(context)
|
||||
tryUmounts = SuSFSManager.getTryUmounts(context)
|
||||
androidDataPath = SuSFSManager.getAndroidDataPath(context)
|
||||
@@ -459,6 +483,7 @@ fun SuSFSConfigScreen(
|
||||
enableHideBl = SuSFSManager.getEnableHideBl(context)
|
||||
enableCleanupResidue = SuSFSManager.getEnableCleanupResidue(context)
|
||||
umountForZygoteIsoService = SuSFSManager.getUmountForZygoteIsoService(context)
|
||||
enableAvcLogSpoofing = SuSFSManager.getEnableAvcLogSpoofing(context)
|
||||
}
|
||||
isLoading = false
|
||||
showRestoreConfirmDialog = false
|
||||
@@ -537,6 +562,60 @@ fun SuSFSConfigScreen(
|
||||
initialValue = editingPath ?: ""
|
||||
)
|
||||
|
||||
AddPathDialog(
|
||||
showDialog = showAddLoopPathDialog,
|
||||
onDismiss = {
|
||||
showAddLoopPathDialog = false
|
||||
editingLoopPath = null
|
||||
},
|
||||
onConfirm = { path ->
|
||||
coroutineScope.launch {
|
||||
isLoading = true
|
||||
val success = if (editingLoopPath != null) {
|
||||
SuSFSManager.editSusLoopPath(context, editingLoopPath!!, path)
|
||||
} else {
|
||||
SuSFSManager.addSusLoopPath(context, path)
|
||||
}
|
||||
if (success) {
|
||||
susLoopPaths = SuSFSManager.getSusLoopPaths(context)
|
||||
}
|
||||
isLoading = false
|
||||
showAddLoopPathDialog = false
|
||||
editingLoopPath = null
|
||||
}
|
||||
},
|
||||
isLoading = isLoading,
|
||||
titleRes = if (editingLoopPath != null) R.string.susfs_edit_sus_loop_path else R.string.susfs_add_sus_loop_path,
|
||||
labelRes = R.string.susfs_loop_path_label,
|
||||
placeholderRes = R.string.susfs_loop_path_placeholder,
|
||||
initialValue = editingLoopPath ?: ""
|
||||
)
|
||||
|
||||
AddAppPathDialog(
|
||||
showDialog = showAddAppPathDialog,
|
||||
onDismiss = { showAddAppPathDialog = false },
|
||||
onConfirm = { packageNames ->
|
||||
coroutineScope.launch {
|
||||
isLoading = true
|
||||
var successCount = 0
|
||||
packageNames.forEach { packageName ->
|
||||
if (SuSFSManager.addAppPaths(context, packageName)) {
|
||||
successCount++
|
||||
}
|
||||
}
|
||||
if (successCount > 0) {
|
||||
susPaths = SuSFSManager.getSusPaths(context)
|
||||
}
|
||||
isLoading = false
|
||||
showAddAppPathDialog = false
|
||||
}
|
||||
},
|
||||
isLoading = isLoading,
|
||||
apps = installedApps,
|
||||
onLoadApps = { loadInstalledApps() },
|
||||
existingSusPaths = susPaths
|
||||
)
|
||||
|
||||
AddPathDialog(
|
||||
showDialog = showAddMountDialog,
|
||||
onDismiss = {
|
||||
@@ -714,6 +793,27 @@ fun SuSFSConfigScreen(
|
||||
isDestructive = true
|
||||
)
|
||||
|
||||
ConfirmDialog(
|
||||
showDialog = showResetLoopPathsDialog,
|
||||
onDismiss = { showResetLoopPathsDialog = false },
|
||||
onConfirm = {
|
||||
coroutineScope.launch {
|
||||
isLoading = true
|
||||
SuSFSManager.saveSusLoopPaths(context, emptySet())
|
||||
susLoopPaths = emptySet()
|
||||
if (SuSFSManager.isAutoStartEnabled(context)) {
|
||||
SuSFSManager.configureAutoStart(context, true)
|
||||
}
|
||||
isLoading = false
|
||||
showResetLoopPathsDialog = false
|
||||
}
|
||||
},
|
||||
titleRes = R.string.susfs_reset_loop_paths_title,
|
||||
messageRes = R.string.susfs_reset_loop_paths_message,
|
||||
isLoading = isLoading,
|
||||
isDestructive = true
|
||||
)
|
||||
|
||||
ConfirmDialog(
|
||||
showDialog = showResetMountsDialog,
|
||||
onDismiss = { showResetMountsDialog = false },
|
||||
@@ -803,7 +903,10 @@ fun SuSFSConfigScreen(
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = {
|
||||
if (!isNavigating) {
|
||||
isNavigating = true
|
||||
navigator.popBackStack()
|
||||
}
|
||||
}) {
|
||||
Icon(
|
||||
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
|
||||
@@ -846,6 +949,7 @@ fun SuSFSConfigScreen(
|
||||
SuSFSManager.saveExecuteInPostFsData(context, executeInPostFsData)
|
||||
SuSFSManager.saveEnableHideBl(context, enableHideBl)
|
||||
SuSFSManager.saveEnableCleanupResidue(context, enableCleanupResidue)
|
||||
SuSFSManager.saveEnableAvcLogSpoofing(context, enableAvcLogSpoofing)
|
||||
}
|
||||
isLoading = false
|
||||
}
|
||||
@@ -907,6 +1011,28 @@ fun SuSFSConfigScreen(
|
||||
}
|
||||
}
|
||||
|
||||
SuSFSTab.SUS_LOOP_PATHS -> {
|
||||
OutlinedButton(
|
||||
onClick = { showResetLoopPathsDialog = true },
|
||||
enabled = !isLoading && susLoopPaths.isNotEmpty(),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(40.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.RestoreFromTrash,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(6.dp))
|
||||
Text(
|
||||
stringResource(R.string.susfs_reset_loop_paths_title),
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
SuSFSTab.SUS_MOUNTS -> {
|
||||
OutlinedButton(
|
||||
onClick = { showResetMountsDialog = true },
|
||||
@@ -1115,6 +1241,17 @@ fun SuSFSConfigScreen(
|
||||
SuSFSManager.configureAutoStart(context, true)
|
||||
}
|
||||
}
|
||||
},
|
||||
enableAvcLogSpoofing = enableAvcLogSpoofing,
|
||||
onEnableAvcLogSpoofingChange = { enabled ->
|
||||
coroutineScope.launch {
|
||||
isLoading = true
|
||||
val success = SuSFSManager.setEnableAvcLogSpoofing(context, enabled)
|
||||
if (success) {
|
||||
enableAvcLogSpoofing = enabled
|
||||
}
|
||||
isLoading = false
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -1123,6 +1260,7 @@ fun SuSFSConfigScreen(
|
||||
susPaths = susPaths,
|
||||
isLoading = isLoading,
|
||||
onAddPath = { showAddPathDialog = true },
|
||||
onAddAppPath = { showAddAppPathDialog = true },
|
||||
onRemovePath = { path ->
|
||||
coroutineScope.launch {
|
||||
isLoading = true
|
||||
@@ -1135,16 +1273,37 @@ fun SuSFSConfigScreen(
|
||||
onEditPath = { path ->
|
||||
editingPath = path
|
||||
showAddPathDialog = true
|
||||
},
|
||||
forceRefreshApps = selectedTab == SuSFSTab.SUS_PATHS
|
||||
)
|
||||
}
|
||||
SuSFSTab.SUS_LOOP_PATHS -> {
|
||||
SusLoopPathsContent(
|
||||
susLoopPaths = susLoopPaths,
|
||||
isLoading = isLoading,
|
||||
onAddLoopPath = { showAddLoopPathDialog = true },
|
||||
onRemoveLoopPath = { path ->
|
||||
coroutineScope.launch {
|
||||
isLoading = true
|
||||
if (SuSFSManager.removeSusLoopPath(context, path)) {
|
||||
susLoopPaths = SuSFSManager.getSusLoopPaths(context)
|
||||
}
|
||||
isLoading = false
|
||||
}
|
||||
},
|
||||
onEditLoopPath = { path ->
|
||||
editingLoopPath = path
|
||||
showAddLoopPathDialog = true
|
||||
}
|
||||
)
|
||||
}
|
||||
SuSFSTab.SUS_MOUNTS -> {
|
||||
val isSusVersion_1_5_8 = remember { isSusVersion_1_5_8() }
|
||||
val isSusVersion158 = remember { isSusVersion158() }
|
||||
|
||||
SusMountsContent(
|
||||
susMounts = susMounts,
|
||||
hideSusMountsForAllProcs = hideSusMountsForAllProcs,
|
||||
isSusVersion_1_5_8 = isSusVersion_1_5_8,
|
||||
isSusVersion158 = isSusVersion158,
|
||||
isLoading = isLoading,
|
||||
onAddMount = { showAddMountDialog = true },
|
||||
onRemoveMount = { mount ->
|
||||
@@ -1312,10 +1471,13 @@ private fun BasicSettingsContent(
|
||||
enableHideBl: Boolean,
|
||||
onEnableHideBlChange: (Boolean) -> Unit,
|
||||
enableCleanupResidue: Boolean,
|
||||
onEnableCleanupResidueChange: (Boolean) -> Unit
|
||||
onEnableCleanupResidueChange: (Boolean) -> Unit,
|
||||
enableAvcLogSpoofing: Boolean,
|
||||
onEnableAvcLogSpoofingChange: (Boolean) -> Unit
|
||||
) {
|
||||
var scriptLocationExpanded by remember { mutableStateOf(false) }
|
||||
val isAbDevice = isAbDevice()
|
||||
val isSusVersion159 = isSusVersion159()
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@@ -1625,6 +1787,66 @@ private fun BasicSettingsContent(
|
||||
}
|
||||
}
|
||||
|
||||
// AVC日志欺骗开关(仅在1.5.9+版本显示)
|
||||
if (isSusVersion159) {
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surface
|
||||
),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(12.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.VisibilityOff,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.avc_log_spoofing),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.avc_log_spoofing_description),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
lineHeight = 14.sp
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.avc_log_spoofing_warning),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
lineHeight = 12.sp
|
||||
)
|
||||
}
|
||||
Switch(
|
||||
checked = enableAvcLogSpoofing,
|
||||
onCheckedChange = onEnableAvcLogSpoofingChange,
|
||||
enabled = !isLoading
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 槽位信息按钮
|
||||
if (isAbDevice) {
|
||||
Card(
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.sukisu.ultra.ui.screen
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.background
|
||||
@@ -26,7 +27,6 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.scale
|
||||
import androidx.compose.ui.draw.rotate
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
@@ -159,7 +159,13 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
|
||||
// 应用分类和排序逻辑
|
||||
val filteredAndSortedApps = remember(viewModel.appList, selectedCategory, currentSortType, viewModel.search) {
|
||||
val filteredAndSortedApps = remember(
|
||||
viewModel.appList,
|
||||
selectedCategory,
|
||||
currentSortType,
|
||||
viewModel.search,
|
||||
viewModel.showSystemApps
|
||||
) {
|
||||
var apps = viewModel.appList
|
||||
|
||||
// 按分类筛选
|
||||
@@ -230,7 +236,7 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
|
||||
// 计算应用数量
|
||||
val appCounts = remember(viewModel.appList) {
|
||||
val appCounts = remember(viewModel.appList, viewModel.showSystemApps) {
|
||||
mapOf(
|
||||
AppCategory.ALL to viewModel.appList.size,
|
||||
AppCategory.ROOT to viewModel.appList.count { it.allowSu },
|
||||
@@ -240,7 +246,7 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
|
||||
}
|
||||
|
||||
// BottomSheet菜单项
|
||||
val bottomSheetMenuItems = remember {
|
||||
val bottomSheetMenuItems = remember(viewModel.showSystemApps) {
|
||||
listOf(
|
||||
BottomSheetMenuItem(
|
||||
icon = Icons.Filled.Refresh,
|
||||
@@ -263,6 +269,7 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
|
||||
onClick = {
|
||||
viewModel.updateShowSystemApps(!viewModel.showSystemApps)
|
||||
scope.launch {
|
||||
kotlinx.coroutines.delay(100)
|
||||
bottomSheetState.hide()
|
||||
showBottomSheet = false
|
||||
}
|
||||
@@ -462,13 +469,14 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
// 根据加载状态显示不同内容
|
||||
if (viewModel.isRefreshing || viewModel.appList.isEmpty()) {
|
||||
if ((viewModel.isRefreshing || viewModel.appList.isEmpty()) && viewModel.search.isEmpty()) {
|
||||
LoadingAnimation(
|
||||
isLoading = true
|
||||
)
|
||||
} else {
|
||||
EmptyState(
|
||||
selectedCategory = selectedCategory
|
||||
selectedCategory = selectedCategory,
|
||||
isSearchEmpty = viewModel.search.isNotEmpty()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -900,17 +908,6 @@ private fun LoadingAnimation(
|
||||
) {
|
||||
val infiniteTransition = rememberInfiniteTransition(label = "loading")
|
||||
|
||||
// 旋转动画
|
||||
val rotation by infiniteTransition.animateFloat(
|
||||
initialValue = 0f,
|
||||
targetValue = 360f,
|
||||
animationSpec = infiniteRepeatable(
|
||||
animation = tween(1000, easing = LinearEasing),
|
||||
repeatMode = RepeatMode.Restart
|
||||
),
|
||||
label = "rotation"
|
||||
)
|
||||
|
||||
// 透明度动画
|
||||
val alpha by infiniteTransition.animateFloat(
|
||||
initialValue = 0.3f,
|
||||
@@ -932,18 +929,6 @@ private fun LoadingAnimation(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
// 主加载图标
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Refresh,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary.copy(alpha = alpha),
|
||||
modifier = Modifier
|
||||
.size(64.dp)
|
||||
.rotate(rotation)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// 进度指示器
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier
|
||||
@@ -960,9 +945,11 @@ private fun LoadingAnimation(
|
||||
* 空状态组件
|
||||
*/
|
||||
@Composable
|
||||
@SuppressLint("ModifierParameter")
|
||||
private fun EmptyState(
|
||||
selectedCategory: AppCategory,
|
||||
modifier: Modifier = Modifier
|
||||
modifier: Modifier = Modifier,
|
||||
isSearchEmpty: Boolean = false
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
@@ -970,7 +957,7 @@ private fun EmptyState(
|
||||
modifier = modifier
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Archive,
|
||||
imageVector = if (isSearchEmpty) Icons.Filled.SearchOff else Icons.Filled.Archive,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary.copy(alpha = 0.6f),
|
||||
modifier = Modifier
|
||||
@@ -978,7 +965,7 @@ private fun EmptyState(
|
||||
.padding(bottom = 16.dp)
|
||||
)
|
||||
Text(
|
||||
text = if (selectedCategory == AppCategory.ALL) {
|
||||
text = if (isSearchEmpty || selectedCategory == AppCategory.ALL) {
|
||||
stringResource(R.string.no_apps_found)
|
||||
} else {
|
||||
stringResource(R.string.no_apps_in_category)
|
||||
|
||||
@@ -1,635 +0,0 @@
|
||||
package com.sukisu.ultra.ui.screen.extensions
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.Folder
|
||||
import androidx.compose.material.icons.filled.PlayArrow
|
||||
import androidx.compose.material.icons.filled.Update
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.filled.Visibility
|
||||
import androidx.compose.material.icons.filled.VisibilityOff
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.sukisu.ultra.R
|
||||
import com.sukisu.ultra.ui.util.SuSFSManager
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
/**
|
||||
* 空状态显示组件
|
||||
*/
|
||||
@Composable
|
||||
fun EmptyStateCard(
|
||||
message: String,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Card(
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.2f)
|
||||
),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(24.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = message,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 路径项目卡片组件
|
||||
*/
|
||||
@Composable
|
||||
fun PathItemCard(
|
||||
path: String,
|
||||
icon: ImageVector,
|
||||
onDelete: () -> Unit,
|
||||
onEdit: (() -> Unit)? = null,
|
||||
isLoading: Boolean = false,
|
||||
additionalInfo: String? = null
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 1.dp),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
elevation = CardDefaults.cardElevation(defaultElevation = 1.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(12.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Column {
|
||||
Text(
|
||||
text = path,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
if (additionalInfo != null) {
|
||||
Text(
|
||||
text = additionalInfo,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
if (onEdit != null) {
|
||||
IconButton(
|
||||
onClick = onEdit,
|
||||
enabled = !isLoading,
|
||||
modifier = Modifier.size(32.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Edit,
|
||||
contentDescription = stringResource(R.string.edit),
|
||||
tint = MaterialTheme.colorScheme.secondary,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
IconButton(
|
||||
onClick = onDelete,
|
||||
enabled = !isLoading,
|
||||
modifier = Modifier.size(32.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Delete,
|
||||
contentDescription = stringResource(R.string.delete),
|
||||
tint = MaterialTheme.colorScheme.error,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Kstat配置项目卡片组件
|
||||
*/
|
||||
@Composable
|
||||
fun KstatConfigItemCard(
|
||||
config: String,
|
||||
onDelete: () -> Unit,
|
||||
onEdit: (() -> Unit)? = null,
|
||||
isLoading: Boolean = false
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 1.dp),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
elevation = CardDefaults.cardElevation(defaultElevation = 1.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(12.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Settings,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Column {
|
||||
val parts = config.split("|")
|
||||
if (parts.isNotEmpty()) {
|
||||
Text(
|
||||
text = parts[0], // 路径
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
if (parts.size > 1) {
|
||||
Text(
|
||||
text = "${parts.drop(1).joinToString(" ")}",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Text(
|
||||
text = config,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
if (onEdit != null) {
|
||||
IconButton(
|
||||
onClick = onEdit,
|
||||
enabled = !isLoading,
|
||||
modifier = Modifier.size(32.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Edit,
|
||||
contentDescription = stringResource(R.string.edit),
|
||||
tint = MaterialTheme.colorScheme.secondary,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
IconButton(
|
||||
onClick = onDelete,
|
||||
enabled = !isLoading,
|
||||
modifier = Modifier.size(32.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Delete,
|
||||
contentDescription = stringResource(R.string.delete),
|
||||
tint = MaterialTheme.colorScheme.error,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Kstat路径项目卡片组件
|
||||
*/
|
||||
@Composable
|
||||
fun AddKstatPathItemCard(
|
||||
path: String,
|
||||
onDelete: () -> Unit,
|
||||
onEdit: (() -> Unit)? = null,
|
||||
onUpdate: () -> Unit,
|
||||
onUpdateFullClone: () -> Unit,
|
||||
isLoading: Boolean = false
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 1.dp),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
elevation = CardDefaults.cardElevation(defaultElevation = 1.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(12.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Folder,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(18.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Text(
|
||||
text = path,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
}
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
if (onEdit != null) {
|
||||
IconButton(
|
||||
onClick = onEdit,
|
||||
enabled = !isLoading,
|
||||
modifier = Modifier.size(32.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Edit,
|
||||
contentDescription = stringResource(R.string.edit),
|
||||
tint = MaterialTheme.colorScheme.secondary,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
IconButton(
|
||||
onClick = onUpdate,
|
||||
enabled = !isLoading,
|
||||
modifier = Modifier.size(32.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Update,
|
||||
contentDescription = stringResource(R.string.update),
|
||||
tint = MaterialTheme.colorScheme.secondary,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = onUpdateFullClone,
|
||||
enabled = !isLoading,
|
||||
modifier = Modifier.size(32.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.PlayArrow,
|
||||
contentDescription = stringResource(R.string.susfs_update_full_clone),
|
||||
tint = MaterialTheme.colorScheme.tertiary,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
}
|
||||
IconButton(
|
||||
onClick = onDelete,
|
||||
enabled = !isLoading,
|
||||
modifier = Modifier.size(32.dp)
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.Delete,
|
||||
contentDescription = stringResource(R.string.delete),
|
||||
tint = MaterialTheme.colorScheme.error,
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 启用功能状态卡片组件
|
||||
*/
|
||||
@Composable
|
||||
fun FeatureStatusCard(
|
||||
feature: SuSFSManager.EnabledFeature,
|
||||
onRefresh: (() -> Unit)? = null,
|
||||
@SuppressLint("ModifierParameter") modifier: Modifier = Modifier
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
// 日志配置对话框状态
|
||||
var showLogConfigDialog by remember { mutableStateOf(false) }
|
||||
var logEnabled by remember { mutableStateOf(SuSFSManager.getEnableLogState(context)) }
|
||||
|
||||
// 日志配置对话框
|
||||
if (showLogConfigDialog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showLogConfigDialog = false },
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.susfs_log_config_title),
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.susfs_log_config_description),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.susfs_enable_log_label),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
Switch(
|
||||
checked = logEnabled,
|
||||
onCheckedChange = { logEnabled = it }
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
coroutineScope.launch {
|
||||
if (SuSFSManager.setEnableLog(context, logEnabled)) {
|
||||
onRefresh?.invoke()
|
||||
}
|
||||
showLogConfigDialog = false
|
||||
}
|
||||
},
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.susfs_apply))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
// 恢复原始状态
|
||||
logEnabled = SuSFSManager.getEnableLogState(context)
|
||||
showLogConfigDialog = false
|
||||
},
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
},
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
)
|
||||
}
|
||||
|
||||
Card(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 1.dp)
|
||||
.then(
|
||||
if (feature.canConfigure) {
|
||||
Modifier.clickable {
|
||||
// 更新当前状态
|
||||
logEnabled = SuSFSManager.getEnableLogState(context)
|
||||
showLogConfigDialog = true
|
||||
}
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
elevation = CardDefaults.cardElevation(defaultElevation = 1.dp)
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(12.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text(
|
||||
text = feature.name,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
if (feature.canConfigure) {
|
||||
Text(
|
||||
text = stringResource(R.string.susfs_feature_configurable),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// 状态标签
|
||||
Surface(
|
||||
shape = RoundedCornerShape(6.dp),
|
||||
color = when {
|
||||
feature.isEnabled -> MaterialTheme.colorScheme.primary
|
||||
else -> Color.Gray
|
||||
}
|
||||
) {
|
||||
Text(
|
||||
text = feature.statusText,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = when {
|
||||
feature.isEnabled -> MaterialTheme.colorScheme.onPrimary
|
||||
else -> Color.White
|
||||
},
|
||||
modifier = Modifier.padding(horizontal = 8.dp, vertical = 3.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* SUS挂载隐藏控制卡片组件
|
||||
*/
|
||||
@Composable
|
||||
fun SusMountHidingControlCard(
|
||||
hideSusMountsForAllProcs: Boolean,
|
||||
isLoading: Boolean,
|
||||
onToggleHiding: (Boolean) -> Unit
|
||||
) {
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surface
|
||||
),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
// 标题行
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
imageVector = if (hideSusMountsForAllProcs) Icons.Default.VisibilityOff else Icons.Default.Visibility,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
modifier = Modifier.size(20.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.susfs_hide_mounts_control_title),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
}
|
||||
|
||||
// 描述文本
|
||||
Text(
|
||||
text = stringResource(R.string.susfs_hide_mounts_control_description),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
lineHeight = 16.sp
|
||||
)
|
||||
|
||||
// 控制开关行
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.susfs_hide_mounts_for_all_procs_label),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
fontWeight = FontWeight.Medium,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Text(
|
||||
text = if (hideSusMountsForAllProcs) {
|
||||
stringResource(R.string.susfs_hide_mounts_for_all_procs_enabled_description)
|
||||
} else {
|
||||
stringResource(R.string.susfs_hide_mounts_for_all_procs_disabled_description)
|
||||
},
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
lineHeight = 14.sp
|
||||
)
|
||||
}
|
||||
Switch(
|
||||
checked = hideSusMountsForAllProcs,
|
||||
onCheckedChange = onToggleHiding,
|
||||
enabled = !isLoading
|
||||
)
|
||||
}
|
||||
|
||||
// 当前设置显示
|
||||
Text(
|
||||
text = stringResource(
|
||||
R.string.susfs_hide_mounts_current_setting,
|
||||
if (hideSusMountsForAllProcs) {
|
||||
stringResource(R.string.susfs_hide_mounts_setting_all)
|
||||
} else {
|
||||
stringResource(R.string.susfs_hide_mounts_setting_non_ksu)
|
||||
}
|
||||
),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
|
||||
// 建议文本
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
colors = CardDefaults.cardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f)
|
||||
),
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.susfs_hide_mounts_recommendation),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
lineHeight = 14.sp,
|
||||
modifier = Modifier.padding(12.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -223,7 +223,11 @@ fun restoreBoot(
|
||||
onFinish: (Boolean, Int) -> Unit, onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||
): Boolean {
|
||||
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libzakoboot.so")
|
||||
val result = flashWithIO("${getKsuDaemonPath()} boot-restore -f --magiskboot $magiskboot", onStdout, onStderr)
|
||||
val result = flashWithIO(
|
||||
"${getKsuDaemonPath()} boot-restore -f --magiskboot $magiskboot",
|
||||
onStdout,
|
||||
onStderr
|
||||
)
|
||||
onFinish(result.isSuccess, result.code)
|
||||
return result.isSuccess
|
||||
}
|
||||
@@ -232,7 +236,8 @@ fun uninstallPermanently(
|
||||
onFinish: (Boolean, Int) -> Unit, onStdout: (String) -> Unit, onStderr: (String) -> Unit
|
||||
): Boolean {
|
||||
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libzakoboot.so")
|
||||
val result = flashWithIO("${getKsuDaemonPath()} uninstall --magiskboot $magiskboot", onStdout, onStderr)
|
||||
val result =
|
||||
flashWithIO("${getKsuDaemonPath()} uninstall --magiskboot $magiskboot", onStdout, onStderr)
|
||||
onFinish(result.isSuccess, result.code)
|
||||
return result.isSuccess
|
||||
}
|
||||
@@ -457,6 +462,7 @@ fun getSuSFSVariant(): String {
|
||||
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} variant")
|
||||
return result
|
||||
}
|
||||
|
||||
fun getSuSFSFeatures(): String {
|
||||
val shell = getRootShell()
|
||||
val result = ShellUtils.fastCmd(shell, "${getSuSFSDaemonPath()} features")
|
||||
@@ -548,3 +554,18 @@ fun getKpmVersion(): String {
|
||||
val result = ShellUtils.fastCmd(shell, cmd)
|
||||
return result.trim()
|
||||
}
|
||||
|
||||
fun getZygiskImplement(): String {
|
||||
val shell = getRootShell()
|
||||
val zygiskPath = "/data/adb/modules/zygisksu"
|
||||
val rezygiskPath = "/data/adb/modules/rezygisk"
|
||||
val result = if (ShellUtils.fastCmdResult(shell, "test -f $zygiskPath/module.prop && test ! -f $zygiskPath/disable")) {
|
||||
ShellUtils.fastCmd(shell, "grep '^name=' $zygiskPath/module.prop | cut -d'=' -f2")
|
||||
} else if (ShellUtils.fastCmdResult(shell, "test -f $rezygiskPath/module.prop && test ! -f $rezygiskPath/disable")) {
|
||||
ShellUtils.fastCmd(shell, "grep '^name=' $rezygiskPath/module.prop | cut -d'=' -f2")
|
||||
} else {
|
||||
"None"
|
||||
}
|
||||
Log.i(TAG, "Zygisk implement: $result")
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ object ModuleUtils {
|
||||
}
|
||||
}?.removeSuffix(".zip") ?: context.getString(R.string.unknown_module)
|
||||
|
||||
var formattedFileName = fileName.replace(Regex("[^a-zA-Z0-9\\s\\-_.@()\\u4e00-\\u9fa5]"), "").trim()
|
||||
val formattedFileName = fileName.replace(Regex("[^a-zA-Z0-9\\s\\-_.@()\\u4e00-\\u9fa5]"), "").trim()
|
||||
var moduleName = formattedFileName
|
||||
|
||||
try {
|
||||
@@ -52,12 +52,10 @@ object ModuleUtils {
|
||||
if (entry.name == "module.prop") {
|
||||
val reader = BufferedReader(InputStreamReader(zipInputStream, StandardCharsets.UTF_8))
|
||||
var line: String?
|
||||
var nameFound = false
|
||||
while (reader.readLine().also { line = it } != null) {
|
||||
if (line?.startsWith("name=") == true) {
|
||||
moduleName = line.substringAfter("=")
|
||||
moduleName = moduleName.replace(Regex("[^a-zA-Z0-9\\s\\-_.@()\\u4e00-\\u9fa5]"), "").trim()
|
||||
nameFound = true
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -102,4 +100,43 @@ object ModuleUtils {
|
||||
Log.e(TAG, "Unable to get persistent permissions on URIs: $uri, Error: ${e.message}")
|
||||
}
|
||||
}
|
||||
|
||||
fun extractModuleId(context: Context, uri: Uri): String? {
|
||||
if (uri == Uri.EMPTY) {
|
||||
return null
|
||||
}
|
||||
|
||||
return try {
|
||||
|
||||
val inputStream = context.contentResolver.openInputStream(uri)
|
||||
if (inputStream == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val zipInputStream = ZipInputStream(inputStream)
|
||||
var entry = zipInputStream.nextEntry
|
||||
var moduleId: String? = null
|
||||
|
||||
// 遍历ZIP文件中的条目,查找module.prop文件
|
||||
while (entry != null) {
|
||||
if (entry.name == "module.prop") {
|
||||
val reader = BufferedReader(InputStreamReader(zipInputStream, StandardCharsets.UTF_8))
|
||||
var line: String?
|
||||
while (reader.readLine().also { line = it } != null) {
|
||||
if (line?.startsWith("id=") == true) {
|
||||
moduleId = line.substringAfter("=").trim()
|
||||
break
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
entry = zipInputStream.nextEntry
|
||||
}
|
||||
zipInputStream.close()
|
||||
moduleId
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "提取模块ID时发生异常: ${e.message}", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,232 @@
|
||||
package com.sukisu.ultra.ui.util
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import com.sukisu.ultra.Natives
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
|
||||
/**
|
||||
* @author ShirkNeko
|
||||
* @date 2025/8/3
|
||||
*/
|
||||
|
||||
// 模块签名验证工具类
|
||||
object ModuleSignatureUtils {
|
||||
private const val TAG = "ModuleSignatureUtils"
|
||||
|
||||
fun verifyModuleSignature(context: Context, moduleUri: Uri): Boolean {
|
||||
return try {
|
||||
// 创建临时文件
|
||||
val tempFile = File(context.cacheDir, "temp_module_${System.currentTimeMillis()}.zip")
|
||||
|
||||
// 复制URI内容到临时文件
|
||||
context.contentResolver.openInputStream(moduleUri)?.use { inputStream ->
|
||||
FileOutputStream(tempFile).use { outputStream ->
|
||||
inputStream.copyTo(outputStream)
|
||||
}
|
||||
}
|
||||
|
||||
// 调用native方法验证签名
|
||||
val isVerified = Natives.verifyModuleSignature(tempFile.absolutePath)
|
||||
|
||||
// 清理临时文件
|
||||
tempFile.delete()
|
||||
|
||||
Log.d(TAG, "Module signature verification result: $isVerified")
|
||||
isVerified
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error verifying module signature", e)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 验证模块签名
|
||||
fun verifyModuleSignature(context: Context, moduleUri: Uri): Boolean {
|
||||
return ModuleSignatureUtils.verifyModuleSignature(context, moduleUri)
|
||||
}
|
||||
|
||||
object ModuleOperationUtils {
|
||||
private const val TAG = "ModuleOperationUtils"
|
||||
|
||||
fun handleModuleInstallSuccess(context: Context, moduleUri: Uri, isSignatureVerified: Boolean) {
|
||||
if (!isSignatureVerified) {
|
||||
Log.d(TAG, "模块签名未验证,跳过创建验证标志")
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 从ZIP文件提取模块ID
|
||||
val moduleId = ModuleUtils.extractModuleId(context, moduleUri)
|
||||
if (moduleId == null) {
|
||||
Log.e(TAG, "无法提取模块ID,无法创建验证标志")
|
||||
return
|
||||
}
|
||||
|
||||
// 创建验证标志文件
|
||||
val success = ModuleVerificationManager.createVerificationFlag(moduleId)
|
||||
if (success) {
|
||||
Log.d(TAG, "模块 $moduleId 验证标志创建成功")
|
||||
} else {
|
||||
Log.e(TAG, "模块 $moduleId 验证标志创建失败")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "处理模块安装成功时发生异常", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun handleModuleUninstall(moduleId: String) {
|
||||
try {
|
||||
val success = ModuleVerificationManager.removeVerificationFlag(moduleId)
|
||||
if (success) {
|
||||
Log.d(TAG, "模块 $moduleId 验证标志移除成功")
|
||||
} else {
|
||||
Log.d(TAG, "模块 $moduleId 验证标志移除失败或不存在")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "处理模块卸载时发生异常: $moduleId", e)
|
||||
}
|
||||
}
|
||||
fun handleModuleUpdate(context: Context, moduleUri: Uri, isSignatureVerified: Boolean) {
|
||||
try {
|
||||
val moduleId = ModuleUtils.extractModuleId(context, moduleUri)
|
||||
if (moduleId == null) {
|
||||
Log.e(TAG, "无法提取模块ID,无法处理验证标志")
|
||||
return
|
||||
}
|
||||
|
||||
if (isSignatureVerified) {
|
||||
// 签名验证通过,创建或更新验证标志
|
||||
val success = ModuleVerificationManager.createVerificationFlag(moduleId)
|
||||
if (success) {
|
||||
Log.d(TAG, "模块 $moduleId 更新后验证标志已更新")
|
||||
} else {
|
||||
Log.e(TAG, "模块 $moduleId 更新后验证标志更新失败")
|
||||
}
|
||||
} else {
|
||||
// 签名验证失败,移除验证标志
|
||||
ModuleVerificationManager.removeVerificationFlag(moduleId)
|
||||
Log.d(TAG, "模块 $moduleId 更新后签名未验证,验证标志已移除")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "处理模块更新时发生异常", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ModuleVerificationManager {
|
||||
private const val TAG = "ModuleVerificationManager"
|
||||
private const val VERIFICATION_FLAGS_DIR = "/data/adb/ksu/verified_modules"
|
||||
|
||||
// 为指定模块创建验证标志文件
|
||||
fun createVerificationFlag(moduleId: String): Boolean {
|
||||
return try {
|
||||
val shell = getRootShell()
|
||||
val flagFilePath = "$VERIFICATION_FLAGS_DIR/$moduleId"
|
||||
|
||||
// 确保目录存在
|
||||
val createDirCommand = "mkdir -p '$VERIFICATION_FLAGS_DIR'"
|
||||
shell.newJob().add(createDirCommand).exec()
|
||||
|
||||
// 创建验证标志文件,写入验证时间戳
|
||||
val timestamp = System.currentTimeMillis()
|
||||
val command = "echo '$timestamp' > '$flagFilePath'"
|
||||
|
||||
val result = shell.newJob().add(command).exec()
|
||||
|
||||
if (result.isSuccess) {
|
||||
Log.d(TAG, "验证标志文件创建成功: $flagFilePath")
|
||||
true
|
||||
} else {
|
||||
Log.e(TAG, "验证标志文件创建失败: $moduleId")
|
||||
false
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "创建验证标志文件时发生异常: $moduleId", e)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun removeVerificationFlag(moduleId: String): Boolean {
|
||||
return try {
|
||||
val shell = getRootShell()
|
||||
val flagFilePath = "$VERIFICATION_FLAGS_DIR/$moduleId"
|
||||
|
||||
val command = "rm -f '$flagFilePath'"
|
||||
val result = shell.newJob().add(command).exec()
|
||||
|
||||
if (result.isSuccess) {
|
||||
Log.d(TAG, "验证标志文件移除成功: $flagFilePath")
|
||||
true
|
||||
} else {
|
||||
Log.e(TAG, "验证标志文件移除失败: $moduleId")
|
||||
false
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "移除验证标志文件时发生异常: $moduleId", e)
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
fun getVerificationTimestamp(moduleId: String): Long {
|
||||
return try {
|
||||
val shell = getRootShell()
|
||||
val flagFilePath = "$VERIFICATION_FLAGS_DIR/$moduleId"
|
||||
|
||||
val command = "cat '$flagFilePath' 2>/dev/null || echo '0'"
|
||||
val result = shell.newJob().add(command).to(ArrayList(), null).exec()
|
||||
|
||||
if (result.isSuccess && result.out.isNotEmpty()) {
|
||||
val timestampStr = result.out.firstOrNull()?.trim() ?: "0"
|
||||
timestampStr.toLongOrNull() ?: 0L
|
||||
} else {
|
||||
0L
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "获取验证时间戳时发生异常: $moduleId", e)
|
||||
0L
|
||||
}
|
||||
}
|
||||
|
||||
fun batchCheckVerificationStatus(moduleIds: List<String>): Map<String, Boolean> {
|
||||
if (moduleIds.isEmpty()) return emptyMap()
|
||||
|
||||
return try {
|
||||
val shell = getRootShell()
|
||||
val result = mutableMapOf<String, Boolean>()
|
||||
|
||||
// 确保目录存在
|
||||
val createDirCommand = "mkdir -p '$VERIFICATION_FLAGS_DIR'"
|
||||
shell.newJob().add(createDirCommand).exec()
|
||||
|
||||
// 批量检查所有模块的验证标志文件
|
||||
val commands = moduleIds.map { moduleId ->
|
||||
"test -f '$VERIFICATION_FLAGS_DIR/$moduleId' && echo '$moduleId:true' || echo '$moduleId:false'"
|
||||
}
|
||||
|
||||
val command = commands.joinToString(" && ")
|
||||
val shellResult = shell.newJob().add(command).to(ArrayList(), null).exec()
|
||||
|
||||
if (shellResult.isSuccess) {
|
||||
shellResult.out.forEach { line ->
|
||||
val parts = line.split(":")
|
||||
if (parts.size == 2) {
|
||||
val moduleId = parts[0]
|
||||
val isVerified = parts[1] == "true"
|
||||
result[moduleId] = isVerified
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "批量验证检查完成,共检查 ${moduleIds.size} 个模块")
|
||||
result
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "批量检查验证状态时发生异常", e)
|
||||
// 返回默认值,所有模块都标记为未验证
|
||||
moduleIds.associateWith { false }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,9 @@ package com.sukisu.ultra.ui.util
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageInfo
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import com.dergoogler.mmrl.platform.Platform.Companion.context
|
||||
import com.sukisu.ultra.Natives
|
||||
@@ -16,6 +19,9 @@ import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import androidx.core.content.edit
|
||||
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import org.json.JSONObject
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
@@ -30,6 +36,7 @@ object SuSFSManager {
|
||||
private const val KEY_BUILD_TIME_VALUE = "build_time_value"
|
||||
private const val KEY_AUTO_START_ENABLED = "auto_start_enabled"
|
||||
private const val KEY_SUS_PATHS = "sus_paths"
|
||||
private const val KEY_SUS_LOOP_PATHS = "sus_loop_paths"
|
||||
private const val KEY_SUS_MOUNTS = "sus_mounts"
|
||||
private const val KEY_TRY_UMOUNTS = "try_umounts"
|
||||
private const val KEY_ANDROID_DATA_PATH = "android_data_path"
|
||||
@@ -42,16 +49,20 @@ object SuSFSManager {
|
||||
private const val KEY_ENABLE_CLEANUP_RESIDUE = "enable_cleanup_residue"
|
||||
private const val KEY_ENABLE_HIDE_BL = "enable_hide_bl"
|
||||
private const val KEY_UMOUNT_FOR_ZYGOTE_ISO_SERVICE = "umount_for_zygote_iso_service"
|
||||
private const val KEY_ENABLE_AVC_LOG_SPOOFING = "enable_avc_log_spoofing"
|
||||
|
||||
|
||||
// 常量
|
||||
private const val SUSFS_BINARY_BASE_NAME = "ksu_susfs"
|
||||
private const val SUSFS_BINARY_TARGET_NAME = "ksu_susfs"
|
||||
private const val DEFAULT_UNAME = "default"
|
||||
private const val DEFAULT_BUILD_TIME = "default"
|
||||
private const val MODULE_ID = "susfs_manager"
|
||||
private const val MODULE_PATH = "/data/adb/modules/$MODULE_ID"
|
||||
private const val MIN_VERSION_FOR_HIDE_MOUNT = "1.5.8"
|
||||
private const val MIN_VERSION_FOR_LOOP_PATH = "1.5.9"
|
||||
private const val BACKUP_FILE_EXTENSION = ".susfs_backup"
|
||||
private const val MEDIA_DATA_PATH = "/data/media/0/Android/data"
|
||||
private const val CGROUP_UID_PATH_PREFIX = "/sys/fs/cgroup/uid_"
|
||||
|
||||
data class SlotInfo(val slotName: String, val uname: String, val buildTime: String)
|
||||
data class CommandResult(val isSuccess: Boolean, val output: String, val errorOutput: String = "")
|
||||
@@ -62,6 +73,16 @@ object SuSFSManager {
|
||||
val canConfigure: Boolean = false
|
||||
)
|
||||
|
||||
/**
|
||||
* 应用信息数据类
|
||||
*/
|
||||
data class AppInfo(
|
||||
val packageName: String,
|
||||
val appName: String,
|
||||
val packageInfo: PackageInfo,
|
||||
val isSystemApp: Boolean
|
||||
)
|
||||
|
||||
/**
|
||||
* 备份数据类
|
||||
*/
|
||||
@@ -125,6 +146,7 @@ object SuSFSManager {
|
||||
val buildTimeValue: String,
|
||||
val executeInPostFsData: Boolean,
|
||||
val susPaths: Set<String>,
|
||||
val susLoopPaths: Set<String>,
|
||||
val susMounts: Set<String>,
|
||||
val tryUmounts: Set<String>,
|
||||
val androidDataPath: String,
|
||||
@@ -136,7 +158,8 @@ object SuSFSManager {
|
||||
val support158: Boolean,
|
||||
val enableHideBl: Boolean,
|
||||
val enableCleanupResidue: Boolean,
|
||||
val umountForZygoteIsoService: Boolean
|
||||
val umountForZygoteIsoService: Boolean,
|
||||
val enableAvcLogSpoofing: Boolean
|
||||
) {
|
||||
/**
|
||||
* 检查是否有需要自启动的配置
|
||||
@@ -145,6 +168,7 @@ object SuSFSManager {
|
||||
return unameValue != DEFAULT_UNAME ||
|
||||
buildTimeValue != DEFAULT_BUILD_TIME ||
|
||||
susPaths.isNotEmpty() ||
|
||||
susLoopPaths.isNotEmpty() ||
|
||||
susMounts.isNotEmpty() ||
|
||||
tryUmounts.isNotEmpty() ||
|
||||
kstatConfigs.isNotEmpty() ||
|
||||
@@ -160,9 +184,9 @@ object SuSFSManager {
|
||||
getSuSFSVersion()
|
||||
} catch (_: Exception) { MIN_VERSION_FOR_HIDE_MOUNT }
|
||||
|
||||
private fun getSuSFSBinaryName(): String = "${SUSFS_BINARY_BASE_NAME}_${getSuSFSVersionUse().removePrefix("v")}"
|
||||
private fun getSuSFSBinaryName(): String = "${SUSFS_BINARY_TARGET_NAME}_${getSuSFSVersionUse().removePrefix("v")}"
|
||||
|
||||
private fun getSuSFSTargetPath(): String = "/data/adb/ksu/bin/${getSuSFSBinaryName()}"
|
||||
private fun getSuSFSTargetPath(): String = "/data/adb/ksu/bin/$SUSFS_BINARY_TARGET_NAME"
|
||||
|
||||
private fun runCmd(shell: Shell, cmd: String): String {
|
||||
return shell.newJob()
|
||||
@@ -199,14 +223,26 @@ object SuSFSManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* 版本检查方法
|
||||
* 检查是否支持设置sdcard路径等功能(1.5.8+)
|
||||
*/
|
||||
fun isSusVersion_1_5_8(): Boolean {
|
||||
fun isSusVersion158(): Boolean {
|
||||
return try {
|
||||
val currentVersion = getSuSFSVersion()
|
||||
compareVersions(currentVersion, MIN_VERSION_FOR_HIDE_MOUNT) >= 0
|
||||
} catch (_: Exception) {
|
||||
true // 默认支持新功能
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查是否支持循环路径和AVC日志欺骗等功能(1.5.9+)
|
||||
*/
|
||||
fun isSusVersion159(): Boolean {
|
||||
return try {
|
||||
val currentVersion = getSuSFSVersion()
|
||||
compareVersions(currentVersion, MIN_VERSION_FOR_LOOP_PATH) >= 0
|
||||
} catch (_: Exception) {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,6 +256,7 @@ object SuSFSManager {
|
||||
buildTimeValue = getBuildTimeValue(context),
|
||||
executeInPostFsData = getExecuteInPostFsData(context),
|
||||
susPaths = getSusPaths(context),
|
||||
susLoopPaths = getSusLoopPaths(context),
|
||||
susMounts = getSusMounts(context),
|
||||
tryUmounts = getTryUmounts(context),
|
||||
androidDataPath = getAndroidDataPath(context),
|
||||
@@ -228,10 +265,11 @@ object SuSFSManager {
|
||||
kstatConfigs = getKstatConfigs(context),
|
||||
addKstatPaths = getAddKstatPaths(context),
|
||||
hideSusMountsForAllProcs = getHideSusMountsForAllProcs(context),
|
||||
support158 = isSusVersion_1_5_8(),
|
||||
support158 = isSusVersion158(),
|
||||
enableHideBl = getEnableHideBl(context),
|
||||
enableCleanupResidue = getEnableCleanupResidue(context),
|
||||
umountForZygoteIsoService = getUmountForZygoteIsoService(context),
|
||||
enableAvcLogSpoofing = getEnableAvcLogSpoofing(context)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -301,6 +339,13 @@ object SuSFSManager {
|
||||
fun getUmountForZygoteIsoService(context: Context): Boolean =
|
||||
getPrefs(context).getBoolean(KEY_UMOUNT_FOR_ZYGOTE_ISO_SERVICE, false)
|
||||
|
||||
// AVC日志欺骗配置
|
||||
fun saveEnableAvcLogSpoofing(context: Context, enabled: Boolean) =
|
||||
getPrefs(context).edit { putBoolean(KEY_ENABLE_AVC_LOG_SPOOFING, enabled) }
|
||||
|
||||
fun getEnableAvcLogSpoofing(context: Context): Boolean =
|
||||
getPrefs(context).getBoolean(KEY_ENABLE_AVC_LOG_SPOOFING, false)
|
||||
|
||||
|
||||
// 路径和配置管理
|
||||
fun saveSusPaths(context: Context, paths: Set<String>) =
|
||||
@@ -309,6 +354,13 @@ object SuSFSManager {
|
||||
fun getSusPaths(context: Context): Set<String> =
|
||||
getPrefs(context).getStringSet(KEY_SUS_PATHS, emptySet()) ?: emptySet()
|
||||
|
||||
// 循环路径管理
|
||||
fun saveSusLoopPaths(context: Context, paths: Set<String>) =
|
||||
getPrefs(context).edit { putStringSet(KEY_SUS_LOOP_PATHS, paths) }
|
||||
|
||||
fun getSusLoopPaths(context: Context): Set<String> =
|
||||
getPrefs(context).getStringSet(KEY_SUS_LOOP_PATHS, emptySet()) ?: emptySet()
|
||||
|
||||
fun saveSusMounts(context: Context, mounts: Set<String>) =
|
||||
getPrefs(context).edit { putStringSet(KEY_SUS_MOUNTS, mounts) }
|
||||
|
||||
@@ -349,6 +401,123 @@ object SuSFSManager {
|
||||
fun getSdcardPath(context: Context): String =
|
||||
getPrefs(context).getString(KEY_SDCARD_PATH, "/sdcard") ?: "/sdcard"
|
||||
|
||||
// 获取已安装的应用列表
|
||||
@SuppressLint("QueryPermissionsNeeded")
|
||||
suspend fun getInstalledApps(): List<AppInfo> = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val allApps = mutableMapOf<String, AppInfo>()
|
||||
|
||||
// 从SuperUser中获取应用
|
||||
SuperUserViewModel.apps.forEach { superUserApp ->
|
||||
try {
|
||||
val isSystemApp = superUserApp.packageInfo.applicationInfo?.let {
|
||||
(it.flags and ApplicationInfo.FLAG_SYSTEM) != 0
|
||||
} ?: false
|
||||
if (!isSystemApp) {
|
||||
allApps[superUserApp.packageName] = AppInfo(
|
||||
packageName = superUserApp.packageName,
|
||||
appName = superUserApp.label,
|
||||
packageInfo = superUserApp.packageInfo,
|
||||
isSystemApp = false
|
||||
)
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
|
||||
// 检查每个应用的数据目录是否存在
|
||||
val filteredApps = allApps.values.map { appInfo ->
|
||||
async(Dispatchers.IO) {
|
||||
val dataPath = "$MEDIA_DATA_PATH/${appInfo.packageName}"
|
||||
val exists = try {
|
||||
val shell = getRootShell()
|
||||
val outputList = mutableListOf<String>()
|
||||
val errorList = mutableListOf<String>()
|
||||
|
||||
val result = shell.newJob()
|
||||
.add("[ -d \"$dataPath\" ] && echo 'exists' || echo 'not_exists'")
|
||||
.to(outputList, errorList)
|
||||
.exec()
|
||||
|
||||
result.isSuccess && outputList.isNotEmpty() && outputList[0].trim() == "exists"
|
||||
} catch (e: Exception) {
|
||||
Log.w("SuSFSManager", "Failed to check directory for ${appInfo.packageName}: ${e.message}")
|
||||
false
|
||||
}
|
||||
if (exists) appInfo else null
|
||||
}
|
||||
}.awaitAll().filterNotNull()
|
||||
|
||||
filteredApps.sortedBy { it.appName }
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
// 获取应用的UID
|
||||
private suspend fun getAppUid(context: Context, packageName: String): Int? = withContext(Dispatchers.IO) {
|
||||
try {
|
||||
// 从SuperUserViewModel中查找
|
||||
val superUserApp = SuperUserViewModel.apps.find { it.packageName == packageName }
|
||||
if (superUserApp != null) {
|
||||
return@withContext superUserApp.packageInfo.applicationInfo?.uid
|
||||
}
|
||||
|
||||
// 从PackageManager中查找
|
||||
val packageManager = context.packageManager
|
||||
val packageInfo = packageManager.getPackageInfo(packageName, 0)
|
||||
packageInfo.applicationInfo?.uid
|
||||
} catch (e: Exception) {
|
||||
Log.w("SuSFSManager", "Failed to get UID for package $packageName: ${e.message}")
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildUidPath(uid: Int): String = "$CGROUP_UID_PATH_PREFIX$uid"
|
||||
|
||||
|
||||
// 快捷添加应用路径
|
||||
suspend fun addAppPaths(context: Context, packageName: String): Boolean {
|
||||
val androidDataPath = getAndroidDataPath(context)
|
||||
getSdcardPath(context)
|
||||
|
||||
val path1 = "$androidDataPath/$packageName"
|
||||
val path2 = "$MEDIA_DATA_PATH/$packageName"
|
||||
|
||||
val uid = getAppUid(context, packageName)
|
||||
if (uid == null) {
|
||||
Log.w("SuSFSManager", "Failed to get UID for package: $packageName")
|
||||
return false
|
||||
}
|
||||
|
||||
val path3 = buildUidPath(uid)
|
||||
|
||||
var successCount = 0
|
||||
val totalCount = 3
|
||||
|
||||
// 添加第一个路径(Android/data路径)
|
||||
if (addSusPath(context, path1)) {
|
||||
successCount++
|
||||
}
|
||||
|
||||
// 添加第二个路径(媒体数据路径)
|
||||
if (addSusPath(context, path2)) {
|
||||
successCount++
|
||||
}
|
||||
|
||||
// 添加第三个路径(UID路径)
|
||||
if (addSusPath(context, path3)) {
|
||||
successCount++
|
||||
}
|
||||
|
||||
val success = successCount > 0
|
||||
|
||||
Log.d("SuSFSManager", "Added $successCount/$totalCount paths for $packageName (UID: $uid)")
|
||||
|
||||
return success
|
||||
}
|
||||
|
||||
// 获取所有配置的Map
|
||||
private fun getAllConfigurations(context: Context): Map<String, Any> {
|
||||
return mapOf(
|
||||
@@ -356,6 +525,7 @@ object SuSFSManager {
|
||||
KEY_BUILD_TIME_VALUE to getBuildTimeValue(context),
|
||||
KEY_AUTO_START_ENABLED to isAutoStartEnabled(context),
|
||||
KEY_SUS_PATHS to getSusPaths(context),
|
||||
KEY_SUS_LOOP_PATHS to getSusLoopPaths(context),
|
||||
KEY_SUS_MOUNTS to getSusMounts(context),
|
||||
KEY_TRY_UMOUNTS to getTryUmounts(context),
|
||||
KEY_ANDROID_DATA_PATH to getAndroidDataPath(context),
|
||||
@@ -368,6 +538,7 @@ object SuSFSManager {
|
||||
KEY_ENABLE_HIDE_BL to getEnableHideBl(context),
|
||||
KEY_ENABLE_CLEANUP_RESIDUE to getEnableCleanupResidue(context),
|
||||
KEY_UMOUNT_FOR_ZYGOTE_ISO_SERVICE to getUmountForZygoteIsoService(context),
|
||||
KEY_ENABLE_AVC_LOG_SPOOFING to getEnableAvcLogSpoofing(context),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -492,12 +663,8 @@ object SuSFSManager {
|
||||
}
|
||||
|
||||
// 获取备份文件路径
|
||||
fun getRecommendedBackupPath(context: Context): String {
|
||||
val documentsDir = File(context.getExternalFilesDir(null), "SuSFS_Backups")
|
||||
if (!documentsDir.exists()) {
|
||||
documentsDir.mkdirs()
|
||||
}
|
||||
return File(documentsDir, generateBackupFileName()).absolutePath
|
||||
fun getDefaultBackupFileName(): String {
|
||||
return generateBackupFileName()
|
||||
}
|
||||
|
||||
// 槽位信息获取
|
||||
@@ -666,6 +833,7 @@ object SuSFSManager {
|
||||
private fun getDefaultDisabledFeatures(context: Context): List<EnabledFeature> {
|
||||
val defaultFeatures = listOf(
|
||||
"sus_path_feature_label" to context.getString(R.string.sus_path_feature_label),
|
||||
"sus_loop_path_feature_label" to context.getString(R.string.sus_loop_path_feature_label),
|
||||
"sus_mount_feature_label" to context.getString(R.string.sus_mount_feature_label),
|
||||
"try_umount_feature_label" to context.getString(R.string.try_umount_feature_label),
|
||||
"spoof_uname_feature_label" to context.getString(R.string.spoof_uname_feature_label),
|
||||
@@ -727,9 +895,28 @@ object SuSFSManager {
|
||||
return success
|
||||
}
|
||||
|
||||
// AVC日志欺骗开关
|
||||
suspend fun setEnableAvcLogSpoofing(context: Context, enabled: Boolean): Boolean {
|
||||
if (!isSusVersion159()) {
|
||||
return false
|
||||
}
|
||||
|
||||
val success = executeSusfsCommand(context, "enable_avc_log_spoofing ${if (enabled) 1 else 0}")
|
||||
if (success) {
|
||||
saveEnableAvcLogSpoofing(context, enabled)
|
||||
if (isAutoStartEnabled(context)) updateMagiskModule(context)
|
||||
showToast(context, if (enabled)
|
||||
context.getString(R.string.avc_log_spoofing_enabled)
|
||||
else
|
||||
context.getString(R.string.avc_log_spoofing_disabled)
|
||||
)
|
||||
}
|
||||
return success
|
||||
}
|
||||
|
||||
// SUS挂载隐藏控制
|
||||
suspend fun setHideSusMountsForAllProcs(context: Context, hideForAll: Boolean): Boolean {
|
||||
if (!isSusVersion_1_5_8()) {
|
||||
if (!isSusVersion158()) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -763,7 +950,7 @@ object SuSFSManager {
|
||||
@SuppressLint("StringFormatInvalid")
|
||||
suspend fun addSusPath(context: Context, path: String): Boolean {
|
||||
// 如果是1.5.8版本,先设置路径配置
|
||||
if (isSusVersion_1_5_8()) {
|
||||
if (isSusVersion158()) {
|
||||
// 获取当前配置的路径,如果没有配置则使用默认值
|
||||
val androidDataPath = getAndroidDataPath(context)
|
||||
val sdcardPath = getSdcardPath(context)
|
||||
@@ -814,16 +1001,111 @@ object SuSFSManager {
|
||||
|
||||
// 编辑SUS路径
|
||||
suspend fun editSusPath(context: Context, oldPath: String, newPath: String): Boolean {
|
||||
return try {
|
||||
val currentPaths = getSusPaths(context).toMutableSet()
|
||||
if (currentPaths.remove(oldPath)) {
|
||||
currentPaths.add(newPath)
|
||||
if (!currentPaths.remove(oldPath)) {
|
||||
showToast(context, "Original path not found: $oldPath")
|
||||
return false
|
||||
}
|
||||
|
||||
saveSusPaths(context, currentPaths)
|
||||
if (isAutoStartEnabled(context)) updateMagiskModule(context)
|
||||
|
||||
val success = addSusPath(context, newPath)
|
||||
|
||||
if (success) {
|
||||
showToast(context, "SUS path updated: $oldPath -> $newPath")
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
// 如果添加新路径失败,恢复旧路径
|
||||
currentPaths.add(oldPath)
|
||||
saveSusPaths(context, currentPaths)
|
||||
if (isAutoStartEnabled(context)) updateMagiskModule(context)
|
||||
showToast(context, "Failed to update path, reverted to original")
|
||||
return false
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
showToast(context, "Error updating SUS path: ${e.message}")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// 循环路径相关方法
|
||||
@SuppressLint("SdCardPath")
|
||||
private fun isValidLoopPath(path: String): Boolean {
|
||||
return !path.startsWith("/storage/") && !path.startsWith("/sdcard/")
|
||||
}
|
||||
|
||||
@SuppressLint("StringFormatInvalid")
|
||||
suspend fun addSusLoopPath(context: Context, path: String): Boolean {
|
||||
// 检查路径是否有效
|
||||
if (!isValidLoopPath(path)) {
|
||||
showToast(context, context.getString(R.string.susfs_loop_path_invalid_location))
|
||||
return false
|
||||
}
|
||||
|
||||
// 执行添加循环路径命令
|
||||
val result = executeSusfsCommandWithOutput(context, "add_sus_path_loop '$path'")
|
||||
val isActuallySuccessful = result.isSuccess && !result.output.contains("not found, skip adding")
|
||||
|
||||
if (isActuallySuccessful) {
|
||||
saveSusLoopPaths(context, getSusLoopPaths(context) + path)
|
||||
if (isAutoStartEnabled(context)) updateMagiskModule(context)
|
||||
showToast(context, context.getString(R.string.susfs_loop_path_added_success, path))
|
||||
} else {
|
||||
val errorMessage = if (result.output.contains("not found, skip adding")) {
|
||||
context.getString(R.string.susfs_path_not_found_error, path)
|
||||
} else {
|
||||
"${context.getString(R.string.susfs_command_failed)}\n${result.output}\n${result.errorOutput}"
|
||||
}
|
||||
showToast(context, errorMessage)
|
||||
}
|
||||
return isActuallySuccessful
|
||||
}
|
||||
|
||||
suspend fun removeSusLoopPath(context: Context, path: String): Boolean {
|
||||
saveSusLoopPaths(context, getSusLoopPaths(context) - path)
|
||||
if (isAutoStartEnabled(context)) updateMagiskModule(context)
|
||||
showToast(context, context.getString(R.string.susfs_loop_path_removed, path))
|
||||
return true
|
||||
}
|
||||
|
||||
// 编辑循环路径
|
||||
suspend fun editSusLoopPath(context: Context, oldPath: String, newPath: String): Boolean {
|
||||
// 检查新路径是否有效
|
||||
if (!isValidLoopPath(newPath)) {
|
||||
showToast(context, context.getString(R.string.susfs_loop_path_invalid_location))
|
||||
return false
|
||||
}
|
||||
|
||||
return try {
|
||||
val currentPaths = getSusLoopPaths(context).toMutableSet()
|
||||
if (!currentPaths.remove(oldPath)) {
|
||||
showToast(context, "Original loop path not found: $oldPath")
|
||||
return false
|
||||
}
|
||||
|
||||
saveSusLoopPaths(context, currentPaths)
|
||||
|
||||
val success = addSusLoopPath(context, newPath)
|
||||
|
||||
if (success) {
|
||||
showToast(context, context.getString(R.string.susfs_loop_path_updated, oldPath, newPath))
|
||||
return true
|
||||
} else {
|
||||
// 如果添加新路径失败,恢复旧路径
|
||||
currentPaths.add(oldPath)
|
||||
saveSusLoopPaths(context, currentPaths)
|
||||
if (isAutoStartEnabled(context)) updateMagiskModule(context)
|
||||
showToast(context, "Failed to update loop path, reverted to original")
|
||||
return false
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
showToast(context, "Error updating SUS loop path: ${e.message}")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// 添加SUS挂载
|
||||
suspend fun addSusMount(context: Context, mount: String): Boolean {
|
||||
@@ -844,16 +1126,34 @@ object SuSFSManager {
|
||||
|
||||
// 编辑SUS挂载
|
||||
suspend fun editSusMount(context: Context, oldMount: String, newMount: String): Boolean {
|
||||
return try {
|
||||
val currentMounts = getSusMounts(context).toMutableSet()
|
||||
if (currentMounts.remove(oldMount)) {
|
||||
currentMounts.add(newMount)
|
||||
if (!currentMounts.remove(oldMount)) {
|
||||
showToast(context, "Original mount not found: $oldMount")
|
||||
return false
|
||||
}
|
||||
|
||||
saveSusMounts(context, currentMounts)
|
||||
if (isAutoStartEnabled(context)) updateMagiskModule(context)
|
||||
|
||||
val success = addSusMount(context, newMount)
|
||||
|
||||
if (success) {
|
||||
showToast(context, "SUS mount updated: $oldMount -> $newMount")
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
// 如果添加新挂载点失败,恢复旧挂载点
|
||||
currentMounts.add(oldMount)
|
||||
saveSusMounts(context, currentMounts)
|
||||
if (isAutoStartEnabled(context)) updateMagiskModule(context)
|
||||
showToast(context, "Failed to update mount, reverted to original")
|
||||
return false
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
showToast(context, "Error updating SUS mount: ${e.message}")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// 添加尝试卸载
|
||||
suspend fun addTryUmount(context: Context, path: String, mode: Int): Boolean {
|
||||
@@ -879,22 +1179,40 @@ object SuSFSManager {
|
||||
|
||||
// 编辑尝试卸载
|
||||
suspend fun editTryUmount(context: Context, oldEntry: String, newPath: String, newMode: Int): Boolean {
|
||||
return try {
|
||||
val currentUmounts = getTryUmounts(context).toMutableSet()
|
||||
if (currentUmounts.remove(oldEntry)) {
|
||||
currentUmounts.add("$newPath|$newMode")
|
||||
if (!currentUmounts.remove(oldEntry)) {
|
||||
showToast(context, "Original umount entry not found: $oldEntry")
|
||||
return false
|
||||
}
|
||||
|
||||
saveTryUmounts(context, currentUmounts)
|
||||
if (isAutoStartEnabled(context)) updateMagiskModule(context)
|
||||
|
||||
val success = addTryUmount(context, newPath, newMode)
|
||||
|
||||
if (success) {
|
||||
showToast(context, "Try umount updated: $oldEntry -> $newPath|$newMode")
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
// 如果添加新条目失败,恢复旧条目
|
||||
currentUmounts.add(oldEntry)
|
||||
saveTryUmounts(context, currentUmounts)
|
||||
if (isAutoStartEnabled(context)) updateMagiskModule(context)
|
||||
showToast(context, "Failed to update umount entry, reverted to original")
|
||||
return false
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
showToast(context, "Error updating try umount: ${e.message}")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun runTryUmount(context: Context): Boolean = executeSusfsCommand(context, "run_try_umount")
|
||||
|
||||
// Zygote隔离服务卸载控制
|
||||
suspend fun setUmountForZygoteIsoService(context: Context, enabled: Boolean): Boolean {
|
||||
if (!isSusVersion_1_5_8()) {
|
||||
if (!isSusVersion158()) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -943,17 +1261,35 @@ object SuSFSManager {
|
||||
suspend fun editKstatConfig(context: Context, oldConfig: String, path: String, ino: String, dev: String, nlink: String,
|
||||
size: String, atime: String, atimeNsec: String, mtime: String, mtimeNsec: String,
|
||||
ctime: String, ctimeNsec: String, blocks: String, blksize: String): Boolean {
|
||||
return try {
|
||||
val currentConfigs = getKstatConfigs(context).toMutableSet()
|
||||
if (currentConfigs.remove(oldConfig)) {
|
||||
val newConfigEntry = "$path|$ino|$dev|$nlink|$size|$atime|$atimeNsec|$mtime|$mtimeNsec|$ctime|$ctimeNsec|$blocks|$blksize"
|
||||
currentConfigs.add(newConfigEntry)
|
||||
if (!currentConfigs.remove(oldConfig)) {
|
||||
showToast(context, "Original kstat config not found")
|
||||
return false
|
||||
}
|
||||
|
||||
saveKstatConfigs(context, currentConfigs)
|
||||
if (isAutoStartEnabled(context)) updateMagiskModule(context)
|
||||
|
||||
val success = addKstatStatically(context, path, ino, dev, nlink, size, atime, atimeNsec,
|
||||
mtime, mtimeNsec, ctime, ctimeNsec, blocks, blksize)
|
||||
|
||||
if (success) {
|
||||
showToast(context, context.getString(R.string.kstat_config_updated, path))
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
// 如果添加新配置失败,恢复旧配置
|
||||
currentConfigs.add(oldConfig)
|
||||
saveKstatConfigs(context, currentConfigs)
|
||||
if (isAutoStartEnabled(context)) updateMagiskModule(context)
|
||||
showToast(context, "Failed to update kstat config, reverted to original")
|
||||
return false
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
showToast(context, "Error updating kstat config: ${e.message}")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// 添加kstat路径
|
||||
suspend fun addKstat(context: Context, path: String): Boolean {
|
||||
@@ -976,16 +1312,34 @@ object SuSFSManager {
|
||||
// 编辑kstat路径
|
||||
@SuppressLint("StringFormatInvalid")
|
||||
suspend fun editAddKstat(context: Context, oldPath: String, newPath: String): Boolean {
|
||||
return try {
|
||||
val currentPaths = getAddKstatPaths(context).toMutableSet()
|
||||
if (currentPaths.remove(oldPath)) {
|
||||
currentPaths.add(newPath)
|
||||
if (!currentPaths.remove(oldPath)) {
|
||||
showToast(context, "Original kstat path not found: $oldPath")
|
||||
return false
|
||||
}
|
||||
|
||||
saveAddKstatPaths(context, currentPaths)
|
||||
if (isAutoStartEnabled(context)) updateMagiskModule(context)
|
||||
|
||||
val success = addKstat(context, newPath)
|
||||
|
||||
if (success) {
|
||||
showToast(context, context.getString(R.string.kstat_path_updated, oldPath, newPath))
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
// 如果添加新路径失败,恢复旧路径
|
||||
currentPaths.add(oldPath)
|
||||
saveAddKstatPaths(context, currentPaths)
|
||||
if (isAutoStartEnabled(context)) updateMagiskModule(context)
|
||||
showToast(context, "Failed to update kstat path, reverted to original")
|
||||
return false
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
showToast(context, "Error updating kstat path: ${e.message}")
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
// 更新kstat
|
||||
suspend fun updateKstat(context: Context, path: String): Boolean {
|
||||
|
||||
@@ -103,6 +103,7 @@ object ScriptGenerator {
|
||||
*/
|
||||
private fun shouldConfigureInService(config: SuSFSManager.ModuleConfig): Boolean {
|
||||
return config.susPaths.isNotEmpty() ||
|
||||
config.susLoopPaths.isNotEmpty() ||
|
||||
config.kstatConfigs.isNotEmpty() ||
|
||||
config.addKstatPaths.isNotEmpty() ||
|
||||
(!config.executeInPostFsData && (config.unameValue != DEFAULT_UNAME || config.buildTimeValue != DEFAULT_BUILD_TIME))
|
||||
@@ -116,6 +117,14 @@ object ScriptGenerator {
|
||||
appendLine()
|
||||
}
|
||||
|
||||
private fun StringBuilder.generateAvcLogSpoofingSection(enableAvcLogSpoofing: Boolean) {
|
||||
appendLine("# 设置AVC日志欺骗状态")
|
||||
val avcLogValue = if (enableAvcLogSpoofing) 1 else 0
|
||||
appendLine("\"${'$'}SUSFS_BIN\" enable_avc_log_spoofing $avcLogValue")
|
||||
appendLine("echo \"$(get_current_time): AVC日志欺骗功能设置为: ${if (enableAvcLogSpoofing) "启用" else "禁用"}\" >> \"${'$'}LOG_FILE\"")
|
||||
appendLine()
|
||||
}
|
||||
|
||||
private fun StringBuilder.generateSusPathsSection(susPaths: Set<String>) {
|
||||
if (susPaths.isNotEmpty()) {
|
||||
appendLine("# 添加SUS路径")
|
||||
@@ -127,6 +136,17 @@ object ScriptGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private fun StringBuilder.generateSusLoopPathsSection(susLoopPaths: Set<String>) {
|
||||
if (susLoopPaths.isNotEmpty()) {
|
||||
appendLine("# 添加SUS循环路径")
|
||||
susLoopPaths.forEach { path ->
|
||||
appendLine("\"${'$'}SUSFS_BIN\" add_sus_path_loop '$path'")
|
||||
appendLine("echo \"$(get_current_time): 添加SUS循环路径: $path\" >> \"${'$'}LOG_FILE\"")
|
||||
}
|
||||
appendLine()
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SdCardPath")
|
||||
private fun StringBuilder.generateKstatSection(
|
||||
kstatConfigs: Set<String>,
|
||||
@@ -374,6 +394,9 @@ object ScriptGenerator {
|
||||
|
||||
generateUmountZygoteIsoServiceSection(config.umountForZygoteIsoService, config.support158)
|
||||
|
||||
// 添加AVC日志欺骗设置
|
||||
generateAvcLogSpoofingSection(config.enableAvcLogSpoofing)
|
||||
|
||||
appendLine("echo \"$(get_current_time): Post-FS-Data脚本执行完成\" >> \"${'$'}LOG_FILE\"")
|
||||
}
|
||||
}
|
||||
@@ -461,14 +484,20 @@ object ScriptGenerator {
|
||||
appendLine()
|
||||
|
||||
// 路径设置和SUS路径设置
|
||||
if (config.susPaths.isNotEmpty()) {
|
||||
if (config.susPaths.isNotEmpty() || config.susLoopPaths.isNotEmpty()) {
|
||||
generatePathSettingSection(config.androidDataPath, config.sdcardPath)
|
||||
appendLine()
|
||||
appendLine("until [ -d \"/sdcard/Android\" ]; do sleep 1; done")
|
||||
appendLine("sleep 45")
|
||||
appendLine()
|
||||
|
||||
// 添加普通SUS路径
|
||||
if (config.susPaths.isNotEmpty()) {
|
||||
generateSusPathsSection(config.susPaths)
|
||||
}
|
||||
|
||||
// 添加循环SUS路径
|
||||
if (config.susLoopPaths.isNotEmpty()) {
|
||||
generateSusLoopPathsSection(config.susLoopPaths)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
appendLine("echo \"$(get_current_time): Boot-Completed脚本执行完成\" >> \"${'$'}LOG_FILE\"")
|
||||
@@ -480,6 +509,8 @@ object ScriptGenerator {
|
||||
appendLine("# 路径配置")
|
||||
appendLine("# 设置Android Data路径")
|
||||
appendLine("until [ -d \"/sdcard/Android\" ]; do sleep 1; done")
|
||||
appendLine("sleep 60")
|
||||
appendLine()
|
||||
appendLine("\"${'$'}SUSFS_BIN\" set_android_data_root_path '$androidDataPath'")
|
||||
appendLine("echo \"$(get_current_time): Android Data路径设置为: $androidDataPath\" >> \"${'$'}LOG_FILE\"")
|
||||
appendLine()
|
||||
|
||||
@@ -12,6 +12,7 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.dergoogler.mmrl.platform.Platform.Companion.context
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonSyntaxException
|
||||
import com.sukisu.ultra.KernelVersion
|
||||
import com.sukisu.ultra.Natives
|
||||
import com.sukisu.ultra.getKernelVersion
|
||||
@@ -31,6 +32,8 @@ class HomeViewModel : ViewModel() {
|
||||
private const val KEY_SYSTEM_INFO = "system_info"
|
||||
private const val KEY_VERSION_INFO = "version_info"
|
||||
private const val KEY_LAST_UPDATE = "last_update_time"
|
||||
private const val KEY_ERROR_COUNT = "error_count"
|
||||
private const val MAX_ERROR_COUNT = 2
|
||||
}
|
||||
|
||||
// 系统状态
|
||||
@@ -60,7 +63,10 @@ class HomeViewModel : ViewModel() {
|
||||
val susSUMode: String = "",
|
||||
val superuserCount: Int = 0,
|
||||
val moduleCount: Int = 0,
|
||||
val kpmModuleCount: Int = 0
|
||||
val kpmModuleCount: Int = 0,
|
||||
val managersList: Natives.ManagersList? = null,
|
||||
val isDynamicSignEnabled: Boolean = false,
|
||||
val zygiskImplement: String = ""
|
||||
)
|
||||
|
||||
private val gson = Gson()
|
||||
@@ -85,43 +91,135 @@ class HomeViewModel : ViewModel() {
|
||||
private set
|
||||
var isHideSusfsStatus by mutableStateOf(false)
|
||||
private set
|
||||
var isHideZygiskImplement by mutableStateOf(false)
|
||||
private set
|
||||
var isHideLinkCard by mutableStateOf(false)
|
||||
private set
|
||||
var showKpmInfo by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
private fun clearAllCache() {
|
||||
try {
|
||||
prefs.edit { clear() }
|
||||
Log.i(TAG, "All cache cleared successfully")
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error clearing cache", e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetToDefaults() {
|
||||
systemStatus = SystemStatus()
|
||||
systemInfo = SystemInfo()
|
||||
latestVersionInfo = LatestVersionInfo()
|
||||
isSimpleMode = false
|
||||
isKernelSimpleMode = false
|
||||
isHideVersion = false
|
||||
isHideOtherInfo = false
|
||||
isHideSusfsStatus = false
|
||||
isHideZygiskImplement = false
|
||||
isHideLinkCard = false
|
||||
showKpmInfo = false
|
||||
}
|
||||
|
||||
private fun handleError(error: Exception, operation: String) {
|
||||
Log.e(TAG, "Error in $operation", error)
|
||||
|
||||
val errorCount = prefs.getInt(KEY_ERROR_COUNT, 0)
|
||||
val newErrorCount = errorCount + 1
|
||||
|
||||
if (newErrorCount >= MAX_ERROR_COUNT) {
|
||||
Log.w(TAG, "Too many errors ($newErrorCount), clearing cache and resetting")
|
||||
clearAllCache()
|
||||
resetToDefaults()
|
||||
} else {
|
||||
prefs.edit {
|
||||
putInt(KEY_ERROR_COUNT, newErrorCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun String?.orSafe(default: String = ""): String {
|
||||
return if (this.isNullOrBlank()) default else this
|
||||
}
|
||||
|
||||
private fun <T, R> Pair<T?, R?>?.orSafe(default: Pair<T, R>): Pair<T, R> {
|
||||
return if (this?.first == null || this.second == null) default else Pair(this.first!!, this.second!!)
|
||||
}
|
||||
|
||||
fun loadUserSettings(context: Context) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
isSimpleMode = prefs.getBoolean("is_simple_mode", false)
|
||||
isKernelSimpleMode = prefs.getBoolean("is_kernel_simple_mode", false)
|
||||
isHideVersion = prefs.getBoolean("is_hide_version", false)
|
||||
isHideOtherInfo = prefs.getBoolean("is_hide_other_info", false)
|
||||
isHideSusfsStatus = prefs.getBoolean("is_hide_susfs_status", false)
|
||||
isHideLinkCard = prefs.getBoolean("is_hide_link_card", false)
|
||||
showKpmInfo = prefs.getBoolean("show_kpm_info", false)
|
||||
try {
|
||||
val settingsPrefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
isSimpleMode = settingsPrefs.getBoolean("is_simple_mode", false)
|
||||
isKernelSimpleMode = settingsPrefs.getBoolean("is_kernel_simple_mode", false)
|
||||
isHideVersion = settingsPrefs.getBoolean("is_hide_version", false)
|
||||
isHideOtherInfo = settingsPrefs.getBoolean("is_hide_other_info", false)
|
||||
isHideSusfsStatus = settingsPrefs.getBoolean("is_hide_susfs_status", false)
|
||||
isHideLinkCard = settingsPrefs.getBoolean("is_hide_link_card", false)
|
||||
isHideZygiskImplement = settingsPrefs.getBoolean("is_hide_zygisk_Implement", false)
|
||||
showKpmInfo = settingsPrefs.getBoolean("show_kpm_info", false)
|
||||
} catch (e: Exception) {
|
||||
handleError(e, "loadUserSettings")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun initializeData() {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
loadCachedData()
|
||||
// 成功加载后重置错误计数
|
||||
prefs.edit {
|
||||
putInt(KEY_ERROR_COUNT, 0)
|
||||
}
|
||||
} catch(e: Exception) {
|
||||
handleError(e, "initializeData")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadCachedData() {
|
||||
prefs.getString(KEY_SYSTEM_STATUS, null)?.let {
|
||||
systemStatus = gson.fromJson(it, SystemStatus::class.java)
|
||||
try {
|
||||
prefs.getString(KEY_SYSTEM_STATUS, null)?.let { statusJson ->
|
||||
try {
|
||||
val cachedStatus = gson.fromJson(statusJson, SystemStatus::class.java)
|
||||
if (cachedStatus != null) {
|
||||
systemStatus = cachedStatus
|
||||
}
|
||||
prefs.getString(KEY_SYSTEM_INFO, null)?.let {
|
||||
systemInfo = gson.fromJson(it, SystemInfo::class.java)
|
||||
} catch (e: JsonSyntaxException) {
|
||||
Log.w(TAG, "Invalid system status JSON, using defaults", e)
|
||||
}
|
||||
prefs.getString(KEY_VERSION_INFO, null)?.let {
|
||||
latestVersionInfo = gson.fromJson(it, LatestVersionInfo::class.java)
|
||||
}
|
||||
|
||||
prefs.getString(KEY_SYSTEM_INFO, null)?.let { infoJson ->
|
||||
try {
|
||||
val cachedInfo = gson.fromJson(infoJson, SystemInfo::class.java)
|
||||
if (cachedInfo != null) {
|
||||
systemInfo = cachedInfo
|
||||
}
|
||||
} catch (e: JsonSyntaxException) {
|
||||
Log.w(TAG, "Invalid system info JSON, using defaults", e)
|
||||
}
|
||||
}
|
||||
|
||||
prefs.getString(KEY_VERSION_INFO, null)?.let { versionJson ->
|
||||
try {
|
||||
val cachedVersion = gson.fromJson(versionJson, LatestVersionInfo::class.java)
|
||||
if (cachedVersion != null) {
|
||||
latestVersionInfo = cachedVersion
|
||||
}
|
||||
} catch (e: JsonSyntaxException) {
|
||||
Log.w(TAG, "Invalid version info JSON, using defaults", e)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error loading cached data", e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchAndSaveData() {
|
||||
try {
|
||||
fetchSystemStatus()
|
||||
fetchSystemInfo()
|
||||
withContext(Dispatchers.IO) {
|
||||
@@ -130,15 +228,19 @@ class HomeViewModel : ViewModel() {
|
||||
putString(KEY_SYSTEM_INFO, gson.toJson(systemInfo))
|
||||
putString(KEY_VERSION_INFO, gson.toJson(latestVersionInfo))
|
||||
putLong(KEY_LAST_UPDATE, System.currentTimeMillis())
|
||||
putInt(KEY_ERROR_COUNT, 0)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
handleError(e, "fetchAndSaveData")
|
||||
}
|
||||
}
|
||||
|
||||
fun checkForUpdates(context: Context) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
try {
|
||||
val checkUpdate = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
.getBoolean("check_update", true)
|
||||
val settingsPrefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
|
||||
val checkUpdate = settingsPrefs.getBoolean("check_update", true)
|
||||
|
||||
if (checkUpdate) {
|
||||
val newVersionInfo = checkNewVersion()
|
||||
@@ -149,7 +251,7 @@ class HomeViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error checking for updates", e)
|
||||
handleError(e, "checkForUpdates")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -160,7 +262,7 @@ class HomeViewModel : ViewModel() {
|
||||
fetchAndSaveData()
|
||||
checkForUpdates(context)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error refreshing data", e)
|
||||
handleError(e, "refreshAllData")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,10 +271,31 @@ class HomeViewModel : ViewModel() {
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val kernelVersion = getKernelVersion()
|
||||
val isManager = Natives.becomeManager(ksuApp.packageName)
|
||||
val ksuVersion = if (isManager) Natives.version else null
|
||||
val fullVersion = Natives.getFullVersion()
|
||||
val isManager = try {
|
||||
Natives.becomeManager(ksuApp.packageName.orSafe("com.sukisu.ultra"))
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to become manager", e)
|
||||
false
|
||||
}
|
||||
|
||||
val ksuVersion = if (isManager) {
|
||||
try {
|
||||
Natives.version
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get KSU version", e)
|
||||
null
|
||||
}
|
||||
} else null
|
||||
|
||||
val fullVersion = try {
|
||||
Natives.getFullVersion().orSafe("Unknown")
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get full version", e)
|
||||
"Unknown"
|
||||
}
|
||||
|
||||
val ksuFullVersion = if (isKernelSimpleMode) {
|
||||
try {
|
||||
val startIndex = fullVersion.indexOf('v')
|
||||
if (startIndex >= 0) {
|
||||
val endIndex = fullVersion.indexOf('-', startIndex)
|
||||
@@ -186,12 +309,44 @@ class HomeViewModel : ViewModel() {
|
||||
} else {
|
||||
fullVersion
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to process full version", e)
|
||||
fullVersion
|
||||
}
|
||||
} else {
|
||||
fullVersion
|
||||
}
|
||||
|
||||
val lkmMode = ksuVersion?.let {
|
||||
if (it >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && kernelVersion.isGKI()) Natives.isLkmMode else null
|
||||
try {
|
||||
if (it >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && kernelVersion.isGKI()) {
|
||||
Natives.isLkmMode
|
||||
} else null
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get LKM mode", e)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val isRootAvailable = try {
|
||||
rootAvailable()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to check root availability", e)
|
||||
false
|
||||
}
|
||||
|
||||
val isKpmConfigured = try {
|
||||
Natives.isKPMEnabled()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to check KPM status", e)
|
||||
false
|
||||
}
|
||||
|
||||
val requireNewKernel = try {
|
||||
isManager && Natives.requireNewKernel()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to check kernel requirement", e)
|
||||
false
|
||||
}
|
||||
|
||||
systemStatus = SystemStatus(
|
||||
@@ -200,12 +355,13 @@ class HomeViewModel : ViewModel() {
|
||||
ksuFullVersion = ksuFullVersion,
|
||||
lkmMode = lkmMode,
|
||||
kernelVersion = kernelVersion,
|
||||
isRootAvailable = rootAvailable(),
|
||||
isKpmConfigured = Natives.isKPMEnabled(),
|
||||
requireNewKernel = isManager && Natives.requireNewKernel()
|
||||
isRootAvailable = isRootAvailable,
|
||||
isKpmConfigured = isKpmConfigured,
|
||||
requireNewKernel = requireNewKernel
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error fetching system status", e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -214,60 +370,188 @@ class HomeViewModel : ViewModel() {
|
||||
private suspend fun fetchSystemInfo() {
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val uname = Os.uname()
|
||||
val kpmVersion = getKpmVersion()
|
||||
val suSFS = getSuSFS()
|
||||
val uname = try {
|
||||
Os.uname()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get uname", e)
|
||||
null
|
||||
}
|
||||
|
||||
val kpmVersion = try {
|
||||
getKpmVersion().orSafe("Unknown")
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get kpm version", e)
|
||||
"Unknown"
|
||||
}
|
||||
|
||||
val suSFS = try {
|
||||
getSuSFS().orSafe("Unknown")
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get SuSFS", e)
|
||||
"Unknown"
|
||||
}
|
||||
|
||||
var suSFSVersion = ""
|
||||
var suSFSVariant = ""
|
||||
var suSFSFeatures = ""
|
||||
var susSUMode = ""
|
||||
|
||||
if (suSFS == "Supported") {
|
||||
suSFSVersion = getSuSFSVersion()
|
||||
suSFSVersion = try {
|
||||
getSuSFSVersion().orSafe("")
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get SuSFS version", e)
|
||||
""
|
||||
}
|
||||
|
||||
if (suSFSVersion.isNotEmpty()) {
|
||||
suSFSVariant = getSuSFSVariant()
|
||||
suSFSFeatures = getSuSFSFeatures()
|
||||
suSFSVariant = try {
|
||||
getSuSFSVariant().orSafe("")
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get SuSFS variant", e)
|
||||
""
|
||||
}
|
||||
|
||||
suSFSFeatures = try {
|
||||
getSuSFSFeatures().orSafe("")
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get SuSFS features", e)
|
||||
""
|
||||
}
|
||||
|
||||
val isSUS_SU = suSFSFeatures == "CONFIG_KSU_SUSFS_SUS_SU"
|
||||
if (isSUS_SU) {
|
||||
susSUMode = try {
|
||||
susfsSUS_SU_Mode().toString()
|
||||
} catch (_: Exception) {
|
||||
susfsSUS_SU_Mode()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get SUS SU mode", e)
|
||||
""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取动态管理器状态和管理器列表
|
||||
val dynamicSignConfig = try {
|
||||
Natives.getDynamicManager()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get dynamic manager config", e)
|
||||
null
|
||||
}
|
||||
|
||||
val isDynamicSignEnabled = try {
|
||||
dynamicSignConfig?.isValid() == true
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to check dynamic manager validity", e)
|
||||
false
|
||||
}
|
||||
|
||||
val managersList = if (isDynamicSignEnabled) {
|
||||
try {
|
||||
Natives.getManagersList()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get managers list", e)
|
||||
null
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val deviceModel = try {
|
||||
getDeviceModel().orSafe("Unknown")
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get device model", e)
|
||||
"Unknown"
|
||||
}
|
||||
|
||||
val managerVersion = try {
|
||||
getManagerVersion(ksuApp.applicationContext).orSafe(Pair("Unknown", 0L))
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get manager version", e)
|
||||
Pair("Unknown", 0L)
|
||||
}
|
||||
|
||||
val seLinuxStatus = try {
|
||||
getSELinuxStatus(context).orSafe("Unknown")
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get SELinux status", e)
|
||||
"Unknown"
|
||||
}
|
||||
|
||||
val superuserCount = try {
|
||||
getSuperuserCount()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get superuser count", e)
|
||||
0
|
||||
}
|
||||
|
||||
val moduleCount = try {
|
||||
getModuleCount()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get module count", e)
|
||||
0
|
||||
}
|
||||
|
||||
val kpmModuleCount = try {
|
||||
getKpmModuleCount()
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get kpm module count", e)
|
||||
0
|
||||
}
|
||||
|
||||
val zygiskImplement = try {
|
||||
getZygiskImplement().orSafe("None")
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get Zygisk implement", e)
|
||||
"None"
|
||||
}
|
||||
|
||||
systemInfo = SystemInfo(
|
||||
kernelRelease = uname.release,
|
||||
androidVersion = Build.VERSION.RELEASE,
|
||||
deviceModel = getDeviceModel(),
|
||||
managerVersion = getManagerVersion(ksuApp.applicationContext),
|
||||
seLinuxStatus = getSELinuxStatus(context),
|
||||
kernelRelease = uname?.release.orSafe("Unknown"),
|
||||
androidVersion = Build.VERSION.RELEASE.orSafe("Unknown"),
|
||||
deviceModel = deviceModel,
|
||||
managerVersion = managerVersion,
|
||||
seLinuxStatus = seLinuxStatus,
|
||||
kpmVersion = kpmVersion,
|
||||
suSFSStatus = suSFS,
|
||||
suSFSVersion = suSFSVersion,
|
||||
suSFSVariant = suSFSVariant,
|
||||
suSFSFeatures = suSFSFeatures,
|
||||
susSUMode = susSUMode,
|
||||
superuserCount = getSuperuserCount(),
|
||||
moduleCount = getModuleCount(),
|
||||
kpmModuleCount = getKpmModuleCount()
|
||||
superuserCount = superuserCount,
|
||||
moduleCount = moduleCount,
|
||||
kpmModuleCount = kpmModuleCount,
|
||||
managersList = managersList,
|
||||
isDynamicSignEnabled = isDynamicSignEnabled,
|
||||
zygiskImplement = zygiskImplement
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error fetching system info", e)
|
||||
throw e
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDeviceInfo(): String {
|
||||
var manufacturer =
|
||||
Build.MANUFACTURER[0].uppercaseChar().toString() + Build.MANUFACTURER.substring(1)
|
||||
if (!Build.BRAND.equals(Build.MANUFACTURER, ignoreCase = true)) {
|
||||
manufacturer += " " + Build.BRAND[0].uppercaseChar() + Build.BRAND.substring(1)
|
||||
return try {
|
||||
var manufacturer = Build.MANUFACTURER.orSafe("Unknown")
|
||||
manufacturer = manufacturer[0].uppercaseChar().toString() + manufacturer.substring(1)
|
||||
|
||||
val brand = Build.BRAND.orSafe("")
|
||||
if (brand.isNotEmpty() && !brand.equals(Build.MANUFACTURER, ignoreCase = true)) {
|
||||
manufacturer += " " + brand[0].uppercaseChar() + brand.substring(1)
|
||||
}
|
||||
|
||||
val model = Build.MODEL.orSafe("")
|
||||
if (model.isNotEmpty()) {
|
||||
manufacturer += " $model "
|
||||
}
|
||||
|
||||
manufacturer
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get device info", e)
|
||||
"Unknown Device"
|
||||
}
|
||||
manufacturer += " " + Build.MODEL + " "
|
||||
return manufacturer
|
||||
}
|
||||
|
||||
@SuppressLint("PrivateApi")
|
||||
@@ -283,27 +567,32 @@ class HomeViewModel : ViewModel() {
|
||||
)
|
||||
var result = getDeviceInfo()
|
||||
for (key in marketNameKeys) {
|
||||
try {
|
||||
val marketName = getMethod.invoke(null, key, "") as String
|
||||
if (marketName.isNotEmpty()) {
|
||||
result = marketName
|
||||
break
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w(TAG, "Failed to get market name for key: $key", e)
|
||||
}
|
||||
}
|
||||
result
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error getting device model", e)
|
||||
Log.w(TAG, "Error getting device model", e)
|
||||
getDeviceInfo()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getManagerVersion(context: Context): Pair<String, Long> {
|
||||
return try {
|
||||
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)!!
|
||||
val packageInfo = context.packageManager.getPackageInfo(context.packageName, 0)
|
||||
val versionCode = androidx.core.content.pm.PackageInfoCompat.getLongVersionCode(packageInfo)
|
||||
Pair(packageInfo.versionName!!, versionCode)
|
||||
val versionName = packageInfo.versionName.orSafe("Unknown")
|
||||
Pair(versionName, versionCode)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error getting manager version", e)
|
||||
Pair("", 0L)
|
||||
Log.w(TAG, "Error getting manager version", e)
|
||||
Pair("Unknown", 0L)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,11 +15,11 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import com.sukisu.ultra.ui.util.HanziToPinyin
|
||||
import com.sukisu.ultra.ui.util.listModules
|
||||
import com.sukisu.ultra.ui.util.getRootShell
|
||||
import com.sukisu.ultra.ui.util.ModuleVerificationManager
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.text.Collator
|
||||
import java.text.DecimalFormat
|
||||
import java.util.Locale
|
||||
@@ -87,6 +87,8 @@ class ModuleViewModel : ViewModel() {
|
||||
val hasActionScript: Boolean,
|
||||
val dirId: String, // real module id (dir name)
|
||||
var config: ModuleConfig? = null,
|
||||
var isVerified: Boolean = false, // 添加验证状态字段
|
||||
var verificationTimestamp: Long = 0L, // 添加验证时间戳
|
||||
)
|
||||
|
||||
var isRefreshing by mutableStateOf(false)
|
||||
@@ -132,7 +134,7 @@ class ModuleViewModel : ViewModel() {
|
||||
Log.i(TAG, "result: $result")
|
||||
|
||||
val array = JSONArray(result)
|
||||
modules = (0 until array.length())
|
||||
val moduleInfos = (0 until array.length())
|
||||
.asSequence()
|
||||
.map { array.getJSONObject(it) }
|
||||
.map { obj ->
|
||||
@@ -152,6 +154,26 @@ class ModuleViewModel : ViewModel() {
|
||||
obj.getString("dir_id")
|
||||
)
|
||||
}.toList()
|
||||
|
||||
// 批量检查所有模块的验证状态
|
||||
val moduleIds = moduleInfos.map { it.dirId }
|
||||
val verificationStatus = ModuleVerificationManager.batchCheckVerificationStatus(moduleIds)
|
||||
|
||||
// 更新模块验证状态
|
||||
modules = moduleInfos.map { moduleInfo ->
|
||||
val isVerified = verificationStatus[moduleInfo.dirId] ?: false
|
||||
val verificationTimestamp = if (isVerified) {
|
||||
ModuleVerificationManager.getVerificationTimestamp(moduleInfo.dirId)
|
||||
} else {
|
||||
0L
|
||||
}
|
||||
|
||||
moduleInfo.copy(
|
||||
isVerified = isVerified,
|
||||
verificationTimestamp = verificationTimestamp
|
||||
)
|
||||
}
|
||||
|
||||
launch {
|
||||
modules.forEach { module ->
|
||||
withContext(Dispatchers.IO) {
|
||||
@@ -270,6 +292,31 @@ class ModuleViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun ModuleViewModel.ModuleInfo.copy(
|
||||
id: String = this.id,
|
||||
name: String = this.name,
|
||||
author: String = this.author,
|
||||
version: String = this.version,
|
||||
versionCode: Int = this.versionCode,
|
||||
description: String = this.description,
|
||||
enabled: Boolean = this.enabled,
|
||||
update: Boolean = this.update,
|
||||
remove: Boolean = this.remove,
|
||||
updateJson: String = this.updateJson,
|
||||
hasWebUi: Boolean = this.hasWebUi,
|
||||
hasActionScript: Boolean = this.hasActionScript,
|
||||
dirId: String = this.dirId,
|
||||
config: ModuleConfig? = this.config,
|
||||
isVerified: Boolean = this.isVerified,
|
||||
verificationTimestamp: Long = this.verificationTimestamp
|
||||
): ModuleViewModel.ModuleInfo {
|
||||
return ModuleViewModel.ModuleInfo(
|
||||
id, name, author, version, versionCode, description,
|
||||
enabled, update, remove, updateJson, hasWebUi, hasActionScript,
|
||||
dirId, config, isVerified, verificationTimestamp
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 模块大小缓存管理器
|
||||
*/
|
||||
@@ -405,14 +452,12 @@ class ModuleSizeCache(context: Context) {
|
||||
*/
|
||||
private fun calculateModuleFolderSize(dirId: String): Long {
|
||||
return try {
|
||||
val process = Runtime.getRuntime().exec(arrayOf("su", "-c", "du -sb /data/adb/modules/$dirId"))
|
||||
val reader = BufferedReader(InputStreamReader(process.inputStream))
|
||||
val output = reader.readLine()
|
||||
process.waitFor()
|
||||
reader.close()
|
||||
val shell = getRootShell()
|
||||
val command = "du -sb /data/adb/modules/$dirId"
|
||||
val result = shell.newJob().add(command).to(ArrayList(), null).exec()
|
||||
|
||||
if (output != null) {
|
||||
val sizeStr = output.split("\t").firstOrNull()
|
||||
if (result.isSuccess && result.out.isNotEmpty()) {
|
||||
val sizeStr = result.out.firstOrNull()?.split("\t")?.firstOrNull()
|
||||
sizeStr?.toLongOrNull() ?: 0L
|
||||
} else {
|
||||
0L
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package com.sukisu.ultra.ui.viewmodel
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.content.SharedPreferences
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageInfo
|
||||
import android.os.IBinder
|
||||
import android.os.Parcelable
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
@@ -29,13 +33,13 @@ import java.util.*
|
||||
import java.util.concurrent.ThreadPoolExecutor
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.concurrent.LinkedBlockingQueue
|
||||
import com.dergoogler.mmrl.platform.Platform
|
||||
import com.dergoogler.mmrl.platform.TIMEOUT_MILLIS
|
||||
import com.sukisu.ultra.ui.webui.getInstalledPackagesAll
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.withTimeoutOrNull
|
||||
import androidx.core.content.edit
|
||||
import com.sukisu.ultra.ui.KsuService
|
||||
import com.sukisu.ultra.ui.util.KsuCli
|
||||
import com.topjohnwu.superuser.Shell
|
||||
import kotlinx.coroutines.asCoroutineDispatcher
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
// 应用分类
|
||||
enum class AppCategory(val displayNameRes: Int, val persistKey: String) {
|
||||
@@ -73,8 +77,6 @@ enum class SortType(val displayNameRes: Int, val persistKey: String) {
|
||||
* @date 2025/5/31.
|
||||
*/
|
||||
class SuperUserViewModel : ViewModel() {
|
||||
val isPlatformAlive get() = Platform.isAlive
|
||||
|
||||
companion object {
|
||||
private const val TAG = "SuperUserViewModel"
|
||||
var apps by mutableStateOf<List<AppInfo>>(emptyList())
|
||||
@@ -82,8 +84,8 @@ class SuperUserViewModel : ViewModel() {
|
||||
private const val KEY_SHOW_SYSTEM_APPS = "show_system_apps"
|
||||
private const val KEY_SELECTED_CATEGORY = "selected_category"
|
||||
private const val KEY_CURRENT_SORT_TYPE = "current_sort_type"
|
||||
private const val CORE_POOL_SIZE = 4
|
||||
private const val MAX_POOL_SIZE = 8
|
||||
private const val CORE_POOL_SIZE = 8
|
||||
private const val MAX_POOL_SIZE = 16
|
||||
private const val KEEP_ALIVE_TIME = 60L
|
||||
private const val BATCH_SIZE = 20
|
||||
}
|
||||
@@ -187,6 +189,13 @@ class SuperUserViewModel : ViewModel() {
|
||||
fun updateShowSystemApps(newValue: Boolean) {
|
||||
showSystemApps = newValue
|
||||
saveShowSystemApps(newValue)
|
||||
notifyAppListChanged()
|
||||
}
|
||||
|
||||
private fun notifyAppListChanged() {
|
||||
val currentApps = apps
|
||||
apps = emptyList()
|
||||
apps = currentApps
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -249,7 +258,7 @@ class SuperUserViewModel : ViewModel() {
|
||||
}
|
||||
|
||||
val appList by derivedStateOf {
|
||||
sortedList.filter {
|
||||
val filtered = sortedList.filter {
|
||||
it.label.contains(search, true) || it.packageName.contains(
|
||||
search,
|
||||
true
|
||||
@@ -258,6 +267,8 @@ class SuperUserViewModel : ViewModel() {
|
||||
}.filter {
|
||||
it.uid == 2000 || showSystemApps || it.packageInfo.applicationInfo!!.flags.and(ApplicationInfo.FLAG_SYSTEM) == 0
|
||||
}
|
||||
|
||||
filtered
|
||||
}
|
||||
|
||||
// 切换批量操作模式
|
||||
@@ -392,83 +403,99 @@ class SuperUserViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
private var serviceConnection: ServiceConnection? = null
|
||||
|
||||
private suspend fun connectKsuService(
|
||||
onDisconnect: () -> Unit = {}
|
||||
): IBinder? = suspendCoroutine { continuation ->
|
||||
val connection = object : ServiceConnection {
|
||||
override fun onServiceDisconnected(name: ComponentName?) {
|
||||
onDisconnect()
|
||||
serviceConnection = null
|
||||
}
|
||||
|
||||
override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
|
||||
continuation.resume(binder)
|
||||
}
|
||||
}
|
||||
|
||||
serviceConnection = connection
|
||||
val intent = Intent(ksuApp, KsuService::class.java)
|
||||
|
||||
try {
|
||||
val task = com.topjohnwu.superuser.ipc.RootService.bindOrTask(
|
||||
intent,
|
||||
Shell.EXECUTOR,
|
||||
connection
|
||||
)
|
||||
val shell = KsuCli.SHELL
|
||||
task?.let { shell.execTask(it) }
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to bind KsuService", e)
|
||||
continuation.resume(null)
|
||||
}
|
||||
}
|
||||
|
||||
private fun stopKsuService() {
|
||||
serviceConnection?.let { connection ->
|
||||
try {
|
||||
val intent = Intent(ksuApp, KsuService::class.java)
|
||||
com.topjohnwu.superuser.ipc.RootService.stop(intent)
|
||||
serviceConnection = null
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to stop KsuService", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun fetchAppList() {
|
||||
isRefreshing = true
|
||||
loadingProgress = 0f
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
withTimeoutOrNull(TIMEOUT_MILLIS) {
|
||||
while (!isPlatformAlive) {
|
||||
delay(500)
|
||||
val result = connectKsuService {
|
||||
Log.w(TAG, "KsuService disconnected")
|
||||
}
|
||||
} ?: return@withContext // Exit early if timeout
|
||||
|
||||
if (result == null) {
|
||||
Log.e(TAG, "Failed to connect to KsuService")
|
||||
isRefreshing = false
|
||||
return
|
||||
}
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
val pm = ksuApp.packageManager
|
||||
val start = SystemClock.elapsedRealtime()
|
||||
|
||||
try {
|
||||
val packages = Platform.getInstalledPackagesAll {
|
||||
Log.e(TAG, "getInstalledPackagesAll:", it)
|
||||
}
|
||||
val service = KsuService.Stub.asInterface(result)
|
||||
val allPackages = service?.getPackages(0)
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
stopKsuService()
|
||||
}
|
||||
loadingProgress = 0.3f
|
||||
|
||||
val filteredPackages = packages.filter { it.packageName != ksuApp.packageName }
|
||||
val packages = allPackages?.list ?: emptyList()
|
||||
|
||||
withContext(appProcessingThreadPool) {
|
||||
supervisorScope {
|
||||
val batches = filteredPackages.chunked(BATCH_SIZE)
|
||||
|
||||
val processedApps = batches.mapIndexed { batchIndex, batch ->
|
||||
async {
|
||||
val batchResult = batch.mapNotNull { packageInfo ->
|
||||
try {
|
||||
apps = packages.map { packageInfo ->
|
||||
val appInfo = packageInfo.applicationInfo!!
|
||||
val uid = appInfo.uid
|
||||
|
||||
val labelDeferred = async {
|
||||
appInfo.loadLabel(pm).toString()
|
||||
}
|
||||
val profileDeferred = async {
|
||||
Natives.getAppProfile(packageInfo.packageName, uid)
|
||||
}
|
||||
|
||||
val label = labelDeferred.await()
|
||||
val profile = profileDeferred.await()
|
||||
|
||||
val profile = Natives.getAppProfile(packageInfo.packageName, uid)
|
||||
AppInfo(
|
||||
label = label,
|
||||
label = appInfo.loadLabel(pm).toString(),
|
||||
packageInfo = packageInfo,
|
||||
profile = profile,
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Log.e(
|
||||
TAG,
|
||||
"Error processing app ${packageInfo.packageName}",
|
||||
e
|
||||
)
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val progress = 0.3f + (batchIndex + 1).toFloat() / batches.size * 0.6f
|
||||
loadingProgress = progress
|
||||
|
||||
batchResult
|
||||
}
|
||||
}.awaitAll().flatten()
|
||||
|
||||
appListMutex.withLock {
|
||||
apps = processedApps
|
||||
}
|
||||
}.filter { it.packageName != ksuApp.packageName }
|
||||
|
||||
loadingProgress = 1f
|
||||
|
||||
val elapsed = SystemClock.elapsedRealtime() - start
|
||||
Log.i(TAG, "Loaded ${processedApps.size} apps in ${elapsed}ms using concurrent processing")
|
||||
}
|
||||
}
|
||||
Log.i(TAG, "load cost: ${SystemClock.elapsedRealtime() - start}")
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error fetching app list", e)
|
||||
withContext(Dispatchers.Main) {
|
||||
stopKsuService()
|
||||
}
|
||||
} finally {
|
||||
isRefreshing = false
|
||||
loadingProgress = 0f
|
||||
@@ -482,6 +509,7 @@ class SuperUserViewModel : ViewModel() {
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
try {
|
||||
stopKsuService()
|
||||
appProcessingThreadPool.close()
|
||||
configChangeListeners.clear()
|
||||
} catch (e: Exception) {
|
||||
|
||||
@@ -41,10 +41,10 @@ class WebUIActivity : ComponentActivity() {
|
||||
val name = intent.getStringExtra("name")!!
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
@Suppress("DEPRECATION")
|
||||
setTaskDescription(ActivityManager.TaskDescription("KernelSU - $name"))
|
||||
setTaskDescription(ActivityManager.TaskDescription("SukiSU-Ultra - $name"))
|
||||
} else {
|
||||
val taskDescription =
|
||||
ActivityManager.TaskDescription.Builder().setLabel("KernelSU - $name").build()
|
||||
ActivityManager.TaskDescription.Builder().setLabel("SukiSU-Ultra - $name").build()
|
||||
setTaskDescription(taskDescription)
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ class WebUIXActivity : ComponentActivity() {
|
||||
val osVersion = Build.VERSION.RELEASE
|
||||
val deviceModel = Build.MODEL
|
||||
|
||||
return "SukiSU /$ksuVersion (Linux; Android $osVersion; $deviceModel; $platform/$platformVersion)"
|
||||
return "SukiSU-Ultra /$ksuVersion (Linux; Android $osVersion; $deviceModel; $platform/$platformVersion)"
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -59,10 +59,10 @@ class WebUIXActivity : ComponentActivity() {
|
||||
val name = intent.getStringExtra("name")!!
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
@Suppress("DEPRECATION")
|
||||
setTaskDescription(ActivityManager.TaskDescription("KernelSU - $name"))
|
||||
setTaskDescription(ActivityManager.TaskDescription("SukiSU-Ultra - $name"))
|
||||
} else {
|
||||
val taskDescription =
|
||||
ActivityManager.TaskDescription.Builder().setLabel("KernelSU - $name").build()
|
||||
ActivityManager.TaskDescription.Builder().setLabel("SukiSU-Ultra - $name").build()
|
||||
setTaskDescription(taskDescription)
|
||||
}
|
||||
|
||||
|
||||
@@ -19,10 +19,12 @@ public class UltraShellHelper {
|
||||
}
|
||||
|
||||
public static boolean isPathExists(String path) {
|
||||
return runCmd("file " + path).contains("No such file or directory");
|
||||
String result = runCmd("test -f '" + path + "' && echo 'exists'");
|
||||
return result.contains("exists");
|
||||
}
|
||||
|
||||
public static void CopyFileTo(String path, String target) {
|
||||
runCmd("cp -f " + path + " " + target);
|
||||
public static boolean CopyFileTo(String path, String target) {
|
||||
String result = runCmd("cp -f '" + path + "' '" + target + "' 2>&1");
|
||||
return !result.contains("cp: ");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,16 +287,10 @@ class HorizonKernelWorker(
|
||||
}
|
||||
|
||||
private fun runCommand(su: Boolean, cmd: String): Int {
|
||||
val process = ProcessBuilder(if (su) "su" else "sh")
|
||||
.redirectErrorStream(true)
|
||||
.start()
|
||||
val shell = if (su) "su" else "sh"
|
||||
val process = Runtime.getRuntime().exec(arrayOf(shell, "-c", cmd))
|
||||
|
||||
return try {
|
||||
process.outputStream.bufferedWriter().use { writer ->
|
||||
writer.write("$cmd\n")
|
||||
writer.write("exit\n")
|
||||
writer.flush()
|
||||
}
|
||||
process.waitFor()
|
||||
} finally {
|
||||
process.destroy()
|
||||
@@ -304,16 +298,10 @@ class HorizonKernelWorker(
|
||||
}
|
||||
|
||||
private fun runCommandGetOutput(su: Boolean, cmd: String): String? {
|
||||
val process = ProcessBuilder(if (su) "su" else "sh")
|
||||
.redirectErrorStream(true)
|
||||
.start()
|
||||
val shell = if (su) "su" else "sh"
|
||||
val process = Runtime.getRuntime().exec(arrayOf(shell, "-c", cmd))
|
||||
|
||||
return try {
|
||||
process.outputStream.bufferedWriter().use { writer ->
|
||||
writer.write("$cmd\n")
|
||||
writer.write("exit\n")
|
||||
writer.flush()
|
||||
}
|
||||
process.inputStream.bufferedReader().use { reader ->
|
||||
reader.readText().trim()
|
||||
}
|
||||
@@ -326,7 +314,7 @@ class HorizonKernelWorker(
|
||||
|
||||
private fun rootAvailable(): Boolean {
|
||||
return try {
|
||||
val process = Runtime.getRuntime().exec("su -c id")
|
||||
val process = Runtime.getRuntime().exec("su -c true")
|
||||
val exitValue = process.waitFor()
|
||||
exitValue == 0
|
||||
} catch (_: Exception) {
|
||||
|
||||
@@ -73,11 +73,13 @@ import kotlin.math.roundToInt
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.automirrored.filled.NavigateNext
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
@@ -86,6 +88,7 @@ import com.sukisu.ultra.ui.theme.getCardElevation
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import com.sukisu.ultra.ksuApp
|
||||
@@ -145,6 +148,13 @@ fun MoreSettingsScreen(
|
||||
var showDpiConfirmDialog by remember { mutableStateOf(false) }
|
||||
var showImageEditor by remember { mutableStateOf(false) }
|
||||
|
||||
// 动态管理器配置状态
|
||||
var dynamicSignConfig by remember { mutableStateOf<Natives.DynamicManagerConfig?>(null) }
|
||||
var isDynamicSignEnabled by remember { mutableStateOf(false) }
|
||||
var dynamicSignSize by remember { mutableStateOf("") }
|
||||
var dynamicSignHash by remember { mutableStateOf("") }
|
||||
var showDynamicSignDialog by remember { mutableStateOf(false) }
|
||||
|
||||
// 主题模式选项
|
||||
val themeOptions = listOf(
|
||||
stringResource(R.string.theme_follow_system),
|
||||
@@ -222,6 +232,11 @@ fun MoreSettingsScreen(
|
||||
mutableStateOf(prefs.getBoolean("show_kpm_info", false))
|
||||
}
|
||||
|
||||
// 隐藏 Zygisk 状态开关状态
|
||||
var isHideZygiskImplement by remember {
|
||||
mutableStateOf(prefs.getBoolean("is_hide_zygisk_Implement", false))
|
||||
}
|
||||
|
||||
// 隐藏SuSFS状态开关状态
|
||||
var isHideSusfsStatus by remember {
|
||||
mutableStateOf(prefs.getBoolean("is_hide_susfs_status", false))
|
||||
@@ -329,6 +344,12 @@ fun MoreSettingsScreen(
|
||||
isHideSusfsStatus = newValue
|
||||
}
|
||||
|
||||
val onHideZygiskImplement = { newValue: Boolean ->
|
||||
prefs.edit { putBoolean("is_hide_zygisk_Implement", newValue) }
|
||||
isHideZygiskImplement = newValue
|
||||
|
||||
}
|
||||
|
||||
// 隐藏链接状态开关状态
|
||||
val onHideLinkCardChange = { newValue: Boolean ->
|
||||
prefs.edit { putBoolean("is_hide_link_card", newValue) }
|
||||
@@ -652,6 +673,167 @@ fun MoreSettingsScreen(
|
||||
)
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
// 初始化动态管理器配置
|
||||
dynamicSignConfig = Natives.getDynamicManager()
|
||||
dynamicSignConfig?.let { config ->
|
||||
if (config.isValid()) {
|
||||
isDynamicSignEnabled = true
|
||||
dynamicSignSize = config.size.toString()
|
||||
dynamicSignHash = config.hash
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun parseDynamicSignSize(input: String): Int? {
|
||||
return try {
|
||||
when {
|
||||
input.startsWith("0x", true) -> input.substring(2).toInt(16)
|
||||
else -> input.toInt()
|
||||
}
|
||||
} catch (_: NumberFormatException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
// 动态管理器配置对话框
|
||||
if (showDynamicSignDialog) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { showDynamicSignDialog = false },
|
||||
title = { Text(stringResource(R.string.dynamic_manager_title)) },
|
||||
text = {
|
||||
Column(
|
||||
modifier = Modifier.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
// 启用开关
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { isDynamicSignEnabled = !isDynamicSignEnabled }
|
||||
.padding(vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Switch(
|
||||
checked = isDynamicSignEnabled,
|
||||
onCheckedChange = { isDynamicSignEnabled = it }
|
||||
)
|
||||
Spacer(modifier = Modifier.width(12.dp))
|
||||
Text(stringResource(R.string.enable_dynamic_manager))
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// 签名大小输入
|
||||
OutlinedTextField(
|
||||
value = dynamicSignSize,
|
||||
onValueChange = { input ->
|
||||
val isValid = when {
|
||||
input.isEmpty() -> true
|
||||
input.matches(Regex("^\\d+$")) -> true
|
||||
input.matches(Regex("^0[xX][0-9a-fA-F]*$")) -> true
|
||||
else -> false
|
||||
}
|
||||
if (isValid) {
|
||||
dynamicSignSize = input
|
||||
}
|
||||
},
|
||||
label = { Text(stringResource(R.string.signature_size)) },
|
||||
enabled = isDynamicSignEnabled,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
singleLine = true,
|
||||
keyboardOptions = KeyboardOptions(
|
||||
keyboardType = KeyboardType.Text
|
||||
)
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
// 签名哈希输入
|
||||
OutlinedTextField(
|
||||
value = dynamicSignHash,
|
||||
onValueChange = { hash ->
|
||||
if (hash.all { it in '0'..'9' || it in 'a'..'f' || it in 'A'..'F' }) {
|
||||
dynamicSignHash = hash
|
||||
}
|
||||
},
|
||||
label = { Text(stringResource(R.string.signature_hash)) },
|
||||
enabled = isDynamicSignEnabled,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
singleLine = true,
|
||||
supportingText = {
|
||||
Text(stringResource(R.string.hash_must_be_64_chars))
|
||||
},
|
||||
isError = isDynamicSignEnabled && dynamicSignHash.isNotEmpty() && dynamicSignHash.length != 64
|
||||
)
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
Button(
|
||||
onClick = {
|
||||
if (isDynamicSignEnabled) {
|
||||
val size = parseDynamicSignSize(dynamicSignSize)
|
||||
if (size != null && size > 0 && dynamicSignHash.length == 64) {
|
||||
val success = Natives.setDynamicManager(size, dynamicSignHash)
|
||||
if (success) {
|
||||
dynamicSignConfig = Natives.DynamicManagerConfig(size, dynamicSignHash)
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.dynamic_manager_set_success),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.dynamic_manager_set_failed),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.invalid_sign_config),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return@Button
|
||||
}
|
||||
} else {
|
||||
val success = Natives.clearDynamicManager()
|
||||
if (success) {
|
||||
dynamicSignConfig = null
|
||||
dynamicSignSize = ""
|
||||
dynamicSignHash = ""
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.dynamic_manager_disabled_success),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
} else {
|
||||
Toast.makeText(
|
||||
context,
|
||||
context.getString(R.string.dynamic_manager_clear_failed),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return@Button
|
||||
}
|
||||
}
|
||||
showDynamicSignDialog = false
|
||||
},
|
||||
enabled = if (isDynamicSignEnabled) {
|
||||
parseDynamicSignSize(dynamicSignSize)?.let { it > 0 } == true &&
|
||||
dynamicSignHash.length == 64
|
||||
} else true
|
||||
) {
|
||||
Text(stringResource(R.string.confirm))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { showDynamicSignDialog = false }) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||
topBar = {
|
||||
@@ -1110,6 +1292,15 @@ fun MoreSettingsScreen(
|
||||
onChange = onHideSusfsStatusChange
|
||||
)
|
||||
|
||||
// Zygsik 实现状态信息
|
||||
SwitchSettingItem(
|
||||
icon = Icons.Filled.VisibilityOff,
|
||||
title = stringResource(R.string.hide_zygisk_implement),
|
||||
summary = stringResource(R.string.hide_zygisk_implement_summary),
|
||||
checked = isHideZygiskImplement,
|
||||
onChange = onHideZygiskImplement
|
||||
)
|
||||
|
||||
if (Natives.version >= Natives.MINIMAL_SUPPORTED_KPM) {
|
||||
// 隐藏KPM开关
|
||||
SwitchSettingItem(
|
||||
@@ -1226,6 +1417,22 @@ fun MoreSettingsScreen(
|
||||
}
|
||||
)
|
||||
}
|
||||
// 动态管理器设置
|
||||
if (Natives.version >= Natives.MINIMAL_SUPPORTED_DYNAMIC_MANAGER) {
|
||||
SettingItem(
|
||||
icon = Icons.Filled.Security,
|
||||
title = stringResource(R.string.dynamic_manager_title),
|
||||
subtitle = if (isDynamicSignEnabled) {
|
||||
stringResource(
|
||||
R.string.dynamic_manager_enabled_summary,
|
||||
dynamicSignSize
|
||||
)
|
||||
} else {
|
||||
stringResource(R.string.dynamic_manager_disabled)
|
||||
},
|
||||
onClick = { showDynamicSignDialog = true }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BIN
manager/app/src/main/jniLibs/arm64-v8a/libzakosign.so
Normal file
BIN
manager/app/src/main/jniLibs/arm64-v8a/libzakosign.so
Normal file
Binary file not shown.
BIN
manager/app/src/main/jniLibs/armeabi-v7a/libzakosign.so
Normal file
BIN
manager/app/src/main/jniLibs/armeabi-v7a/libzakosign.so
Normal file
Binary file not shown.
@@ -60,6 +60,7 @@
|
||||
<string name="profile_selinux_context">Konteks SELinux</string>
|
||||
<string name="profile_umount_modules">Umount Modul</string>
|
||||
<string name="failed_to_update_app_profile">Gagal membarui Profil pada %s</string>
|
||||
<string name="require_kernel_version" formatted="false">Versi KernelSU saat ini %s terlalu rendah untuk menjalankan manager dengan baik. Harap tingkatkan ke versi %s atau yang lebih tinggi!</string>
|
||||
<string name="settings_umount_modules_default">Melepas Modul secara bawaan</string>
|
||||
<string name="settings_umount_modules_default_summary">Menggunakan \"Umount Modul\" secara universal pada Profil Aplikasi. Jika diaktifkan, akan menghapus semua modifikasi sistem untuk aplikasi yang tidak memiliki set profil.</string>
|
||||
<string name="settings_susfs_toggle">Nonaktifkan kprobe hooks</string>
|
||||
@@ -112,6 +113,7 @@
|
||||
\nLanjutkan?</string>
|
||||
<string name="install_next">Selanjutnya</string>
|
||||
<string name="select_file_tip">%1$s image partisi terekomendasi</string>
|
||||
<string name="select_file_tip_vendor">(tidak stabil)</string>
|
||||
<string name="select_kmi">Pilih KMI</string>
|
||||
<string name="settings_uninstall">Hapus</string>
|
||||
<string name="settings_uninstall_temporary">Hapus sementara</string>
|
||||
@@ -129,7 +131,9 @@
|
||||
<string name="sus_su_mode">Mode SuS SU:</string>
|
||||
<!-- Module related -->
|
||||
<string name="module_install_confirm">konfirmasi pemasangan modul %1$s?</string>
|
||||
<string name="unknown_module">module tidak dikenal</string>
|
||||
<!-- Restore related -->
|
||||
<string name="restore_confirm_title">Konfirmasi pemulihan module</string>
|
||||
<string name="restore_confirm_message">Operasi ini akan menimpa semua modul yang ada. Lanjutkan?</string>
|
||||
<string name="confirm">Konfirmasi</string>
|
||||
<string name="cancel">Batal</string>
|
||||
@@ -144,15 +148,29 @@
|
||||
<string name="restart_now">Mulai Ulang Sekarang</string>
|
||||
<string name="unknown_error">Kesalahan tidak diketahui</string>
|
||||
<!-- Command related -->
|
||||
<string name="command_execution_failed">Eksekusi perintah gagal: %1$s</string>
|
||||
<!-- Allowlist related -->
|
||||
<string name="allowlist_backup_success">Cadangan daftar izin berhasil</string>
|
||||
<string name="allowlist_backup_failed">Gagal mencadangkan daftar izin: %1$s</string>
|
||||
<string name="allowlist_restore_confirm_title">Konfirmasi Pemulihan Daftar Izin</string>
|
||||
<string name="allowlist_restore_confirm_message">Operasi ini akan menimpa daftar izin saat ini. Lanjutkan?</string>
|
||||
<string name="allowlist_restore_success">Daftar izin berhasil dipulihkan</string>
|
||||
<string name="allowlist_restore_failed">Gagal memulihkan daftar izin: %1$s</string>
|
||||
<string name="backup_allowlist">Cadangkan Daftar Izin</string>
|
||||
<string name="restore_allowlist">Pulihkan Daftar Izin</string>
|
||||
<string name="settings_custom_background">Latar belakang kustom</string>
|
||||
<string name="settings_custom_background_summary">Pilih gambar untuk latar belakang</string>
|
||||
<string name="settings_card_alpha">NavBar transparant</string>
|
||||
<string name="home_android_version">Versi Android</string>
|
||||
<string name="home_device_model">Model Perangkat</string>
|
||||
<string name="su_not_allowed">Memberikan hak superuser kepada %s tidak diizinkan</string>
|
||||
<string name="settings_disable_su">Nonaktifkan kompatibilitas SU</string>
|
||||
<string name="settings_disable_su_summary">Nonaktifkan sementara kemampuan aplikasi untuk mendapatkan hak akses root melalui perintah su (proses root yang sedang berjalan tidak akan terpengaruh)</string>
|
||||
<string name="module_install_multiple_confirm_with_names">Apakah Anda yakin ingin menginstal %1$d modul berikut?\n\n%2$s</string>
|
||||
<string name="more_settings">Setelan lainnya</string>
|
||||
<string name="selinux">Selinux</string>
|
||||
<string name="selinux_enabled">Aktifkan</string>
|
||||
<string name="selinux_disabled">Nonaktifkan</string>
|
||||
<string name="simple_mode">Mode simple</string>
|
||||
<string name="simple_mode_summary">Sembunyikan papan kartu di beranda</string>
|
||||
<string name="hide_kernel_kernelsu_version">Sembunyikan versi kernel</string>
|
||||
@@ -161,6 +179,8 @@
|
||||
<string name="hide_other_info_summary">Sembunyikan notifikasi titik merah (jumlah Super User, modul, dan modul KPM) di bilah navigasi</string>
|
||||
<string name="hide_susfs_status">Sembunyikan status SuSFs</string>
|
||||
<string name="hide_susfs_status_summary">Sembunyikan status susfs di halaman awal beranda</string>
|
||||
<string name="hide_zygisk_implement">Sembunyikan status zygisk</string>
|
||||
<string name="hide_zygisk_implement_summary">Sembunyikan informasi implementasi Zygisk di halaman utama</string>
|
||||
<string name="hide_link_card">Sembunyikan kartu tautan</string>
|
||||
<string name="hide_link_card_summary">Sembunyikan papan kartu URL di halaman awal beranda</string>
|
||||
<string name="hide_tag_card">Sembunyikan baris label modul</string>
|
||||
@@ -169,6 +189,7 @@
|
||||
<string name="theme_follow_system">Mengikuti sistem</string>
|
||||
<string name="theme_light">Terang</string>
|
||||
<string name="theme_dark">Hitam</string>
|
||||
<string name="manual_hook">Hook manual</string>
|
||||
<string name="dynamic_color_title">Warna dinamik</string>
|
||||
<string name="dynamic_color_summary">Warna dinamik, menggunakan sistem tema</string>
|
||||
<string name="choose_theme_color">Pilih warna tema</string>
|
||||
@@ -176,8 +197,18 @@
|
||||
<string name="color_green">Hijau</string>
|
||||
<string name="color_purple">Ungu</string>
|
||||
<string name="color_orange">Oren</string>
|
||||
<string name="color_pink">Ping</string>
|
||||
<string name="color_gray">Abu</string>
|
||||
<string name="color_yellow">Kuning</string>
|
||||
<string name="horizon_kernel">Memasang Anykernel3</string>
|
||||
<string name="horizon_kernel_summary">Memasang file kernel AnyKernel3</string>
|
||||
<string name="root_required">Butuh izin root</string>
|
||||
<string name="reboot_complete_title">Pembersihan selesai</string>
|
||||
<string name="reboot_complete_msg">Apakah ingin restart sekarang?</string>
|
||||
<string name="yes">Iya</string>
|
||||
<string name="no">Tidak</string>
|
||||
<string name="failed_reboot">Mulai ulang gagal</string>
|
||||
<string name="kpm_title">KPM</string>
|
||||
<string name="kpm_empty">Tidak ada modul kernel yang terpasang saat ini</string>
|
||||
<string name="kpm_version">Versi</string>
|
||||
<string name="kpm_author">Pembuat</string>
|
||||
@@ -189,16 +220,75 @@
|
||||
<string name="kpm_args">Parameter</string>
|
||||
<string name="kpm_control">Eksekusi</string>
|
||||
<string name="home_kpm_version">Versi KPM</string>
|
||||
<string name="close_notice">Tutup</string>
|
||||
<string name="kernel_module_notice">Fungsi-fungsi modul kernel berikut dikembangkan oleh KernelPatch dan dimodifikasi untuk menyertakan fungsi modul kernel dari SukiSU Ultra</string>
|
||||
<string name="home_ContributionCard_kernelsu">Antusias Untuk SukiSU Ultra</string>
|
||||
<string name="kpm_control_success">Sukses</string>
|
||||
<string name="kpm_control_failed">Gagal</string>
|
||||
<string name="home_click_to_ContributionCard_kernelsu">SukiSU Ultra akan menjadi cabang KSU yang relatif independen di masa mendatang, tetapi kami tetap menghargai KernelSU dan MKSU resmi dan sebagainya atas kontribusi mereka!</string>
|
||||
<string name="not_supported">Tidak Mendukung</string>
|
||||
<string name="supported">Mendukung</string>
|
||||
<string name="kernel_patched">Kernel belum ditambal</string>
|
||||
<string name="kernel_not_enabled">Kernel belum dikonfigurasi</string>
|
||||
<string name="custom_settings">Pengaturan kostum</string>
|
||||
<string name="kpm_install_mode">Instalasi KPM</string>
|
||||
<string name="kpm_install_mode_load">Muat</string>
|
||||
<string name="kpm_install_mode_embed">Sematkan</string>
|
||||
<string name="kpm_install_mode_description">Silakan pilih: %1\$s Mode Instalasi Modul \n\nMuat: Memuat sementara modul \nSematkan: Menginstal secara permanen ke dalam sistem</string>
|
||||
<string name="snackbar_failed_to_check_module_file">Gagal memeriksa keberadaan file modul</string>
|
||||
<string name="theme_color">Warna Tema</string>
|
||||
<string name="invalid_file_type">Format file tidak sesuai. Silakan pilih file dengan format .kpm.</string>
|
||||
<string name="confirm_uninstall_title_with_filename">Menghapus instalan</string>
|
||||
<string name="confirm_uninstall_content">KPM berikut akan diuninstall: %s</string>
|
||||
<string name="settings_susfs_toggle_summary">Nonaktifkan kprobe hooks yang dibuat oleh KernelSU, gunakan inline hooks sebagai gantinya (metode ini mirip dengan hooking kernel non-GKI).</string>
|
||||
<string name="image_editor_hint">Gunakan dua jari untuk memperbesar gambar, dan satu jari untuk menggeser mengatur posisi</string>
|
||||
<string name="reprovision">Reprovisi</string>
|
||||
<!-- Kernel Flash Progress Related -->
|
||||
<string name="horizon_flash_title">Memasang Kernel</string>
|
||||
<string name="horizon_logs_label">Log:</string>
|
||||
<string name="horizon_flash_complete">Flash Selesai</string>
|
||||
<!-- Flash Status Related -->
|
||||
<string name="horizon_preparing">Mempersiapkan…</string>
|
||||
<string name="horizon_cleaning_files">Membersihkan Berkas...</string>
|
||||
<string name="horizon_copying_files">Menyalin file...</string>
|
||||
<string name="horizon_extracting_tool">Mengekstrak alat flash…</string>
|
||||
<string name="horizon_patching_script">Memperbaiki skrip flash…</string>
|
||||
<string name="horizon_flashing">Mem-flash kernel…</string>
|
||||
<string name="horizon_flash_complete_status">Flash selesai</string>
|
||||
<!-- Slot selection related strings -->
|
||||
<string name="select_slot_title">Pilih Slot Flash</string>
|
||||
<string name="select_slot_description">Silakan pilih slot target untuk flash boot</string>
|
||||
<string name="slot_a">Slot A</string>
|
||||
<string name="slot_b">Slot B</string>
|
||||
<string name="selected_slot">Slot yang dipilih: %1$s</string>
|
||||
<string name="horizon_getting_original_slot">Mendapatkan slot asli</string>
|
||||
<string name="horizon_setting_target_slot">Mengatur slot yang ditentukan</string>
|
||||
<string name="horizon_restoring_original_slot">Pulihkan Slot Default</string>
|
||||
<string name="current_slot">Slot default sistem saat ini:%1$s </string>
|
||||
<!-- Error Messages -->
|
||||
<string name="horizon_copy_failed">Menyalin gagal</string>
|
||||
<string name="horizon_unknown_error">Kesalahan yang tidak diketahui</string>
|
||||
<string name="flash_failed_message">Flash gagal</string>
|
||||
<!-- lkm/gki install -->
|
||||
<string name="Lkm_install_methods">Perbaikan/pemasangan LKM</string>
|
||||
<string name="GKI_install_methods">Mem-flash AnyKernel3</string>
|
||||
<string name="kernel_version_log">Versi kernel: %1$s</string>
|
||||
<string name="tool_version_log">Menggunakan alat perbaikan:%1$s</string>
|
||||
<string name="configuration">Konfigurasi</string>
|
||||
<string name="app_settings">Pengaturan Aplikasi</string>
|
||||
<string name="tools">Alat-Alat</string>
|
||||
<!-- String resources used in SuperUser -->
|
||||
<string name="no_apps_found">Aplikasi tidak ditemukan</string>
|
||||
<string name="selinux_enabled_toast">SELinux Dinyalakan</string>
|
||||
<string name="selinux_disabled_toast">SELinux Dimatikan</string>
|
||||
<string name="selinux_change_failed">Perubahan Status SELinux Gagal</string>
|
||||
<string name="advanced_settings">Pengaturan Lanjutan</string>
|
||||
<string name="appearance_settings">Kustomisasi toolbar</string>
|
||||
<string name="back">Kembali</string>
|
||||
<string name="susfs_enabled">SuSFS dinyalakan</string>
|
||||
<string name="susfs_disabled">SuSFS dimatikan</string>
|
||||
<string name="background_set_success">Set latar belakang berhasil</string>
|
||||
<string name="background_removed">Latar belakang khusus yang dihapus</string>
|
||||
<string name="icon_switch_title">Ubah ikon</string>
|
||||
<string name="icon_switch_summary">Ubah ikon peluncur aplikasi ke ikon KernelSU</string>
|
||||
<string name="icon_switched">Ikon dirubah</string>
|
||||
@@ -210,6 +300,8 @@
|
||||
<string name="engine_auto_select">Otomatis memilih</string>
|
||||
<string name="engine_force_webuix">Paksa menggunakan WebUI X</string>
|
||||
<string name="engine_force_ksu">Penggunaan wajib KSU WebUI</string>
|
||||
<string name="use_webuix_eruda">Suntik Eruda ke WebUI X</string>
|
||||
<string name="use_webuix_eruda_summary">Suntikkan konsol debug ke dalam WebUI X untuk mempermudah proses debugging. Memerlukan pengaktifan web debugging.</string>
|
||||
<!-- DPI setting related strings -->
|
||||
<string name="app_dpi_title">Ubah DPI</string>
|
||||
<string name="app_dpi_summary">Pengaturan DPI hanya untuk aplikasi ini saja</string>
|
||||
@@ -227,31 +319,302 @@
|
||||
<string name="language_setting">Bahasa Aplikasi</string>
|
||||
<string name="language_follow_system">Mengikuti sistem</string>
|
||||
<string name="language_changed">Bahasa dirubah, mulai ulang aplikasi untuk menerapkan</string>
|
||||
<string name="settings_card_dim">Penyesuaian Kegelapan Kartu</string>
|
||||
<!-- Flash related -->
|
||||
<string name="error_code">Kode error</string>
|
||||
<string name="check_log">Silahkan periksa log</string>
|
||||
<string name="installing_module">Modul yang dipasang %1$d/%2$d</string>
|
||||
<string name="module_failed_count">%d Gagal memasang modul baru</string>
|
||||
<string name="module_download_error">Download modul gagal</string>
|
||||
<string name="kernel_flashing">Memasang Kernel</string>
|
||||
<!-- 分类相关 -->
|
||||
<string name="category_all_apps">Semua</string>
|
||||
<string name="category_root_apps">Akar</string>
|
||||
<string name="category_custom_apps">Kostum</string>
|
||||
<string name="category_default_apps">Bawaan</string>
|
||||
<!-- 排序相关 -->
|
||||
<string name="sort_name_asc">Urutan naik nama</string>
|
||||
<string name="sort_name_desc">Urutan turun nama</string>
|
||||
<string name="sort_install_time_new">Waktu pemasangan (baru)</string>
|
||||
<string name="sort_install_time_old">Waktu pemasangan (lama)</string>
|
||||
<string name="sort_size_desc">Urutan turun ukuran</string>
|
||||
<string name="sort_size_asc">Urutan naik ukuran</string>
|
||||
<string name="sort_usage_freq">Frekuensi penggunaan</string>
|
||||
<!-- 状态相关 -->
|
||||
<string name="no_apps_in_category">Tidak ada aplikasi dalam kategori ini</string>
|
||||
<!-- 标签相关 -->
|
||||
<string name="deny_authorization">Penolakan otorisasi</string>
|
||||
<string name="grant_authorization">Otorisasi</string>
|
||||
<string name="unmount_modules">Melepas Pemasangan Modul</string>
|
||||
<string name="disable_unmount">Nonaktifkan pelepasan pemasangan modul</string>
|
||||
<string name="expand_menu">Luaskan menu</string>
|
||||
<string name="collapse_menu">Tutup menu</string>
|
||||
<string name="scroll_to_top">Atas</string>
|
||||
<string name="scroll_to_bottom">Bawah</string>
|
||||
<string name="selected">Dipilih</string>
|
||||
<string name="select">pilihan</string>
|
||||
<!-- BottomSheet相关 -->
|
||||
<string name="menu_options">Opsi Menu</string>
|
||||
<string name="sort_options">Urut berdasarkan</string>
|
||||
<string name="app_categories">Pilihan Jenis Aplikasi</string>
|
||||
<!-- SuSFS Configuration -->
|
||||
<string name="susfs_config_title">Konfigurasi SuSFS</string>
|
||||
<string name="susfs_config_description">Deskripsi Konfigurasi</string>
|
||||
<string name="susfs_config_description_text">Fitur ini memungkinkan Anda menyesuaikan nilai uname SuSFS dan spoofing waktu build. Masukkan nilai yang ingin Anda atur lalu klik Terapkan untuk memproses perubahan.</string>
|
||||
<string name="susfs_uname_label">Nilai Uname</string>
|
||||
<string name="susfs_uname_placeholder">Silakan masukkan nilai uname khusus</string>
|
||||
<string name="susfs_build_time_label">Spoofing Waktu membangun</string>
|
||||
<string name="susfs_build_time_placeholder">Masukkan nilai spoofing waktu membangun</string>
|
||||
<string name="susfs_current_value">Nilai saat ini: %s</string>
|
||||
<string name="susfs_current_build_time">Waktu membangun saat ini: %s</string>
|
||||
<string name="susfs_reset_to_default">Setel Ulang ke Default</string>
|
||||
<string name="susfs_apply">Terapkan</string>
|
||||
<!-- SuSFS Reset Confirmation -->
|
||||
<string name="susfs_reset_confirm_title">Konfirmasi Setel Ulang</string>
|
||||
<!-- SuSFS Toast Messages -->
|
||||
<string name="susfs_binary_not_found">File ksu_susfs tidak ditemukan</string>
|
||||
<string name="susfs_command_failed">Eksekusi perintah SUSFS gagal</string>
|
||||
<string name="susfs_command_error">Gagal menjalankan perintah SUSFS: %s</string>
|
||||
<string name="susfs_uname_set_success" formatted="false">Berhasil atur uname dan waktu build SUSFS: %s, %s</string>
|
||||
<!-- SuSFS Settings Item -->
|
||||
<string name="susfs_config_setting_title">Konfigurasi SUSFS</string>
|
||||
<!-- 开机自启动相关 -->
|
||||
<string name="susfs_autostart_title">Mulai Otomatis</string>
|
||||
<string name="susfs_autostart_description">Terapkan semua konfigurasi non-default secara otomatis saat mulai ulang</string>
|
||||
<string name="susfs_autostart_requirement">Perlu tambahan konfigurasi untuk mengaktifkan</string>
|
||||
<string name="susfs_autostart_enable_failed">Gagal mengaktifkan mulai otomatis</string>
|
||||
<string name="susfs_autostart_disable_failed">Gagal menonaktifkan mulai otomatis</string>
|
||||
<string name="susfs_autostart_error">Kesalahan konfigurasi mulai otomatis: %s</string>
|
||||
<string name="susfs_no_config_to_autostart">Tidak ada konfigurasi yang tersedia untuk mulai otomatis</string>
|
||||
<!-- SuSFS Tab Titles -->
|
||||
<string name="susfs_tab_basic_settings">Pengaturan Dasar</string>
|
||||
<string name="susfs_tab_sus_paths">Jalur SUS</string>
|
||||
<string name="susfs_tab_sus_mounts">Pemasangan SUS</string>
|
||||
<string name="susfs_tab_try_umount">Coba Umount</string>
|
||||
<string name="susfs_tab_path_settings">Pengaturan Path</string>
|
||||
<string name="susfs_tab_enabled_features">Status Fitur yang Diaktifkan</string>
|
||||
<!-- SuSFS Path Management -->
|
||||
<string name="susfs_add_sus_path">Tambahkan Jalur SUS</string>
|
||||
<string name="susfs_add_sus_mount">Tambahkan Pemasangan SUS</string>
|
||||
<string name="susfs_add_try_umount">Tambahkan Coba Umount</string>
|
||||
<string name="susfs_sus_path_added_success">Jalur SUS berhasil ditambahkan</string>
|
||||
<string name="susfs_path_not_found_error">Kesalahan jalur tidak ditemukan</string>
|
||||
<string name="susfs_path_label">Jalur</string>
|
||||
<string name="susfs_mount_path_label">Jalur Pemasangan</string>
|
||||
<string name="susfs_path_placeholder">contoh: /system/addon.d</string>
|
||||
<string name="susfs_no_paths_configured">Tidak ada jalur SUS yang dikonfigurasi</string>
|
||||
<string name="susfs_no_mounts_configured">Tidak ada pemasangan SUS yang dikonfigurasi</string>
|
||||
<string name="susfs_no_umounts_configured">Tidak ada coba umount yang dikonfigurasi</string>
|
||||
<!-- SuSFS Umount Mode -->
|
||||
<string name="susfs_umount_mode_label">Mode Umount</string>
|
||||
<string name="susfs_umount_mode_normal">Umount Normal (0)</string>
|
||||
<string name="susfs_umount_mode_detach">Umount Lepas (1)</string>
|
||||
<string name="susfs_umount_mode_normal_short">Normal</string>
|
||||
<string name="susfs_umount_mode_detach_short">Lepas</string>
|
||||
<string name="susfs_umount_mode_display">Mode: %1$s (%2$s)</string>
|
||||
<string name="susfs_try_umount_added_success">Jalur coba umount berhasil ditambahkan: %s</string>
|
||||
<string name="susfs_try_umount_added_saved">Jalur coba umount berhasil disimpan: %s</string>
|
||||
<!-- SuSFS Run Umount -->
|
||||
<string name="susfs_run_umount_confirm_title">Konfirmasi Jalankan Coba Umount</string>
|
||||
<string name="susfs_run_umount_confirm_message">Ini akan segera mengeksekusi semua operasi umount yang dikonfigurasi. Apakah Anda yakin ingin melanjutkan?</string>
|
||||
<!-- SuSFS Reset Categories -->
|
||||
<string name="susfs_reset_paths_title">Setel Ulang Jalur SUS</string>
|
||||
<string name="susfs_reset_paths_message">Ini akan menghapus semua konfigurasi jalur SUS. Apakah Anda yakin ingin melanjutkan?</string>
|
||||
<string name="susfs_reset_mounts_title">Setel Ulang Pemasangan SUS</string>
|
||||
<string name="susfs_reset_mounts_message">Ini akan menghapus semua konfigurasi mount SUS. Apakah Anda yakin ingin melanjutkan?</string>
|
||||
<string name="susfs_reset_umounts_title">Setel Ulang Coba Umount</string>
|
||||
<string name="susfs_reset_umounts_message">Ini akan menghapus semua konfigurasi umount. Apakah Anda yakin ingin melanjutkan?</string>
|
||||
<string name="susfs_reset_path_title">Setel Ulang Pengaturan Jalur</string>
|
||||
<!-- SuSFS Path Settings -->
|
||||
<string name="susfs_android_data_path_label">Jalur Data Android</string>
|
||||
<string name="susfs_sdcard_path_label">Jalur SD Card</string>
|
||||
<string name="susfs_set_android_data_path">Atur Jalur Data Android</string>
|
||||
<string name="susfs_set_sdcard_path">Atur Jalur SD Card</string>
|
||||
<!-- SuSFS Enabled Features -->
|
||||
<string name="susfs_enabled_features_description">Tampilkan status fitur SuSFS yang saat ini diaktifkan</string>
|
||||
<string name="susfs_no_features_found">Tidak ditemukan informasi status fitur</string>
|
||||
<string name="susfs_feature_enabled">Diaktifkan</string>
|
||||
<string name="susfs_feature_disabled">Dinonaktifkan</string>
|
||||
<!-- Feature Labels -->
|
||||
<string name="sus_path_feature_label">Dukungan Jalur SUS</string>
|
||||
<string name="sus_mount_feature_label">Dukungan Pemasangan SUS</string>
|
||||
<string name="try_umount_feature_label">Dukungan Coba Umount</string>
|
||||
<string name="spoof_uname_feature_label">Dukungan Spoof uname</string>
|
||||
<string name="spoof_cmdline_feature_label">Spoof Cmdline/Bootconfig</string>
|
||||
<string name="open_redirect_feature_label">Dukungan Pengalihan Terbuka</string>
|
||||
<string name="enable_log_feature_label">Dukungan Logging</string>
|
||||
<string name="auto_default_mount_feature_label">Pemasangan Default Otomatis</string>
|
||||
<string name="auto_bind_mount_feature_label">Pemasangan Bind Otomatis</string>
|
||||
<string name="auto_try_umount_bind_feature_label">Coba Umount Bind Mount Otomatis</string>
|
||||
<string name="hide_symbols_feature_label">Sembunyikan Simbol KSU SUSFS</string>
|
||||
<string name="magic_mount_feature_label">Dukungan Pemasangan Ajaib</string>
|
||||
<string name="sus_kstat_feature_label">Dukungan SUS Kstat</string>
|
||||
<string name="sus_su_feature_label">Fungsi pengalihan mode SUS SU</string>
|
||||
<!-- 可切换状态 -->
|
||||
<string name="susfs_feature_configurable">Fitur SuSFS yang Dapat Dikonfigurasi</string>
|
||||
<string name="susfs_enable_log_label">Aktifkan Log SuSFS</string>
|
||||
<string name="susfs_log_config_description">Aktifkan atau nonaktifkan logging untuk SuSFS</string>
|
||||
<string name="susfs_log_config_title">Konfigurasi Logging SuSFS</string>
|
||||
<string name="susfs_log_enabled">Mengaktifkan Logging SuSFS</string>
|
||||
<string name="susfs_log_disabled">Menonaktifkan logging SuSFS</string>
|
||||
<string name="module_update_json">Perbarui JSON</string>
|
||||
<string name="module_update_json_copied">URL Pembaruan JSON disalin ke papan klip</string>
|
||||
<!-- Settings related strings -->
|
||||
<string name="show_more_module_info">Tampilkan info modul lainnya</string>
|
||||
<string name="show_more_module_info_summary">Pajang info modul tambahan seperti URL pembaruan JSON</string>
|
||||
<string name="susfs_execution_location_label">Lokasi Eksekusi</string>
|
||||
<string name="susfs_current_execution_location">Lokasi eksekusi saat ini: %s</string>
|
||||
<string name="susfs_execution_location_service">Layanan</string>
|
||||
<string name="susfs_execution_location_post_fs_data">Post-FS-Data</string>
|
||||
<string name="susfs_execution_location_service_description">Eksekusi setelah layanan sistem dimulai</string>
|
||||
<string name="susfs_execution_location_post_fs_data_description">Eksekusi setelah sistem file dipasang tetapi sebelum sistem sepenuhnya boot, Dapat menyebabkan boot loop</string>
|
||||
<string name="susfs_slot_info_title">Informasi Slot</string>
|
||||
<string name="susfs_slot_info_description">Lihat informasi slot boot saat ini dan salin nilai</string>
|
||||
<string name="susfs_current_active_slot">Slot Aktif Saat Ini: %s</string>
|
||||
<string name="susfs_slot_uname">Uname: %s</string>
|
||||
<string name="susfs_slot_build_time">Waktu Build: %s</string>
|
||||
<string name="susfs_slot_current_badge">Saat Ini</string>
|
||||
<string name="susfs_slot_use_uname">Gunakan Uname</string>
|
||||
<string name="susfs_slot_use_build_time">Gunakan Waktu Build</string>
|
||||
<string name="susfs_slot_info_unavailable">Tidak dapat mengambil informasi slot</string>
|
||||
<!-- SuSFS 自启动相关字符串 -->
|
||||
<string name="susfs_autostart_enabled_success">Modul autostart SuSFS diaktifkan, jalur modul: %s</string>
|
||||
<string name="susfs_autostart_disabled_success">Modul autostart SuSFS dinonaktifkan</string>
|
||||
<!-- SuSFS Kstat相关字符串 -->
|
||||
<string name="susfs_tab_kstat_config">Konfigurasi Kstat</string>
|
||||
<string name="kstat_static_config_added">Konfigurasi statis Kstat ditambahkan: %1$s</string>
|
||||
<string name="kstat_config_removed">Konfigurasi Kstat dihapus: %1$s</string>
|
||||
<string name="kstat_path_added">Jalur Kstat ditambahkan: %1$s</string>
|
||||
<string name="kstat_path_removed">Jalur Kstat dihapus: %1$s</string>
|
||||
<string name="kstat_updated">Kstat diperbarui: %1$s</string>
|
||||
<string name="kstat_full_clone_updated">Kstat full clone diperbarui: %1$s</string>
|
||||
<string name="add_kstat_statically_title">Tambahkan Konfigurasi Statis Kstat</string>
|
||||
<string name="file_or_directory_path_label">Jalur File/Direktori</string>
|
||||
<string name="hint_use_default_value">Petunjuk: Anda dapat menggunakan ”default“ untuk menggunakan nilai asli</string>
|
||||
<string name="add_kstat_path_title">Tambahkan Jalur Kstat</string>
|
||||
<string name="add">Tambahkan</string>
|
||||
<string name="reset_kstat_config_title">Setel Ulang Konfigurasi Kstat</string>
|
||||
<string name="reset_kstat_config_message">Apakah Anda yakin ingin menghapus semua konfigurasi Kstat? Tindakan ini tidak dapat dibatalkan.</string>
|
||||
<string name="kstat_config_description_title">Deskripsi Konfigurasi Kstat</string>
|
||||
<string name="kstat_config_description_add_statically">• add_sus_kstat_statically: Info stat statis file/direktori</string>
|
||||
<string name="kstat_config_description_add">• add_sus_kstat: Tambahkan jalur sebelum bind mount, menyimpan info stat asli</string>
|
||||
<string name="kstat_config_description_update">• update_sus_kstat: Perbarui target ino, pertahankan ukuran dan blok tidak berubah</string>
|
||||
<string name="kstat_config_description_update_full_clone">• update_sus_kstat_full_clone: Perbarui ino saja, pertahankan nilai asli lainnya</string>
|
||||
<string name="static_kstat_config">Konfigurasi Statis Kstat</string>
|
||||
<string name="kstat_path_management">Manajemen Jalur Kstat</string>
|
||||
<string name="no_kstat_config_message">Belum ada konfigurasi Kstat, klik tombol di atas untuk menambahkan</string>
|
||||
<!-- SuSFS Mount Hiding Control Related Strings -->
|
||||
<string name="susfs_hide_mounts_control_title">Kontrol Penyembunyian Pemasangan SUS</string>
|
||||
<string name="susfs_hide_mounts_control_description">Kontrol perilaku penyembunyian pemasangan SUS untuk proses</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_label">Sembunyikan pemasangan SUS untuk semua proses</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_enabled_description">Saat diaktifkan, pemasangan SUS akan disembunyikan dari semua proses, termasuk proses KSU</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_disabled_description">Saat dinonaktifkan, pemasangan SUS hanya akan disembunyikan dari proses non-KSU, proses KSU dapat melihat pemasangan</string>
|
||||
<string name="susfs_hide_mounts_all_enabled">Mengaktifkan penyembunyian pemasangan SUS untuk semua proses</string>
|
||||
<string name="susfs_hide_mounts_all_disabled">Menonaktifkan penyembunyian pemasangan SUS untuk semua proses</string>
|
||||
<string name="susfs_hide_mounts_recommendation">Disarankan untuk menonaktifkan setelah layar tidak terkunci, atau selama tahap service.sh atau boot-completed.sh, karena ini seharusnya memperbaiki masalah pada beberapa aplikasi root yang bergantung pada pemasangan yang dipasang oleh proses KSU</string>
|
||||
<string name="susfs_hide_mounts_current_setting">Pengaturan saat ini: %s</string>
|
||||
<string name="susfs_hide_mounts_setting_all">Sembunyikan untuk semua proses</string>
|
||||
<string name="susfs_hide_mounts_setting_non_ksu">Sembunyikan hanya untuk proses non-KSU</string>
|
||||
<string name="susfs_run">Jalankan</string>
|
||||
<string name="kernel_simple_kernel">Mode Ringkas Versi Kernel</string>
|
||||
<string name="kernel_simple_kernel_summary">Aktifkan atau nonaktifkan mode bersih yang ditampilkan oleh versi kernel SukiSU</string>
|
||||
<string name="susfs_android_data_path_set">Jalur Data Android telah diatur ke: %s</string>
|
||||
<string name="susfs_sdcard_path_set">Jalur SD card telah diatur ke: %s</string>
|
||||
<string name="susfs_path_setup_warning">Penyiapan jalur mungkin tidak sepenuhnya berhasil, tetapi jalur SUS akan terus ditambahkan</string>
|
||||
<!-- 备份和还原相关字符串 -->
|
||||
<string name="susfs_backup_title">Backup</string>
|
||||
<string name="susfs_backup_description">Buat backup dari semua konfigurasi SuSFS. File backup akan mencakup semua pengaturan, jalur, dan konfigurasi.</string>
|
||||
<string name="susfs_backup_create">Buat Backup</string>
|
||||
<string name="susfs_backup_success">Backup berhasil dibuat: %s</string>
|
||||
<string name="susfs_backup_failed">Pembuatan backup gagal: %s</string>
|
||||
<string name="susfs_backup_file_not_found">File backup tidak ditemukan</string>
|
||||
<string name="susfs_backup_invalid_format">Format file backup tidak valid</string>
|
||||
<string name="susfs_backup_version_mismatch">Versi backup tidak cocok, tetapi akan mencoba memulihkan</string>
|
||||
<string name="susfs_restore_title">Pulihkan</string>
|
||||
<string name="susfs_restore_description">Pulihkan konfigurasi SuSFS dari file backup. Ini akan menimpa semua pengaturan saat ini.</string>
|
||||
<string name="susfs_restore_select_file">Pilih File Backup</string>
|
||||
<string name="susfs_restore_success" formatted="false">Konfigurasi berhasil dipulihkan dari backup yang dibuat pada %s dari perangkat: %s</string>
|
||||
<string name="susfs_restore_failed">Pemulihan gagal: %s</string>
|
||||
<string name="susfs_restore_confirm_title">Konfirmasi Pemulihan</string>
|
||||
<string name="susfs_restore_confirm_description">Ini akan menimpa semua konfigurasi SuSFS saat ini. Apakah Anda yakin ingin melanjutkan?</string>
|
||||
<string name="susfs_restore_confirm">Pulihkan</string>
|
||||
<string name="susfs_backup_info_date">Tanggal Backup: %s</string>
|
||||
<string name="susfs_backup_info_device">Perangkat: %s</string>
|
||||
<string name="susfs_backup_info_version">Versi: %s</string>
|
||||
<string name="hide_bl_script">Status kunci</string>
|
||||
<string name="hide_bl_script_description">Timpa atribut status penguncian bootloader dalam mode layanan late_start</string>
|
||||
<string name="cleanup_residue">Bersihkan Residu</string>
|
||||
<string name="cleanup_residue_description">Bersihkan file dan direktori sisa dari berbagai modul dan alat (mungkin terhapus secara tidak sengaja, mengakibatkan kehilangan dan gagal memulai, gunakan dengan hati-hati)</string>
|
||||
<string name="susfs_edit_sus_path">Edit Jalur SUS</string>
|
||||
<string name="susfs_edit_sus_mount">Edit Pemasangan SUS</string>
|
||||
<string name="susfs_edit_try_umount">Edit Coba Umount</string>
|
||||
<string name="edit_kstat_statically_title">Edit Konfigurasi Statis Kstat</string>
|
||||
<string name="edit_kstat_path_title">Edit Jalur Kstat</string>
|
||||
<string name="susfs_save">Simpan</string>
|
||||
<string name="edit">Edit</string>
|
||||
<string name="delete">Hapus</string>
|
||||
<string name="update">Perbarui</string>
|
||||
<string name="kstat_config_updated">Pembaruan konfigurasi Kstat</string>
|
||||
<string name="kstat_path_updated">Pembaruan jalur Kstat</string>
|
||||
<string name="susfs_update_full_clone">Pembaruan full clone Susfs</string>
|
||||
<string name="umount_zygote_iso_service">Lepas Layanan Isolasi Zygote</string>
|
||||
<string name="umount_zygote_iso_service_description">Aktifkan opsi ini untuk melepaskan titik pemasangan layanan isolasi Zygote saat sistem mulai</string>
|
||||
<string name="umount_zygote_iso_service_enabled">Lepas layanan isolasi Zygote diaktifkan</string>
|
||||
<string name="umount_zygote_iso_service_disabled">Lepas layanan isolasi Zygote dinonaktifkan</string>
|
||||
<string name="app_paths_section">Jalur Aplikasi</string>
|
||||
<string name="other_paths_section">Jalur lainnya</string>
|
||||
<string name="add_custom_path">Lainnya</string>
|
||||
<string name="add_app_path">Aplikasi</string>
|
||||
<string name="susfs_add_app_path">Tambahkan Jalur Aplikasi</string>
|
||||
<string name="search_apps">Cari Aplikasi</string>
|
||||
<string name="selected_apps_count">%1$d aplikasi dipilih</string>
|
||||
<string name="already_added_apps_count">%1$d aplikasi sudah ditambahkan</string>
|
||||
<string name="all_apps_already_added">Semua aplikasi telah ditambahkan</string>
|
||||
<string name="dynamic_manager_title">Konfigurasi Tanda Tangan Dinamis</string>
|
||||
<string name="dynamic_manager_enabled_summary">Diaktifkan (Ukuran: %s)</string>
|
||||
<string name="dynamic_manager_disabled">Dinonaktifkan</string>
|
||||
<string name="enable_dynamic_manager">Aktifkan Tanda Tangan Dinamis</string>
|
||||
<string name="signature_size">Ukuran Tanda Tangan</string>
|
||||
<string name="signature_hash">Hash Tanda Tangan</string>
|
||||
<string name="hash_must_be_64_chars">Hash harus 64 karakter heksadesimal</string>
|
||||
<string name="dynamic_manager_set_success">Konfigurasi tanda tangan dinamis berhasil diatur</string>
|
||||
<string name="dynamic_manager_set_failed">Gagal mengatur konfigurasi tanda tangan dinamis</string>
|
||||
<string name="invalid_sign_config">Konfigurasi tanda tangan tidak valid</string>
|
||||
<string name="dynamic_manager_disabled_success">Tanda tangan dinamis dinonaktifkan</string>
|
||||
<string name="dynamic_manager_clear_failed">Gagal membersihkan tanda tangan dinamis</string>
|
||||
<string name="dynamic_managerature">Dinamis</string>
|
||||
<string name="signature_index">Tanda Tangan %1$d</string>
|
||||
<string name="unknown_signature">Tidak diketahui</string>
|
||||
<string name="multi_manager_list">Manajer Aktif</string>
|
||||
<string name="no_active_manager">Tidak ada manajer aktif</string>
|
||||
<string name="default_signature">SukiSU</string>
|
||||
<string name="home_zygisk_implement">Implementasi Zygisk</string>
|
||||
<!-- 循环路径相关 -->
|
||||
<string name="susfs_tab_sus_loop_paths">Jalur Loop SUS</string>
|
||||
<string name="susfs_add_sus_loop_path">Tambahkan Jalur Loop SUS</string>
|
||||
<string name="susfs_edit_sus_loop_path">Edit Jalur Loop SUS</string>
|
||||
<string name="susfs_loop_path_added_success">Jalur loop SUS berhasil ditambahkan: %1$s</string>
|
||||
<string name="susfs_loop_path_removed">Jalur loop SUS dihapus: %1$s</string>
|
||||
<string name="susfs_loop_path_updated">Jalur loop SUS diperbarui: %1$s -> %2$s</string>
|
||||
<string name="susfs_no_loop_paths_configured">Tidak ada jalur loop SUS yang dikonfigurasi</string>
|
||||
<string name="susfs_reset_loop_paths_title">Setel Ulang Jalur Loop</string>
|
||||
<string name="susfs_reset_loop_paths_message">Apakah Anda yakin ingin menghapus semua jalur loop SUS? Tindakan ini tidak dapat dibatalkan.</string>
|
||||
<string name="susfs_loop_path_label">Jalur Loop</string>
|
||||
<string name="susfs_loop_path_placeholder">/data/contoh/jalur</string>
|
||||
<string name="susfs_loop_path_restriction_warning">Catatan: Hanya jalur TIDAK di dalam /storage/ dan /sdcard/ yang dapat ditambahkan melalui jalur loop.</string>
|
||||
<string name="susfs_loop_path_invalid_location">Kesalahan: Jalur loop tidak dapat berada di dalam direktori /storage/ atau /sdcard/</string>
|
||||
<string name="loop_paths_section">Jalur Loop</string>
|
||||
<string name="add_loop_path">Tambahkan Jalur Loop</string>
|
||||
<!-- 循环路径功能描述 -->
|
||||
<string name="sus_loop_path_feature_label">Jalur Loop SUS</string>
|
||||
<string name="sus_loop_paths_description_title">Konfigurasi Jalur Loop</string>
|
||||
<string name="sus_loop_paths_description_text">Jalur loop ditandai ulang sebagai SUS_PATH pada setiap startup aplikasi pengguna non-root atau layanan terisolasi. Ini membantu mengatasi masalah di mana jalur yang ditambahkan mungkin memiliki status inode direset atau inode dibuat ulang di kernel.</string>
|
||||
<!-- 模块签名功能描述 -->
|
||||
<string name="module_verified">Tervalidasi</string>
|
||||
<string name="module_signature_verified">Tanda tangan modul tervalidasi</string>
|
||||
<string name="module_signature_verification">Verifikasi Tanda Tangan</string>
|
||||
<string name="module_signature_verification_summary">Verifikasi tanda tangan secara paksa saat modul dipasang. (Hanya tersedia untuk arsitektur ARM)</string>
|
||||
<string name="module_signature_invalid">Penerbit tidak dikenal</string>
|
||||
<string name="module_signature_invalid_message">Modul yang tidak ditandatangani mungkin tidak lengkap. Untuk melindungi perangkat Anda, pemasangan modul ini diblokir.</string>
|
||||
<string name="module_signature_verification_failed">Modul yang tidak ditandatangani mungkin tidak lengkap. Apakah Anda ingin mengizinkan modul berikut dari penerbit tidak dikenal untuk dipasang di perangkat ini?</string>
|
||||
</resources>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<string name="home_click_to_learn_kernelsu">KernelSU のインストール方法やモジュールの使い方を学習できます。</string>
|
||||
<string name="home_support_title">支援する</string>
|
||||
<string name="home_support_content">KernelSU は今後も無料でオープンソースです。ですが、寄付をして頂けると開発者への貢献になります。</string>
|
||||
<string name="about_source_code"><![CDATA[ソースコードは %1$s で確認できます。<br/>%2$s チャンネルにご参加ください。]]></string>
|
||||
<string name="about_source_code"><![CDATA[ソースコードは %1$s で確認できます<br/>%2$s チャンネルにご参加ください<br/><br/>アニメキャラのスタンプ付き画像の著作権は%3$sにあり、画像の Brand Intellectual Property は%4$sによって所有され。これらのファイルを使用する前に、%5$sを遵守することに加えて、アートコンテンツを使用するために前の 2 人の作者から許可を得る必要があります。]]></string>
|
||||
<string name="profile_default">デフォルト</string>
|
||||
<string name="profile_template">テンプレート</string>
|
||||
<string name="profile_custom">カスタム</string>
|
||||
@@ -179,6 +179,8 @@
|
||||
<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="hide_zygisk_implement">Zygisk のステータスを非表示</string>
|
||||
<string name="hide_zygisk_implement_summary">ホームページ上の Zygisk 実装情報を非表示にします。</string>
|
||||
<string name="hide_link_card">リンクカードのステータスを非表示</string>
|
||||
<string name="hide_link_card_summary">ホームページ上のリンクカード情報を非表示にします。</string>
|
||||
<string name="hide_tag_card">モジュールラベルの行を非表示</string>
|
||||
@@ -520,4 +522,107 @@
|
||||
<string name="susfs_sdcard_path_set">SD カードのパスは次のように設定済みです: %s</string>
|
||||
<string name="susfs_path_setup_warning">パスの設定は完全に成功しない可能性がありますが、SUS パスは引き続き追加されます。</string>
|
||||
<!-- 备份和还原相关字符串 -->
|
||||
<string name="susfs_backup_title">バックアップ</string>
|
||||
<string name="susfs_backup_description">SuSFS のすべての設定のバックアップを作成します。バックアップファイルは「すべての設定、パス、構成」が含まれます。</string>
|
||||
<string name="susfs_backup_create">バックアップを作成</string>
|
||||
<string name="susfs_backup_success">バックアップの作成に成功しました: %s</string>
|
||||
<string name="susfs_backup_failed">バックアップの作成に失敗しました: %s</string>
|
||||
<string name="susfs_backup_file_not_found">バックアップファイルが見つかりません</string>
|
||||
<string name="susfs_backup_invalid_format">無効なバックアップファイル形式</string>
|
||||
<string name="susfs_backup_version_mismatch">バックアップバージョンが一致しませんが、復元を試みます。</string>
|
||||
<string name="susfs_restore_title">復元</string>
|
||||
<string name="susfs_restore_description">SuSFS の構成をバックアップファイルから復元します。これにより、現在の設定がすべて上書きされます。</string>
|
||||
<string name="susfs_restore_select_file">バックアップファイルを選択</string>
|
||||
<string name="susfs_restore_success" formatted="false">デバイス: %s から「%s」に作成されたバックアップから構成が正常に復元されました。</string>
|
||||
<string name="susfs_restore_failed">復元に失敗しました: %s</string>
|
||||
<string name="susfs_restore_confirm_title">復元を確認</string>
|
||||
<string name="susfs_restore_confirm_description">これにより現在の SuSFS 構成がすべて上書きされます。続行してもよろしいですか?</string>
|
||||
<string name="susfs_restore_confirm">復元</string>
|
||||
<string name="susfs_backup_info_date">バックアップ日時: %s</string>
|
||||
<string name="susfs_backup_info_device">デバイス: %s</string>
|
||||
<string name="susfs_backup_info_version">バージョン: %s</string>
|
||||
<string name="hide_bl_script">ロック状態</string>
|
||||
<string name="hide_bl_script_description">late_start サービスモードでブートローダーのロック状態属性を上書きする</string>
|
||||
<string name="cleanup_residue">残骸をクリーンアップ</string>
|
||||
<string name="cleanup_residue_description">様々なモジュールや残骸となったツールのファイルとディレクトリをクリーンアップします (誤って削除すると損失や起動の失敗に繋がる可能性があるため、注意して使用してください)</string>
|
||||
<string name="susfs_edit_sus_path">SUS のパスを編集</string>
|
||||
<string name="susfs_edit_sus_mount">SUS マウントを編集</string>
|
||||
<string name="susfs_edit_try_umount">アンマウントを試すを編集</string>
|
||||
<string name="edit_kstat_statically_title">Kstat 静的構成を編集</string>
|
||||
<string name="edit_kstat_path_title">Kstat のパスを編集</string>
|
||||
<string name="susfs_save">保存</string>
|
||||
<string name="edit">編集</string>
|
||||
<string name="delete">消去</string>
|
||||
<string name="update">更新</string>
|
||||
<string name="kstat_config_updated">Kstat の構成を更新</string>
|
||||
<string name="kstat_path_updated">Kstat のパスを更新</string>
|
||||
<string name="susfs_update_full_clone">フルクローンの SuSFS を更新</string>
|
||||
<string name="umount_zygote_iso_service">Zygote 分離サービスをアンマウント</string>
|
||||
<string name="umount_zygote_iso_service_description">このオプションを有効化すると、システムの起動時に Zygote 分離サービスのマウントポイントがアンマウントされます。</string>
|
||||
<string name="umount_zygote_iso_service_enabled">Zygote 分離サービスのアンマウントが有効です</string>
|
||||
<string name="umount_zygote_iso_service_disabled">Zygote 分離サービスのアンマウントが無効です</string>
|
||||
<string name="app_paths_section">アプリのパス</string>
|
||||
<string name="other_paths_section">その他のパス</string>
|
||||
<string name="add_custom_path">その他</string>
|
||||
<string name="add_app_path">アプリ</string>
|
||||
<string name="susfs_add_app_path">追加のアプリパス</string>
|
||||
<string name="search_apps">アプリを検索</string>
|
||||
<string name="selected_apps_count">%1$d 個のアプリを選択済み</string>
|
||||
<string name="already_added_apps_count">%1$d 個のアプリを追加済み</string>
|
||||
<string name="all_apps_already_added">すべてのアプリが追加されました</string>
|
||||
<string name="dynamic_manager_title">動的な署名の構成</string>
|
||||
<string name="dynamic_manager_enabled_summary">有効 (サイズ: %s)</string>
|
||||
<string name="dynamic_manager_disabled">無効</string>
|
||||
<string name="enable_dynamic_manager">動的な署名を有効化</string>
|
||||
<string name="signature_size">署名のサイズ</string>
|
||||
<string name="signature_hash">署名のハッシュ</string>
|
||||
<string name="hash_must_be_64_chars">ハッシュは 64 桁の 16 進数の文字列でなければなりません。</string>
|
||||
<string name="dynamic_manager_set_success">動的な署名の構成が正常に設定されました</string>
|
||||
<string name="dynamic_manager_set_failed">動的な署名の構成の設定に失敗しました</string>
|
||||
<string name="invalid_sign_config">無効な署名の構成</string>
|
||||
<string name="dynamic_manager_disabled_success">動的な署名が無効です</string>
|
||||
<string name="dynamic_manager_clear_failed">動的な署名の消去に失敗しました</string>
|
||||
<string name="dynamic_managerature">動的</string>
|
||||
<string name="signature_index">署名 %1$d</string>
|
||||
<string name="unknown_signature">不明</string>
|
||||
<string name="multi_manager_list">有効なマネージャー</string>
|
||||
<string name="no_active_manager">有効なマネージャーがありません</string>
|
||||
<string name="default_signature">SukiSU</string>
|
||||
<string name="home_zygisk_implement">Zygisk を実装</string>
|
||||
<!-- 循环路径相关 -->
|
||||
<string name="susfs_tab_sus_loop_paths">SUS ループパス</string>
|
||||
<string name="susfs_add_sus_loop_path">SUS ループパスを追加</string>
|
||||
<string name="susfs_edit_sus_loop_path">SUS ループパスを編集</string>
|
||||
<string name="susfs_loop_path_added_success">SUS ループパスが正常に追加されました: %1$s</string>
|
||||
<string name="susfs_loop_path_removed">SUS ループパスが削除されました: %1$s</string>
|
||||
<string name="susfs_loop_path_updated">SUS ループパスが更新されました: %1$s -> %2$s</string>
|
||||
<string name="susfs_no_loop_paths_configured">SUS ループパスが構成されていません</string>
|
||||
<string name="susfs_reset_loop_paths_title">ループパスをリセット</string>
|
||||
<string name="susfs_reset_loop_paths_message">すべての SUS ループパスを消去してもよろしいですか?この操作は元に戻せません。</string>
|
||||
<string name="susfs_loop_path_label">ループパス</string>
|
||||
<string name="susfs_loop_path_placeholder">/data/example/path</string>
|
||||
<string name="susfs_loop_path_restriction_warning">注意: ループパス経由で追加できるのは /storage/ と /sdcard/ 内にないパスのみです。</string>
|
||||
<string name="susfs_loop_path_invalid_location">エラー: ループパスは /storage/ または /sdcard/ のディレクトリ内に配置できません。</string>
|
||||
<string name="loop_paths_section">ループパス</string>
|
||||
<string name="add_loop_path">ループパスを追加</string>
|
||||
<!-- 循环路径功能描述 -->
|
||||
<string name="sus_loop_path_feature_label">SUS ループパス</string>
|
||||
<string name="sus_loop_paths_description_title">ループパスの構成</string>
|
||||
<string name="sus_loop_paths_description_text">ループパスは、非 root ユーザーアプリまたは独立したサービスの起動ごとに SUS_PATH として再設定されます。これにより、追加されたパスの inode ステータスがリセットされたり、カーネル内で inode が再生成される問題に対処できます。</string>
|
||||
<string name="avc_log_spoofing">AVC ログの偽装</string>
|
||||
<string name="avc_log_spoofing_enabled">AVC ログの偽装が有効化されました</string>
|
||||
<string name="avc_log_spoofing_disabled">AVC ログの偽装が無効化されました</string>
|
||||
<string name="avc_log_spoofing_description">無効: カーネルの AVC ログに表示される「su」の SUS T コンテキストの偽装を無効化します。\n
|
||||
有効: カーネルの AVC ログに表示される「kernel」を使用して「su」の SUS T コンテキストを偽装する機能を有効化します。</string>
|
||||
<string name="avc_log_spoofing_warning">重要な注意事項:\n
|
||||
- カーネルはデフォルトで「0」に設定されています。\n
|
||||
- これを有効化すると、開発者が何らかの権限や SELinux の問題をデバッグするときに原因を特定するのが難しくなる場合があるため、デバッグ時はこれを無効化することをお勧めします。</string>
|
||||
<!-- 模块签名功能描述 -->
|
||||
<string name="module_verified">検証済み</string>
|
||||
<string name="module_signature_verified">モジュールの署名が検証されました</string>
|
||||
<string name="module_signature_verification">署名の検証</string>
|
||||
<string name="module_signature_verification_summary">モジュールのインストール時に署名の検証を強制します。(ARMアーキテクチャのみ)</string>
|
||||
<string name="module_signature_invalid">不明な発行元</string>
|
||||
<string name="module_signature_invalid_message">署名されていないモジュールは不完全な可能性があります。デバイスを保護するため、このモジュールのインストールをブロックしました。</string>
|
||||
<string name="module_signature_verification_failed">署名されていないモジュールは不完全な可能性があります。不明な発行元のモジュールをこのデバイスにインストールすることを許可しますか?</string>
|
||||
</resources>
|
||||
|
||||
@@ -179,6 +179,8 @@
|
||||
<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="hide_zygisk_implement">Скрыть статус Zygisk</string>
|
||||
<string name="hide_zygisk_implement_summary">Скрывать информацию об имплементации Zygisk на главной странице</string>
|
||||
<string name="hide_link_card">Скрыть карточки со ссылками</string>
|
||||
<string name="hide_link_card_summary">Скрывать карточки со ссылками на главной странице</string>
|
||||
<string name="hide_tag_card">Скрыть строки модуля</string>
|
||||
@@ -254,7 +256,7 @@
|
||||
<string name="horizon_flashing">Прошивка ядра…</string>
|
||||
<string name="horizon_flash_complete_status">Прошивка завершена</string>
|
||||
<!-- Slot selection related strings -->
|
||||
<string name="select_slot_title">Выбрать слот Flash</string>
|
||||
<string name="select_slot_title">Выберите слот для прошивки</string>
|
||||
<string name="select_slot_description">Пожалуйста, выберите целевой слот для прошивки загрузки</string>
|
||||
<string name="slot_a">Слот A</string>
|
||||
<string name="slot_b">Слот B</string>
|
||||
@@ -539,8 +541,84 @@
|
||||
<string name="susfs_backup_info_date">Дата резервного копирования: %s</string>
|
||||
<string name="susfs_backup_info_device">Устройство: %s</string>
|
||||
<string name="susfs_backup_info_version">Версия: %s</string>
|
||||
<string name="hide_bl_script">Скрыть BL скрипт</string>
|
||||
<string name="hide_bl_script_description">Включить скрипт Hide Bootloader Unlock Status</string>
|
||||
<string name="hide_bl_script">Заблокированное состояние</string>
|
||||
<string name="hide_bl_script_description">Переопределить свойство состояния блокировки загрузчика в режиме службы late_start</string>
|
||||
<string name="cleanup_residue">Очистка</string>
|
||||
<string name="cleanup_residue_description">Очистка остаточных файлов и каталогов различных модулей и инструментов (может быть удален по ошибке, в результате потери и неспособности начаться, используйте с осторожностью)</string>
|
||||
<string name="susfs_edit_sus_path">Редактировать путь SUS</string>
|
||||
<string name="susfs_edit_sus_mount">Редактировать точку монтирования SUS</string>
|
||||
<string name="susfs_edit_try_umount">Изменить попробовать размонтировать</string>
|
||||
<string name="edit_kstat_statically_title">Изменить статическую конфигурацию Kstat</string>
|
||||
<string name="edit_kstat_path_title">Редактировать путь Kstat</string>
|
||||
<string name="susfs_save">Сохранить</string>
|
||||
<string name="edit">Редактировать</string>
|
||||
<string name="delete">Удалить</string>
|
||||
<string name="update">Обновить</string>
|
||||
<string name="kstat_config_updated">Обновить конфиг Kstat</string>
|
||||
<string name="kstat_path_updated">Обновить путь Kstat</string>
|
||||
<string name="susfs_update_full_clone">Полное обновление клона SuSFS</string>
|
||||
<string name="umount_zygote_iso_service">Размонтировать сервис изоляции Zygote</string>
|
||||
<string name="umount_zygote_iso_service_description">Включите эту опцию, чтобы размонтировать точки монтирования Zygote при запуске системы</string>
|
||||
<string name="umount_zygote_iso_service_enabled">Размонтирование служб Zygote включено</string>
|
||||
<string name="umount_zygote_iso_service_disabled">Размонтирование служб Zygote выключено</string>
|
||||
<string name="app_paths_section">Путь к приложению</string>
|
||||
<string name="other_paths_section">Другие пути</string>
|
||||
<string name="add_custom_path">Другое</string>
|
||||
<string name="add_app_path">Приложение</string>
|
||||
<string name="susfs_add_app_path">Добавить путь приложения</string>
|
||||
<string name="search_apps">Поиск приложений</string>
|
||||
<string name="selected_apps_count">Выбрано %1$d приложений</string>
|
||||
<string name="already_added_apps_count">%1$d приложений уже добавлено</string>
|
||||
<string name="all_apps_already_added">Все приложения были добавлены</string>
|
||||
<string name="dynamic_manager_title">Конфигурация динамической подписи</string>
|
||||
<string name="dynamic_manager_enabled_summary">Включено (размер: %s)</string>
|
||||
<string name="dynamic_manager_disabled">Выключено</string>
|
||||
<string name="enable_dynamic_manager">Включить динамическую подпись</string>
|
||||
<string name="signature_size">Размер подписи</string>
|
||||
<string name="signature_hash">Хэш подписи</string>
|
||||
<string name="hash_must_be_64_chars">Хеш должен содержать 64 шестнадцатеричных символа</string>
|
||||
<string name="dynamic_manager_set_success">Конфигурация динамической подписи успешно установлена</string>
|
||||
<string name="dynamic_manager_set_failed">Не удалось установить конфигурацию динамической подписи</string>
|
||||
<string name="invalid_sign_config">Неверная конфигурация подписи</string>
|
||||
<string name="dynamic_manager_disabled_success">Динамическая подпись отключена</string>
|
||||
<string name="dynamic_manager_clear_failed">Не удалось очистить динамическую подпись</string>
|
||||
<string name="dynamic_managerature">Динамическая</string>
|
||||
<string name="signature_index">Подпись %1$d</string>
|
||||
<string name="unknown_signature">Неизвестно</string>
|
||||
<string name="multi_manager_list">Активный менеджер</string>
|
||||
<string name="no_active_manager">Нет активного менеджера</string>
|
||||
<string name="default_signature">SukiSU</string>
|
||||
<string name="home_zygisk_implement">Реализация Zygisk</string>
|
||||
<!-- 循环路径相关 -->
|
||||
<string name="susfs_tab_sus_loop_paths">Пути цикла SUS</string>
|
||||
<string name="susfs_add_sus_loop_path">Добавить путь SUS </string>
|
||||
<string name="susfs_edit_sus_loop_path">Редактировать путь SUS</string>
|
||||
<string name="susfs_loop_path_added_success">Путь SUS успешно добавлен: %1$s</string>
|
||||
<string name="susfs_loop_path_removed">Удален путь цикла SUS: %1$s</string>
|
||||
<string name="susfs_loop_path_updated">Обновлен путь цикла SUS: %1$s -> %2$s</string>
|
||||
<string name="susfs_no_loop_paths_configured">SUS пути не настроены</string>
|
||||
<string name="susfs_reset_loop_paths_title">Сбросить SUS пути</string>
|
||||
<string name="susfs_reset_loop_paths_message">Вы уверены, что хотите очистить все пути цикла SUS? Это действие нельзя отменить.</string>
|
||||
<string name="susfs_loop_path_label">Циклический путь</string>
|
||||
<string name="susfs_loop_path_placeholder">/data/example/path</string>
|
||||
<string name="susfs_loop_path_restriction_warning">Примечание: Только пути НЕ внутри /storage/ и /sdcard/ могут быть добавлены по цикловым путям.</string>
|
||||
<string name="susfs_loop_path_invalid_location">Ошибка: пути не могут быть внутри /storage/ или /sdcard/ каталогов</string>
|
||||
<string name="loop_paths_section">Циклические пути</string>
|
||||
<string name="add_loop_path">Добавить циклический путь</string>
|
||||
<!-- 循环路径功能描述 -->
|
||||
<string name="sus_loop_path_feature_label">Циклический путь SUS</string>
|
||||
<string name="sus_loop_paths_description_title">Конфигурация пути цикла</string>
|
||||
<string name="sus_loop_paths_description_text">Пути цикла повторно отмечены как SUS_PATH в каждом пользовательском приложении, не являющемся root, или изолированном запуске службы. Это помогает решить проблемы, в которых добавленные пути могут иметь сброс статуса inode или повторно созданные inode в ядре.</string>
|
||||
<string name="avc_log_spoofing">Спуф AVC лога</string>
|
||||
<string name="avc_log_spoofing_enabled">Спуф AVC лога включен</string>
|
||||
<string name="avc_log_spoofing_disabled">Спуф AVC лога отключен</string>
|
||||
<string name="avc_log_spoofing_description">отключен: Отключить спуффинг sus tcontext от \'su\' показывать в Avc логе в ядре.\n включен: Включить спуффинг sus tcontext от \'su\' с \'ядром\' показывать в Avc логе в ядре</string>
|
||||
<!-- 模块签名功能描述 -->
|
||||
<string name="module_verified">Проверенные</string>
|
||||
<string name="module_signature_verified">Подпись модуля подтверждена</string>
|
||||
<string name="module_signature_verification">Подтверждение подписи</string>
|
||||
<string name="module_signature_verification_summary">При установке модуля принудительно проверять подпись. (Только для архитектуры ARM)</string>
|
||||
<string name="module_signature_invalid">Неизвестный издатель</string>
|
||||
<string name="module_signature_invalid_message">Неподписанные модули могут быть неполными. Для защиты устройства установка этого модуля была заблокирована.</string>
|
||||
<string name="module_signature_verification_failed">Неподписанные модули могут быть неполными. Вы хотите разрешить установку на этом устройстве следующего модуля от неизвестного издателя?</string>
|
||||
</resources>
|
||||
|
||||
@@ -36,6 +36,8 @@
|
||||
<string name="module_uninstall_confirm">%s modülünü kaldırmak istediğinizden emin misiniz?</string>
|
||||
<string name="module_uninstall_success">%s kaldırıldı</string>
|
||||
<string name="module_uninstall_failed">Kaldırılamadı: %s</string>
|
||||
<string name="module_version">Sürüm</string>
|
||||
<string name="module_author">Geliştirici</string>
|
||||
<string name="refresh">Yenile</string>
|
||||
<string name="show_system_apps">Sistem uygulamalarını göster</string>
|
||||
<string name="hide_system_apps">Sistem uygulamalarını gizle</string>
|
||||
@@ -44,6 +46,7 @@
|
||||
<string name="reboot_to_apply">Etkili olması için yeniden başlatın</string>
|
||||
<string name="module_magisk_conflict">Magisk ile çakışma nedeniyle modüller kullanılamıyor!</string>
|
||||
<string name="home_learn_kernelsu">KernelSU\'yu öğrenin</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">KernelSU\'yu nasıl yükleyeceğinizi ve modülleri nasıl kullanacağınızı öğrenin</string>
|
||||
<string name="home_support_title">Bizi destekleyin</string>
|
||||
<string name="home_support_content">KernelSU, her zaman olduğu gibi ücretsiz ve açık kaynaklıdır. Ancak bir bağış yaparak bizi destekleyebilirsiniz.</string>
|
||||
@@ -98,6 +101,7 @@
|
||||
<string name="settings_check_update">Güncelleme kontrolü</string>
|
||||
<string name="settings_check_update_summary">Uygulama açıldığında otomatik olarak güncellemeleri kontrol et</string>
|
||||
<string name="grant_root_failed">Root yetkisi verilemedi!</string>
|
||||
<string name="action">Eylem</string>
|
||||
<string name="close">Kapat</string>
|
||||
<string name="enable_web_debugging">WebView hata ayıklama etkinleştir</string>
|
||||
<string name="enable_web_debugging_summary">WebUI\'yi hata ayıklamak için kullanılabilir. Sadece ihtiyaç duyulduğunda etkinleştirin.</string>
|
||||
@@ -107,6 +111,7 @@
|
||||
<string name="install_inactive_slot_warning">Cihazınız, yeniden başlatma sonrasında **ZORUNLU** olarak mevcut etkin olmayan yuvaya önyükleme yapacaktır!\nSadece OTA tamamlandıktan sonra bu seçeneği kullanın.\nDevam etmek istiyor musunuz?</string>
|
||||
<string name="install_next">İleri</string>
|
||||
<string name="select_file_tip">%1$s bölüm görüntüsü önerilir</string>
|
||||
<string name="select_file_tip_vendor">(kararsız)</string>
|
||||
<string name="select_kmi">KMI seçin</string>
|
||||
<string name="settings_uninstall">Kaldır</string>
|
||||
<string name="settings_uninstall_temporary">Geçici olarak kaldır</string>
|
||||
@@ -161,6 +166,7 @@
|
||||
<string name="settings_disable_su_summary">Geçici olarak herhangi bir uygulamanın su komutu aracılığıyla root ayrıcalıkları elde etmesini devre dışı bırakır (mevcut root işlemleri etkilenmez).</string>
|
||||
<string name="module_install_multiple_confirm_with_names">Aşağıdaki %1$d modülü yüklemek istediğinizden emin misiniz? \n\n%2$s</string>
|
||||
<string name="more_settings">Daha fazla ayar</string>
|
||||
<string name="selinux">SELinux</string>
|
||||
<string name="selinux_enabled">Etkin</string>
|
||||
<string name="selinux_disabled">Devre dışı</string>
|
||||
<string name="simple_mode">Basit mod</string>
|
||||
@@ -171,6 +177,8 @@
|
||||
<string name="hide_other_info_summary">Gezinme çubuğu sayfasında süper kullanıcı, modül ve KPM modülü sayısı hakkında bilgi veren kırmızı noktayı gizler</string>
|
||||
<string name="hide_susfs_status">SuSFS durumunu gizle</string>
|
||||
<string name="hide_susfs_status_summary">Ana sayfadaki SuSFS durum bilgilerini gizle</string>
|
||||
<string name="hide_zygisk_implement">Zygisk durumunu gizle</string>
|
||||
<string name="hide_zygisk_implement_summary">Ana sayfada Zygisk uygulama bilgisini gizle</string>
|
||||
<string name="hide_link_card">Bağlantı Kartı Durumunu Gizle</string>
|
||||
<string name="hide_link_card_summary">Ana sayfadaki bağlantı kartı bilgilerini gizle</string>
|
||||
<string name="hide_tag_card">Modül etiket satırlarını gizle</string>
|
||||
@@ -198,8 +206,10 @@
|
||||
<string name="yes">Evet</string>
|
||||
<string name="no">Hayır</string>
|
||||
<string name="failed_reboot">Yeniden başlatma başarısız</string>
|
||||
<string name="kpm_title">KPM</string>
|
||||
<string name="kpm_empty">Şu anda yüklü çekirdek modülü yok</string>
|
||||
<string name="kpm_version">Sürüm</string>
|
||||
<string name="kpm_author">Geliştirici</string>
|
||||
<string name="kpm_uninstall">Kaldır</string>
|
||||
<string name="kpm_uninstall_success">Başarıyla kaldırıldı</string>
|
||||
<string name="kpm_uninstall_failed">Kaldırılamadı</string>
|
||||
@@ -259,6 +269,7 @@
|
||||
<string name="flash_failed_message">Flash\'lama başarısız</string>
|
||||
<!-- lkm/gki install -->
|
||||
<string name="Lkm_install_methods">LKM onarımı/yükle</string>
|
||||
<string name="GKI_install_methods">AnyKernel3 Flaşlama</string>
|
||||
<string name="kernel_version_log">Çekirdek sürümü:%1$s</string>
|
||||
<string name="tool_version_log">Kullanılan yama aracı:%1$s</string>
|
||||
<string name="configuration">Yapılandır</string>
|
||||
@@ -316,6 +327,7 @@
|
||||
<string name="kernel_flashing">Çekirdek Yükleniyor</string>
|
||||
<!-- 分类相关 -->
|
||||
<string name="category_all_apps">Tümü</string>
|
||||
<string name="category_root_apps">Root</string>
|
||||
<string name="category_custom_apps">Özel</string>
|
||||
<string name="category_default_apps">Varsayılan</string>
|
||||
<!-- 排序相关 -->
|
||||
@@ -349,6 +361,7 @@
|
||||
<string name="susfs_config_description_text">Bu özellik, SuSFS uname değerini ve build time spoofing\'i özelleştirmenize olanak tanır. Ayarlamak istediğiniz değerleri girin ve uygulayın.</string>
|
||||
<string name="susfs_uname_label">Uname Değeri</string>
|
||||
<string name="susfs_uname_placeholder">Lütfen özel uname değeri girin</string>
|
||||
<string name="susfs_build_time_label">Derleme Zamanı Sahteciliği</string>
|
||||
<string name="susfs_build_time_placeholder">Lütfen build time spoofing değeri girin</string>
|
||||
<string name="susfs_current_value">Mevcut değer: %s</string>
|
||||
<string name="susfs_current_build_time">Mevcut build time: %s</string>
|
||||
@@ -382,6 +395,8 @@
|
||||
<string name="susfs_add_sus_path">SUS Yolu Ekle</string>
|
||||
<string name="susfs_add_sus_mount">SUS Bağlama Noktası Ekle</string>
|
||||
<string name="susfs_add_try_umount">Bağlamayı Kaldırmayı Dene Ekle</string>
|
||||
<string name="susfs_sus_path_added_success">SUS yolu başarıyla eklendi</string>
|
||||
<string name="susfs_path_not_found_error">Yol bulunamadı hatası</string>
|
||||
<string name="susfs_path_label">Yol</string>
|
||||
<string name="susfs_mount_path_label">Bağlama Yolu</string>
|
||||
<string name="susfs_path_placeholder">örn.: /system/addon.d</string>
|
||||
@@ -392,6 +407,7 @@
|
||||
<string name="susfs_umount_mode_label">Bağlamayı Kaldır Modu</string>
|
||||
<string name="susfs_umount_mode_normal">Normal Bağlamayı Kaldır (0)</string>
|
||||
<string name="susfs_umount_mode_detach">Ayrı Bağlamayı Kaldır (1)</string>
|
||||
<string name="susfs_umount_mode_normal_short">Normal</string>
|
||||
<string name="susfs_umount_mode_detach_short">Ayrı</string>
|
||||
<string name="susfs_umount_mode_display">Mod: %1$s (%2$s)</string>
|
||||
<string name="susfs_try_umount_added_success">Bağlamayı kaldırmayı dene yolu başarıyla eklendi: %s</string>
|
||||
@@ -406,6 +422,7 @@
|
||||
<string name="susfs_reset_mounts_message">Bu, tüm SUS bağlama noktası yapılandırmalarını temizleyecektir. Devam etmek istiyor musunuz?</string>
|
||||
<string name="susfs_reset_umounts_title">Bağlamayı Kaldırmayı Dene Sıfırla</string>
|
||||
<string name="susfs_reset_umounts_message">Bu, tüm bağlamayı kaldırmayı dene yapılandırmalarını temizleyecektir. Devam etmek istiyor musunuz?</string>
|
||||
<string name="susfs_reset_path_title">Yol Ayarlarını Sıfırla</string>
|
||||
<!-- SuSFS Path Settings -->
|
||||
<string name="susfs_android_data_path_label">Android Veri Yolu</string>
|
||||
<string name="susfs_sdcard_path_label">SD Kart Yolu</string>
|
||||
@@ -443,8 +460,167 @@
|
||||
<!-- Settings related strings -->
|
||||
<string name="show_more_module_info">Daha Fazla Modül Bilgisi Göster</string>
|
||||
<string name="show_more_module_info_summary">Güncelleme JSON URL\'leri gibi ek modül bilgilerini göster</string>
|
||||
<string name="susfs_execution_location_label">Çalıştırma Konumu</string>
|
||||
<string name="susfs_current_execution_location">Mevcut çalıştırma konumu: %s</string>
|
||||
<string name="susfs_execution_location_service">Servis</string>
|
||||
<string name="susfs_execution_location_post_fs_data">Post-FS-Data</string>
|
||||
<string name="susfs_execution_location_service_description">Sistem servisleri başladıktan sonra çalıştır</string>
|
||||
<string name="susfs_execution_location_post_fs_data_description">Dosya sistemi bağlandıktan sonra ancak sistem tam olarak önyüklenmeden önce çalıştır, önyükleme döngüsüne neden olabilir</string>
|
||||
<string name="susfs_slot_info_title">Bölüm Bilgisi</string>
|
||||
<string name="susfs_slot_info_description">Mevcut önyükleme bölümü bilgisini görüntüleyin ve değerleri kopyalayın</string>
|
||||
<string name="susfs_current_active_slot">Mevcut Aktif Bölüm: %s</string>
|
||||
<string name="susfs_slot_uname">Uname: %s</string>
|
||||
<string name="susfs_slot_build_time">Derleme Zamanı: %s</string>
|
||||
<string name="susfs_slot_current_badge">Mevcut</string>
|
||||
<string name="susfs_slot_use_uname">Uname Kullan</string>
|
||||
<string name="susfs_slot_use_build_time">Derleme Zamanı Kullan</string>
|
||||
<string name="susfs_slot_info_unavailable">Bölüm bilgisi alınamıyor</string>
|
||||
<!-- SuSFS 自启动相关字符串 -->
|
||||
<string name="susfs_autostart_enabled_success">SuSFS otomatik başlatma modülü etkinleştirildi, modül yolu: %s</string>
|
||||
<string name="susfs_autostart_disabled_success">SuSFS otomatik başlatma modülü devre dışı bırakıldı</string>
|
||||
<!-- SuSFS Kstat相关字符串 -->
|
||||
<string name="susfs_tab_kstat_config">Kstat Yapılandırması</string>
|
||||
<string name="kstat_static_config_added">Kstat statik yapılandırması eklendi: %1$s</string>
|
||||
<string name="kstat_config_removed">Kstat yapılandırması kaldırıldı: %1$s</string>
|
||||
<string name="kstat_path_added">Kstat yolu eklendi: %1$s</string>
|
||||
<string name="kstat_path_removed">Kstat yolu kaldırıldı: %1$s</string>
|
||||
<string name="kstat_updated">Kstat güncellendi: %1$s</string>
|
||||
<string name="kstat_full_clone_updated">Kstat tam klonu güncellendi: %1$s</string>
|
||||
<string name="add_kstat_statically_title">Kstat Statik Yapılandırması Ekle</string>
|
||||
<string name="file_or_directory_path_label">Dosya/Dizin Yolu</string>
|
||||
<string name="hint_use_default_value">İpucu: Orijinal değeri kullanmak için ”default“ kullanabilirsiniz</string>
|
||||
<string name="add_kstat_path_title">Kstat Yolu Ekle</string>
|
||||
<string name="add">Ekle</string>
|
||||
<string name="reset_kstat_config_title">Kstat Yapılandırmasını Sıfırla</string>
|
||||
<string name="reset_kstat_config_message">Tüm Kstat yapılandırmalarını temizlemek istediğinizden emin misiniz? Bu işlem geri alınamaz.</string>
|
||||
<string name="kstat_config_description_title">Kstat Yapılandırma Açıklaması</string>
|
||||
<string name="kstat_config_description_add_statically">• add_sus_kstat_statically: Dosyaların/dizinlerin statik stat bilgisi</string>
|
||||
<string name="kstat_config_description_add">• add_sus_kstat: Bind bağlamadan önce yol ekle, orijinal stat bilgisini sakla</string>
|
||||
<string name="kstat_config_description_update">• update_sus_kstat: Hedef ino\'yu güncelle, boyut ve blokları değiştirmeden bırak</string>
|
||||
<string name="kstat_config_description_update_full_clone">• update_sus_kstat_full_clone: Sadece ino\'yu güncelle, diğer orijinal değerleri koru</string>
|
||||
<string name="static_kstat_config">Statik Kstat Yapılandırması</string>
|
||||
<string name="kstat_path_management">Kstat Yol Yönetimi</string>
|
||||
<string name="no_kstat_config_message">Henüz Kstat yapılandırması yok, eklemek için yukarıdaki düğmeye tıklayın</string>
|
||||
<!-- SuSFS Mount Hiding Control Related Strings -->
|
||||
<string name="susfs_hide_mounts_control_title">SUS Bağlama Noktası Gizleme Kontrolü</string>
|
||||
<string name="susfs_hide_mounts_control_description">İşlemler için SUS bağlama noktalarının gizleme davranışını kontrol et</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_label">Tüm işlemler için SUS bağlama noktalarını gizle</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_enabled_description">Etkinleştirildiğinde, SUS bağlama noktaları KSU işlemleri dahil tüm işlemlerden gizlenir</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_disabled_description">Devre dışı bırakıldığında, SUS bağlama noktaları sadece KSU dışı işlemlerden gizlenir, KSU işlemleri bağlama noktalarını görebilir</string>
|
||||
<string name="susfs_hide_mounts_all_enabled">Tüm işlemler için SUS bağlama noktalarını gizleme etkinleştirildi</string>
|
||||
<string name="susfs_hide_mounts_all_disabled">Tüm işlemler için SUS bağlama noktalarını gizleme devre dışı bırakıldı</string>
|
||||
<string name="susfs_hide_mounts_recommendation">Ekran kilidi açıldıktan sonra veya service.sh ya da boot-completed.sh aşamasında devre dışı bırakılması önerilir, çünkü bu, KSU işlemi tarafından bağlanan bağlama noktalarına dayanan bazı rootlu uygulamalardaki sorunu çözmelidir</string>
|
||||
<string name="susfs_hide_mounts_current_setting">Mevcut ayar: %s</string>
|
||||
<string name="susfs_hide_mounts_setting_all">Tüm işlemler için gizle</string>
|
||||
<string name="susfs_hide_mounts_setting_non_ksu">Sadece KSU dışı işlemler için gizle</string>
|
||||
<string name="susfs_run">Çalıştır</string>
|
||||
<string name="kernel_simple_kernel">Çekirdek Sürümü Özet Modu</string>
|
||||
<string name="kernel_simple_kernel_summary">SukiSU çekirdek sürümünün gösterdiği sade modu etkinleştirin veya devre dışı bırakın</string>
|
||||
<string name="susfs_android_data_path_set">Android Veri yolu şuna ayarlandı: %s</string>
|
||||
<string name="susfs_sdcard_path_set">SD kart yolu şuna ayarlandı: %s</string>
|
||||
<string name="susfs_path_setup_warning">Yol kurulumu tam olarak başarılı olmayabilir, ancak SUS yolları eklenmeye devam edecektir</string>
|
||||
<!-- 备份和还原相关字符串 -->
|
||||
<string name="susfs_backup_title">Yedekleme</string>
|
||||
<string name="susfs_backup_description">Tüm SuSFS yapılandırmalarının bir yedeğini oluşturun. Yedekleme dosyası tüm ayarları, yolları ve yapılandırmaları içerecektir.</string>
|
||||
<string name="susfs_backup_create">Yedek Oluştur</string>
|
||||
<string name="susfs_backup_success">Yedek başarıyla oluşturuldu: %s</string>
|
||||
<string name="susfs_backup_failed">Yedek oluşturma başarısız: %s</string>
|
||||
<string name="susfs_backup_file_not_found">Yedekleme dosyası bulunamadı</string>
|
||||
<string name="susfs_backup_invalid_format">Geçersiz yedekleme dosyası formatı</string>
|
||||
<string name="susfs_backup_version_mismatch">Yedekleme sürümü uyuşmuyor, ancak geri yüklemeye çalışılacak</string>
|
||||
<string name="susfs_restore_title">Geri Yükle</string>
|
||||
<string name="susfs_restore_description">SuSFS yapılandırmalarını bir yedekleme dosyasından geri yükleyin. Bu, tüm mevcut ayarların üzerine yazacaktır.</string>
|
||||
<string name="susfs_restore_select_file">Yedekleme Dosyası Seç</string>
|
||||
<string name="susfs_restore_success" formatted="false">%s tarihinde %s cihazından oluşturulan yedekten yapılandırma başarıyla geri yüklendi</string>
|
||||
<string name="susfs_restore_failed">Geri yükleme başarısız: %s</string>
|
||||
<string name="susfs_restore_confirm_title">Geri Yüklemeyi Onayla</string>
|
||||
<string name="susfs_restore_confirm_description">Bu, tüm mevcut SuSFS yapılandırmalarının üzerine yazacaktır. Devam etmek istediğinizden emin misiniz?</string>
|
||||
<string name="susfs_restore_confirm">Geri Yükle</string>
|
||||
<string name="susfs_backup_info_date">Yedekleme Tarihi: %s</string>
|
||||
<string name="susfs_backup_info_device">Cihaz: %s</string>
|
||||
<string name="susfs_backup_info_version">Sürüm: %s</string>
|
||||
<string name="hide_bl_script">Kilitli durum</string>
|
||||
<string name="hide_bl_script_description">late_start hizmet modunda önyükleme kilidi durumu özniteliğini geçersiz kıl</string>
|
||||
<string name="cleanup_residue">Kalıntıları Temizle</string>
|
||||
<string name="cleanup_residue_description">Çeşitli modüllerin ve araçların kalıntı dosyalarını ve dizinlerini temizleyin (yanlışlıkla silinerek kayba ve başlatılamamaya neden olabilir, dikkatli kullanın)</string>
|
||||
<string name="susfs_edit_sus_path">SUS Yolunu Düzenle</string>
|
||||
<string name="susfs_edit_sus_mount">SUS Bağlama Noktasını Düzenle</string>
|
||||
<string name="susfs_edit_try_umount">Ayırmayı Deneme Yolunu Düzenle</string>
|
||||
<string name="edit_kstat_statically_title">Kstat Statik Yapılandırmasını Düzenle</string>
|
||||
<string name="edit_kstat_path_title">Kstat Yolunu Düzenle</string>
|
||||
<string name="susfs_save">Kaydet</string>
|
||||
<string name="edit">Düzenle</string>
|
||||
<string name="delete">Sil</string>
|
||||
<string name="update">Güncelle</string>
|
||||
<string name="kstat_config_updated">Kstat yapılandırması güncellendi</string>
|
||||
<string name="kstat_path_updated">Kstat yolu güncellendi</string>
|
||||
<string name="susfs_update_full_clone">Susfs tam klon güncellemesi</string>
|
||||
<string name="umount_zygote_iso_service">Zygote İzolasyon Servisi Bağlantısını Kes</string>
|
||||
<string name="umount_zygote_iso_service_description">Sistem başlangıcında Zygote izolasyon servisi bağlama noktalarının bağlantısını kesmek için bu seçeneği etkinleştirin</string>
|
||||
<string name="umount_zygote_iso_service_enabled">Zygote izolasyon servisi bağlantı kesme etkinleştirildi</string>
|
||||
<string name="umount_zygote_iso_service_disabled">Zygote izolasyon servisi bağlantı kesme devre dışı bırakıldı</string>
|
||||
<string name="app_paths_section">Uygulama Yolu</string>
|
||||
<string name="other_paths_section">Diğer yollar</string>
|
||||
<string name="add_custom_path">Diğer</string>
|
||||
<string name="add_app_path">Uygulama</string>
|
||||
<string name="susfs_add_app_path">Uygulama Yolu Ekle</string>
|
||||
<string name="search_apps">Uygulama Ara</string>
|
||||
<string name="selected_apps_count">%1$d uygulama seçildi</string>
|
||||
<string name="already_added_apps_count">%1$d uygulama zaten eklendi</string>
|
||||
<string name="all_apps_already_added">Tüm uygulamalar eklendi</string>
|
||||
<string name="dynamic_manager_title">Dinamik İmza Yapılandırması</string>
|
||||
<string name="dynamic_manager_enabled_summary">Etkin (Boyut: %s)</string>
|
||||
<string name="dynamic_manager_disabled">Devre Dışı</string>
|
||||
<string name="enable_dynamic_manager">Dinamik İmzayı Etkinleştir</string>
|
||||
<string name="signature_size">İmza Boyutu</string>
|
||||
<string name="signature_hash">İmza Hash</string>
|
||||
<string name="hash_must_be_64_chars">Hash, 64 adet onaltılık karakterden oluşmalıdır</string>
|
||||
<string name="dynamic_manager_set_success">Dinamik imza yapılandırması başarıyla ayarlandı</string>
|
||||
<string name="dynamic_manager_set_failed">Dinamik imza yapılandırması ayarlanamadı</string>
|
||||
<string name="invalid_sign_config">Geçersiz imza yapılandırması</string>
|
||||
<string name="dynamic_manager_disabled_success">Dinamik imza devre dışı bırakıldı</string>
|
||||
<string name="dynamic_manager_clear_failed">Dinamik imza temizlenemedi</string>
|
||||
<string name="dynamic_managerature">Dinamik</string>
|
||||
<string name="signature_index">İmza %1$d</string>
|
||||
<string name="unknown_signature">Bilinmiyor</string>
|
||||
<string name="multi_manager_list">Aktif Yönetici</string>
|
||||
<string name="no_active_manager">Aktif yönetici yok</string>
|
||||
<string name="default_signature">SukiSU</string>
|
||||
<string name="home_zygisk_implement">Zygisk uygulaması</string>
|
||||
<!-- 循环路径相关 -->
|
||||
<string name="susfs_tab_sus_loop_paths">SUS Döngü Yolları</string>
|
||||
<string name="susfs_add_sus_loop_path">SUS Döngü Yolu Ekle</string>
|
||||
<string name="susfs_edit_sus_loop_path">SUS Döngü Yolunu Düzenle</string>
|
||||
<string name="susfs_loop_path_added_success">SUS döngü yolu başarıyla eklendi: %1$s</string>
|
||||
<string name="susfs_loop_path_removed">SUS döngü yolu kaldırıldı: %1$s</string>
|
||||
<string name="susfs_loop_path_updated">SUS döngü yolu güncellendi: %1$s -> %2$s</string>
|
||||
<string name="susfs_no_loop_paths_configured">Yapılandırılmış SUS döngü yolu yok</string>
|
||||
<string name="susfs_reset_loop_paths_title">Döngü Yollarını Sıfırla</string>
|
||||
<string name="susfs_reset_loop_paths_message">Tüm SUS döngü yollarını temizlemek istediğinizden emin misiniz? Bu işlem geri alınamaz.</string>
|
||||
<string name="susfs_loop_path_label">Döngü Yolu</string>
|
||||
<string name="susfs_loop_path_placeholder">/data/ornek/yol</string>
|
||||
<string name="susfs_loop_path_restriction_warning">Not: Döngü yolları aracılığıyla yalnızca /storage/ ve /sdcard/ içinde OLMAYAN yollar eklenebilir.</string>
|
||||
<string name="susfs_loop_path_invalid_location">Hata: Döngü yolları /storage/ veya /sdcard/ dizinleri içinde olamaz</string>
|
||||
<string name="loop_paths_section">Döngü Yolları</string>
|
||||
<string name="add_loop_path">Döngü Yolu Ekle</string>
|
||||
<!-- 循环路径功能描述 -->
|
||||
<string name="sus_loop_path_feature_label">SUS Döngü Yolu</string>
|
||||
<string name="sus_loop_paths_description_title">Döngü Yolu Yapılandırması</string>
|
||||
<string name="sus_loop_paths_description_text">Döngü yolları, her kök olmayan (non-root) kullanıcı uygulaması veya yalıtılmış hizmet başlangıcında SUS_PATH olarak yeniden işaretlenir. Bu, eklenen yolların inode durumunun sıfırlanması veya çekirdekte yeniden oluşturulması gibi sorunları gidermeye yardımcı olur.</string>
|
||||
<string name="avc_log_spoofing">AVC Günlük Kaydı Taklidi</string>
|
||||
<string name="avc_log_spoofing_enabled">AVC günlük kaydı taklidi etkinleştirildi</string>
|
||||
<string name="avc_log_spoofing_disabled">AVC günlük kaydı taklidi devre dışı bırakıldı</string>
|
||||
<string name="avc_log_spoofing_description">devre dışı: Çekirdekteki AVC günlük kaydında, \'su\' komutuna ait tcontext\'in taklit edilmesini devre dışı bırakır.\n
|
||||
etkin: Çekirdekteki AVC günlük kaydında, \'su\' komutuna ait tcontext\'i \'kernel\' olarak taklit etmeyi etkinleştirir.</string>
|
||||
<string name="avc_log_spoofing_warning">Önemli Not:\n
|
||||
- Çekirdekte varsayılan olarak \'0\' değerine ayarlıdır.\n
|
||||
- Bu özelliği etkinleştirmek, geliştiricilerin bir izin veya SELinux sorunu için hata ayıklaması yaparken sorunun kaynağını bulmalarını zorlaştırabilir. Bu nedenle, bu tür işlemler sırasında özelliğin devre dışı bırakılması tavsiye edilir.</string>
|
||||
<!-- 模块签名功能描述 -->
|
||||
<string name="module_verified">Doğrulandı</string>
|
||||
<string name="module_signature_verified">Modül imzası doğrulandı</string>
|
||||
<string name="module_signature_verification">İmza Doğrulaması</string>
|
||||
<string name="module_signature_verification_summary">Modüller yüklendiğinde imza doğrulamasını zorunlu kıl. (Sadece ARM mimarisi için geçerlidir)</string>
|
||||
<string name="module_signature_invalid">Bilinmeyen yayıncı</string>
|
||||
<string name="module_signature_invalid_message">İmzasız modüller eksik veya değiştirilmiş olabilir. Cihazınızı korumak için bu modülün kurulumu engellenmiştir.</string>
|
||||
<string name="module_signature_verification_failed">İmzasız modüller eksik veya değiştirilmiş olabilir. Bilinmeyen bir yayıncıdan gelen aşağıdaki modülün bu cihaza kurulmasına izin vermek istiyor musunuz?</string>
|
||||
</resources>
|
||||
|
||||
@@ -539,8 +539,8 @@
|
||||
<string name="susfs_backup_info_date">Дата резервної копії: %s</string>
|
||||
<string name="susfs_backup_info_device">Пристрій: %s</string>
|
||||
<string name="susfs_backup_info_version">Версія: %s</string>
|
||||
<string name="hide_bl_script">Приховати скрипт BL</string>
|
||||
<string name="hide_bl_script_description">Увімкнути скрипти для приховування статусу розблокування завантажувача</string>
|
||||
<string name="hide_bl_script">Заблокированное состояние</string>
|
||||
<string name="hide_bl_script_description">Переопределить свойство состояния блокировки загрузчика в режиме службы late_start</string>
|
||||
<string name="cleanup_residue">Очистити залишки</string>
|
||||
<string name="cleanup_residue_description">Очищення залишкових файлів та каталогів різних модулів та інструментів (може призвести до випадкового видалення, втрати даних та неможливості завантаження, використовуйте з обережністю)</string>
|
||||
</resources>
|
||||
|
||||
@@ -42,15 +42,15 @@
|
||||
<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 logs</string>
|
||||
<string name="safe_mode">CHẾ ĐỘ AN TOÀN</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 module không khả dụng do xung đột với Magisk!</string>
|
||||
<string name="home_learn_kernelsu">Tìm hiểu về 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 Module!</string>
|
||||
<string name="home_support_title">Hỗ trợ chúng tôi</string>
|
||||
<string name="home_support_title">Ủng hộ 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[Xem mã nguồn tại %1$s<br/>Tham gia kênh %2$s của chúng tôi]]></string>
|
||||
<string name="about_source_code"><![CDATA[Xem mã nguồn tại %1$s<br/>Tham gia kênh %2$s của chúng tôi<br/><br/>Hình ảnh của các tệp có nhãn dán nhân vật anime thuộc bản quyền của %3$s, Quyền sở hữu trí tuệ thương hiệu trong hình ảnh thuộc về %4$s. Trước khi sử dụng các tệp này, ngoài việc tuân thủ %5$s, bạn cũng cần tuân thủ sự cho phép của hai tác giả để sử dụng các nội dung nghệ thuật này]]></string>
|
||||
<string name="profile_default">Mặc định</string>
|
||||
<string name="profile_template">Bản mẫu</string>
|
||||
<string name="profile_custom">Tuỳ chỉnh</string>
|
||||
@@ -63,7 +63,7 @@
|
||||
<string name="require_kernel_version" formatted="false">Phiên bản SukiSU Ultra hiện tại %s quá thấp để trình quản lý hoạt động bình thường. Vui lòng cập nhật lên phiên bản %s hoặc cao hơn!</string>
|
||||
<string name="settings_umount_modules_default">Umount modules</string>
|
||||
<string name="settings_umount_modules_default_summary">Giá trị mặc định chung cho \"Umount modules\" trong Hồ sơ ứng dụng. Nếu được bật, mọi thay đổi hệ thống do các module gây ra sẽ bị gỡ bỏ khỏi hệ thống và các ứng dụng chưa thiết lập hồ sơ</string>
|
||||
<string name="settings_susfs_toggle">Vô hiệu hóa kprobes hook</string>
|
||||
<string name="settings_susfs_toggle">Vô hiệu hoá kprobes hook</string>
|
||||
<string name="profile_umount_modules_summary">Bật tùy chọn này sẽ cho phép SukiSU Ultra khôi phục mọi file đã được các module sửa đổi trong ứng dụng này</string>
|
||||
<string name="profile_selinux_domain">Tên miền</string>
|
||||
<string name="profile_selinux_rules">Quy tắc</string>
|
||||
@@ -83,7 +83,7 @@
|
||||
<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_description">Mô 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>
|
||||
@@ -131,7 +131,7 @@
|
||||
<string name="module_install_confirm">Xác nhận cài đặt module %1$s?</string>
|
||||
<string name="unknown_module">Module không xác định</string>
|
||||
<!-- Restore related -->
|
||||
<string name="restore_confirm_title">Xác Nhận Khôi Phục Module</string>
|
||||
<string name="restore_confirm_title">Xác nhận khôi phục module</string>
|
||||
<string name="restore_confirm_message">Hành động này sẽ ghi đè lên tất cả các module hiện có. Tiếp tục?</string>
|
||||
<string name="confirm">Xác nhận</string>
|
||||
<string name="cancel">Thoát</string>
|
||||
@@ -146,7 +146,7 @@
|
||||
<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 thất bại: %1$s</string>
|
||||
<string name="command_execution_failed">Thực thi lệnh thất bại: %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 thất bại: %1$s</string>
|
||||
@@ -161,7 +161,7 @@
|
||||
<string name="settings_card_alpha">Độ trong suốt của thanh điều hướng</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 quyền SU cho %s</string>
|
||||
<string name="su_not_allowed">Không thể cấp quyền Superuser cho %s</string>
|
||||
<string name="settings_disable_su">Vô hiệu hoá lệnh SU</string>
|
||||
<string name="settings_disable_su_summary">Vô hiệu hoá khả năng thực thi lệnh SU để lấy quyền root (Những app đã cấp trước đó không bị ảnh hưởng)</string>
|
||||
<string name="module_install_multiple_confirm_with_names">Bạn có chắc muốn cài đặt các module %1$d không? \n\n%2$s</string>
|
||||
@@ -177,6 +177,8 @@
|
||||
<string name="hide_other_info_summary">Ẩn thông tin về số lượng ở các mục Superuser, Module và KPModule trên thanh điều hướng</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 ở trang chủ</string>
|
||||
<string name="hide_zygisk_implement">Ẩn trạng thái Zygisk</string>
|
||||
<string name="hide_zygisk_implement_summary">Ẩn thông tin triển khai Zygisk trên trang chủ</string>
|
||||
<string name="hide_link_card">Ẩn trạng thái thẻ liên kết</string>
|
||||
<string name="hide_link_card_summary">Ẩn thông tin thẻ liên kết ở trang chủ</string>
|
||||
<string name="hide_tag_card">Ẩn các nhãn module</string>
|
||||
@@ -185,7 +187,7 @@
|
||||
<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="manual_hook">Hook thủ công</string>
|
||||
<string name="dynamic_color_title">Màu sắc động</string>
|
||||
<string name="dynamic_color_summary">Sử dụng màu sắc động làm chủ đề hệ thống</string>
|
||||
<string name="choose_theme_color">Chọn màu chủ đề</string>
|
||||
@@ -218,7 +220,7 @@
|
||||
<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 Kernel Module sau đây được KernelPatch phát triển và sửa đổi để tương thích với các chức năng Kernel Module của SukiSU Ultra</string>
|
||||
<string name="home_ContributionCard_kernelsu">SukiSU Ultra mong đợi</string>
|
||||
<string name="home_ContributionCard_kernelsu">Tương lai của SukiSU Ultra</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 chúng tôi xin cảm ơn KernelSU và MKSU,... vì những đóng góp của họ!</string>
|
||||
@@ -236,8 +238,8 @@
|
||||
<string name="invalid_file_type">Loại file không đúng! Vui lòng chọn file .kpm</string>
|
||||
<string name="confirm_uninstall_title_with_filename">Gỡ cài đặt</string>
|
||||
<string name="confirm_uninstall_content">KPM sau đây sẽ được gỡ cài đặt: %s</string>
|
||||
<string name="settings_susfs_toggle_summary">Vô hiệu hóa kprobes hook được tạo bởi SukiSU Ultra, thay vào đó sử dụng non-kprobes hook được nhúng, tương tự như phương pháp hook của Kernel non-GKI</string>
|
||||
<string name="image_editor_hint">Sử dụng hai ngón tay để phóng to hình ảnh và một ngón tay kéo nó để điều chỉnh vị trí</string>
|
||||
<string name="settings_susfs_toggle_summary">Vô hiệu hoá kprobes hook được tạo bởi SukiSU Ultra, thay vào đó sử dụng inlines hook, tương tự như phương pháp hook của Kernel non-GKI</string>
|
||||
<string name="image_editor_hint">Sử dụng hai ngón tay để phóng to hình ảnh và một ngón tay kéo thả để điều chỉnh vị trí</string>
|
||||
<string name="reprovision">Chọn lại</string>
|
||||
<!-- Kernel Flash Progress Related -->
|
||||
<string name="horizon_flash_title">Kernel Flashing</string>
|
||||
@@ -256,13 +258,13 @@
|
||||
<string name="select_slot_description">Vui lòng chọn Slot để Flash Boot</string>
|
||||
<string name="slot_a">Slot A</string>
|
||||
<string name="slot_b">Slot B</string>
|
||||
<string name="selected_slot">Đã chọn Slot: %1$s</string>
|
||||
<string name="selected_slot">Slot đã chọn: %1$s</string>
|
||||
<string name="horizon_getting_original_slot">Lấy Slot ban đầu</string>
|
||||
<string name="horizon_setting_target_slot">Cài đặt Slot được chỉ định</string>
|
||||
<string name="horizon_restoring_original_slot">Khôi phục Slot mặc định</string>
|
||||
<string name="current_slot">Slot hiện tại: %1$s </string>
|
||||
<!-- Error Messages -->
|
||||
<string name="horizon_copy_failed">Copy thất bại</string>
|
||||
<string name="horizon_copy_failed">Sao chép thất bại</string>
|
||||
<string name="horizon_unknown_error">Lỗi không xác định</string>
|
||||
<string name="flash_failed_message">Flash thất bại</string>
|
||||
<!-- lkm/gki install -->
|
||||
@@ -369,7 +371,7 @@
|
||||
<string name="susfs_reset_confirm_title">Xác nhận khôi phục</string>
|
||||
<!-- SuSFS Toast Messages -->
|
||||
<string name="susfs_binary_not_found">Không tìm thấy file ksu_susfs</string>
|
||||
<string name="susfs_command_failed">Thực hiện lệnh SuSFS thất bại</string>
|
||||
<string name="susfs_command_failed">Thực thi lệnh SuSFS thất bại</string>
|
||||
<string name="susfs_command_error">Lỗi khi thực hiện lệnh SuSFS: %s</string>
|
||||
<string name="susfs_uname_set_success" formatted="false">SuSFS Uname và Thời gian xây dựng được thiết lập thành công: %s, %s</string>
|
||||
<!-- SuSFS Settings Item -->
|
||||
@@ -386,13 +388,13 @@
|
||||
<string name="susfs_tab_basic_settings">Cài đặt cơ bản</string>
|
||||
<string name="susfs_tab_sus_paths">Đường dẫn SuS</string>
|
||||
<string name="susfs_tab_sus_mounts">SuS Mount</string>
|
||||
<string name="susfs_tab_try_umount">SuS Umount</string>
|
||||
<string name="susfs_tab_try_umount">Try Umount</string>
|
||||
<string name="susfs_tab_path_settings">Cài đặt Đường dẫn</string>
|
||||
<string name="susfs_tab_enabled_features">Trạng thái tính năng</string>
|
||||
<!-- SuSFS Path Management -->
|
||||
<string name="susfs_add_sus_path">Thêm Đường dẫn SuS</string>
|
||||
<string name="susfs_add_sus_mount">Thêm SuS Mount</string>
|
||||
<string name="susfs_add_try_umount">Thêm SuS Umount</string>
|
||||
<string name="susfs_add_try_umount">Thêm Try Umount</string>
|
||||
<string name="susfs_sus_path_added_success">Đường dẫn SuS đã được thêm thành công</string>
|
||||
<string name="susfs_path_not_found_error">Lỗi không tìm thấy đường dẫn</string>
|
||||
<string name="susfs_path_label">Đường dẫn</string>
|
||||
@@ -400,7 +402,7 @@
|
||||
<string name="susfs_path_placeholder">Ví dụ: /system/addon.d</string>
|
||||
<string name="susfs_no_paths_configured">Không có Đường dẫn SuS nào được cấu hình</string>
|
||||
<string name="susfs_no_mounts_configured">Không có SuS Mount nào được cấu hình</string>
|
||||
<string name="susfs_no_umounts_configured">Không có SuS Umount nào được cấu hình</string>
|
||||
<string name="susfs_no_umounts_configured">Không có Try Umount nào được cấu hình</string>
|
||||
<!-- SuSFS Umount Mode -->
|
||||
<string name="susfs_umount_mode_label">Chế độ Umount</string>
|
||||
<string name="susfs_umount_mode_normal">Normal Umount (0)</string>
|
||||
@@ -408,18 +410,18 @@
|
||||
<string name="susfs_umount_mode_normal_short">Normal</string>
|
||||
<string name="susfs_umount_mode_detach_short">Detach</string>
|
||||
<string name="susfs_umount_mode_display">Chế độ: %1$s (%2$s)</string>
|
||||
<string name="susfs_try_umount_added_success">Đường dẫn SuS Umount đã thêm thành công: %s</string>
|
||||
<string name="susfs_try_umount_added_saved">Đường dẫn SuS Umount đã lưu thành công: %s</string>
|
||||
<string name="susfs_try_umount_added_success">Đường dẫn Try Umount đã thêm thành công: %s</string>
|
||||
<string name="susfs_try_umount_added_saved">Đường dẫn Try Umount đã lưu thành công: %s</string>
|
||||
<!-- SuSFS Run Umount -->
|
||||
<string name="susfs_run_umount_confirm_title">Xác nhận chạy SuS Umount</string>
|
||||
<string name="susfs_run_umount_confirm_message">Thao tác này sẽ áp dụng ngay lập tức tất cả các thiết lập SuS Umount đã cấu hình. Bạn có chắc chắn muốn tiếp tục không?</string>
|
||||
<string name="susfs_run_umount_confirm_title">Xác nhận chạy Try Umount</string>
|
||||
<string name="susfs_run_umount_confirm_message">Thao tác này sẽ áp dụng ngay lập tức tất cả các thiết lập Try Umount đã cấu hình. Bạn có chắc chắn muốn tiếp tục không?</string>
|
||||
<!-- SuSFS Reset Categories -->
|
||||
<string name="susfs_reset_paths_title">Khôi phục Đường dẫn SuS</string>
|
||||
<string name="susfs_reset_paths_message">Thao tác này sẽ xóa tất cả các cấu hình Đường dẫn SuS. Bạn có chắc chắn muốn tiếp tục không?</string>
|
||||
<string name="susfs_reset_mounts_title">Khôi phục SuS Mount</string>
|
||||
<string name="susfs_reset_mounts_message">Thao tác này sẽ xóa tất cả các cấu hình SuS Mount. Bạn có chắc chắn muốn tiếp tục không?</string>
|
||||
<string name="susfs_reset_umounts_title">Khôi phục SuS Umount</string>
|
||||
<string name="susfs_reset_umounts_message">Thao tác này sẽ xóa tất cả các cấu hình SuS Umount. Bạn có chắc chắn muốn tiếp tục không?</string>
|
||||
<string name="susfs_reset_umounts_title">Khôi phục Try Umount</string>
|
||||
<string name="susfs_reset_umounts_message">Thao tác này sẽ xóa tất cả các cấu hình Try Umount. Bạn có chắc chắn muốn tiếp tục không?</string>
|
||||
<string name="susfs_reset_path_title">Reset Cài đặt Đường dẫn</string>
|
||||
<!-- SuSFS Path Settings -->
|
||||
<string name="susfs_android_data_path_label">Đường dẫn Android Data</string>
|
||||
@@ -434,14 +436,14 @@
|
||||
<!-- Feature Labels -->
|
||||
<string name="sus_path_feature_label">Hỗ trợ Đường dẫn SuS</string>
|
||||
<string name="sus_mount_feature_label">Hỗ trợ SuS Mount</string>
|
||||
<string name="try_umount_feature_label">Hỗ trợ SuS Umount</string>
|
||||
<string name="try_umount_feature_label">Hỗ trợ Try Umount</string>
|
||||
<string name="spoof_uname_feature_label">Hỗ trợ giả mạo Uname</string>
|
||||
<string name="spoof_cmdline_feature_label">Giả mạo Cmdline/Bootconfig</string>
|
||||
<string name="open_redirect_feature_label">Mở hỗ trợ chuyển hướng</string>
|
||||
<string name="enable_log_feature_label">Hỗ trợ ghi logs</string>
|
||||
<string name="auto_default_mount_feature_label">Tự động Mount mặc định</string>
|
||||
<string name="auto_bind_mount_feature_label">Tự động Bind Mount</string>
|
||||
<string name="auto_try_umount_bind_feature_label">Tự động Umount Bind Mount</string>
|
||||
<string name="auto_try_umount_bind_feature_label">Tự động Try Umount Bind Mount</string>
|
||||
<string name="hide_symbols_feature_label">Ẩn biểu tượng KSU SuSFS</string>
|
||||
<string name="magic_mount_feature_label">Hỗ trợ Magic Mount</string>
|
||||
<string name="sus_kstat_feature_label">Hỗ trợ SuS Kstat</string>
|
||||
@@ -458,12 +460,12 @@
|
||||
<!-- Settings related strings -->
|
||||
<string name="show_more_module_info">Hiển thị \"JSON URLs\"</string>
|
||||
<string name="show_more_module_info_summary">Hiển thị thông tin đường dẫn cập nhật \"JSON URLs\" của module</string>
|
||||
<string name="susfs_execution_location_label">Địa điểm thực thi</string>
|
||||
<string name="susfs_execution_location_label">Vị trí thực thi</string>
|
||||
<string name="susfs_current_execution_location">Vị trí thực thi hiện tại: %s</string>
|
||||
<string name="susfs_execution_location_service">Service</string>
|
||||
<string name="susfs_execution_location_post_fs_data">Post-FS-Data</string>
|
||||
<string name="susfs_execution_location_service_description">Thực hiện sau khi dịch vụ hệ thống bắt đầu</string>
|
||||
<string name="susfs_execution_location_post_fs_data_description">Thực hiện sau khi file hệ thống được mount nhưng trước khi hệ thống khởi động hoàn toàn, có thể gây ra boot loop</string>
|
||||
<string name="susfs_execution_location_service_description">Thực thi sau khi dịch vụ hệ thống khởi động</string>
|
||||
<string name="susfs_execution_location_post_fs_data_description">Thực thi sau khi file hệ thống được mount nhưng trước khi hệ thống khởi động hoàn toàn, có thể gây ra boot loop</string>
|
||||
<string name="susfs_slot_info_title">Thông tin Slot</string>
|
||||
<string name="susfs_slot_info_description">Xem thông tin Slot khởi động hiện tại và sao chép giá trị</string>
|
||||
<string name="susfs_current_active_slot">Slot hiện tại: %s</string>
|
||||
@@ -475,7 +477,7 @@
|
||||
<string name="susfs_slot_info_unavailable">Không thể lấy thông tin Slot</string>
|
||||
<!-- SuSFS 自启动相关字符串 -->
|
||||
<string name="susfs_autostart_enabled_success">Module tự động khởi động SuSFS đã bật, đường dẫn module: %s</string>
|
||||
<string name="susfs_autostart_disabled_success">Module tự động khởi động SuSFS đã bị vô hiệu hóa</string>
|
||||
<string name="susfs_autostart_disabled_success">Module tự động khởi động SuSFS đã bị vô hiệu hoá</string>
|
||||
<!-- SuSFS Kstat相关字符串 -->
|
||||
<string name="susfs_tab_kstat_config">Cấu hình Kstat</string>
|
||||
<string name="kstat_static_config_added">Đã thêm cấu hình Kstat tĩnh: %1$s</string>
|
||||
@@ -504,10 +506,10 @@
|
||||
<string name="susfs_hide_mounts_control_description">Kiểm soát hành vi ẩn của SuS Mount với các tiến trình</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_label">Ẩn SuS Mount khỏi tất cả các tiến trình</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_enabled_description">Khi kích hoạt, SuS Mount sẽ bị ẩn khỏi tất cả các tiến trình, bao gồm cả các tiến trình KSU</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_disabled_description">Khi bị vô hiệu hóa, SuS Mount sẽ chỉ bị ẩn khỏi các tiến trình không phải KSU, không bị ẩn ở tiến trình KSU</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_disabled_description">Khi bị vô hiệu hoá, SuS Mount sẽ chỉ bị ẩn khỏi các tiến trình không phải KSU, không bị ẩn ở tiến trình KSU</string>
|
||||
<string name="susfs_hide_mounts_all_enabled">Đã kích hoạt ẩn SuS Mount khỏi tất cả các tiến trình</string>
|
||||
<string name="susfs_hide_mounts_all_disabled">Đã vô hiệu hóa việc ẩn SuS Mount cho tất cả các tiến trình</string>
|
||||
<string name="susfs_hide_mounts_recommendation">Nên vô hiệu hóa sau khi màn hình được mở khóa hoặc trong giai đoạn service.sh hoặc boot-completed.sh, vì điều này sẽ khắc phục sự cố trên một số ứng dụng đã root dựa vào mount bởi tiến trình KSU</string>
|
||||
<string name="susfs_hide_mounts_all_disabled">Đã vô hiệu hoá việc ẩn SuS Mount cho tất cả các tiến trình</string>
|
||||
<string name="susfs_hide_mounts_recommendation">Nên vô hiệu hoá sau khi màn hình được mở khoá hoặc trong giai đoạn service.sh hoặc boot-completed.sh, vì điều này sẽ khắc phục sự cố trên một số ứng dụng đã root dựa vào mount bởi tiến trình KSU</string>
|
||||
<string name="susfs_hide_mounts_current_setting">Cài đặt hiện tại: %s</string>
|
||||
<string name="susfs_hide_mounts_setting_all">Ẩn khỏi tất cả các tiến trình</string>
|
||||
<string name="susfs_hide_mounts_setting_non_ksu">Chỉ ẩn đối với các tiến trình không phải KSU</string>
|
||||
@@ -537,13 +539,13 @@
|
||||
<string name="susfs_backup_info_date">Ngày sao lưu: %s</string>
|
||||
<string name="susfs_backup_info_device">Thiết bị: %s</string>
|
||||
<string name="susfs_backup_info_version">Phiên bản: %s</string>
|
||||
<string name="hide_bl_script">Ẩn Script BL</string>
|
||||
<string name="hide_bl_script_description">Ẩn scripts trạng thái Unlock Bootloader</string>
|
||||
<string name="hide_bl_script">Trạng thái Lock BL</string>
|
||||
<string name="hide_bl_script_description">Ghi đè thuộc tính trạng thái lock bootloader ở chế độ dịch vụ late_start</string>
|
||||
<string name="cleanup_residue">Dọn rác</string>
|
||||
<string name="cleanup_residue_description">Dọn dẹp các file và folder còn sót lại của các module và công cụ (Có thể bị xóa nhầm, dẫn đến mất dữ liệu và không khởi động được)</string>
|
||||
<string name="susfs_edit_sus_path">Chỉnh sửa Đường dẫn SuS</string>
|
||||
<string name="susfs_edit_sus_mount">Chỉnh sửa SuS Mount</string>
|
||||
<string name="susfs_edit_try_umount">Chỉnh sửa SuS Umount</string>
|
||||
<string name="susfs_edit_try_umount">Chỉnh sửa Try Umount</string>
|
||||
<string name="edit_kstat_statically_title">Chỉnh sửa cấu hình Kstat tĩnh</string>
|
||||
<string name="edit_kstat_path_title">Chỉnh sửa Đường dẫn Kstat</string>
|
||||
<string name="susfs_save">Lưu</string>
|
||||
@@ -553,4 +555,72 @@
|
||||
<string name="kstat_config_updated">Cập nhật cấu hình Kstat</string>
|
||||
<string name="kstat_path_updated">Cập nhật Đường dẫn Kstat</string>
|
||||
<string name="susfs_update_full_clone">Cập nhật bản sao SuSFS đầy đủ</string>
|
||||
<string name="umount_zygote_iso_service">Umount dịch vụ cô lập Zygote</string>
|
||||
<string name="umount_zygote_iso_service_description">Umount các điểm dịch vụ cô lập Zygote khi khởi động hệ thống</string>
|
||||
<string name="umount_zygote_iso_service_enabled">Umount dịch vụ cô lập Zygote đã bật</string>
|
||||
<string name="umount_zygote_iso_service_disabled">Umount dịch vụ cô lập Zygote đã tắt</string>
|
||||
<string name="app_paths_section">Đường dẫn ứng dụng</string>
|
||||
<string name="other_paths_section">Đường dẫn khác</string>
|
||||
<string name="add_custom_path">Khác</string>
|
||||
<string name="add_app_path">Ứng dụng</string>
|
||||
<string name="susfs_add_app_path">Thêm Đường dẫn ứng dụng</string>
|
||||
<string name="search_apps">Tìm kiếm ứng dụng</string>
|
||||
<string name="selected_apps_count">%1$d ứng dụng đã chọn</string>
|
||||
<string name="already_added_apps_count">%1$d ứng dụng đã thêm</string>
|
||||
<string name="all_apps_already_added">Tất cả các ứng dụng đã được thêm vào</string>
|
||||
<string name="dynamic_manager_title">Cấu hình chữ ký động</string>
|
||||
<string name="dynamic_manager_enabled_summary">Đã kích hoạt (Size: %s)</string>
|
||||
<string name="dynamic_manager_disabled">Đã vô hiệu hoá</string>
|
||||
<string name="enable_dynamic_manager">Kích hoạt chữ ký động</string>
|
||||
<string name="signature_size">Size chữ ký</string>
|
||||
<string name="signature_hash">Hash chữ ký</string>
|
||||
<string name="hash_must_be_64_chars">Hash phải dài 64 ký tự thập lục phân</string>
|
||||
<string name="dynamic_manager_set_success">Cấu hình chữ ký động đã được thiết lập thành công</string>
|
||||
<string name="dynamic_manager_set_failed">Thiết lập cấu hình chữ ký động thất bại</string>
|
||||
<string name="invalid_sign_config">Cấu hình chữ ký không hợp lệ</string>
|
||||
<string name="dynamic_manager_disabled_success">Chữ ký động đã bị vô hiệu hoá</string>
|
||||
<string name="dynamic_manager_clear_failed">Xoá chữ ký động thất bại</string>
|
||||
<string name="dynamic_managerature">Chữ ký động</string>
|
||||
<string name="signature_index">Chữ ký %1$d</string>
|
||||
<string name="unknown_signature">Không xác định</string>
|
||||
<string name="multi_manager_list">Trình quản lý đang hoạt động</string>
|
||||
<string name="no_active_manager">Trình quản lý đang không hoạt động</string>
|
||||
<string name="default_signature">SukiSU</string>
|
||||
<string name="home_zygisk_implement">Triển khai Zygisk</string>
|
||||
<!-- 循环路径相关 -->
|
||||
<string name="susfs_tab_sus_loop_paths">Đường dẫn Vòng lặp SuS</string>
|
||||
<string name="susfs_add_sus_loop_path">Thêm Đường dẫn Vòng lặp SuS</string>
|
||||
<string name="susfs_edit_sus_loop_path">Chỉnh sửa Đường dẫn Vòng lặp SuS</string>
|
||||
<string name="susfs_loop_path_added_success">Đường dẫn Vòng lặp SuS đã thêm thành công: %1$s</string>
|
||||
<string name="susfs_loop_path_removed">Đã xoá Đường dẫn Vòng lặp SuS: %1$s</string>
|
||||
<string name="susfs_loop_path_updated">Đã cập nhật Đường dẫn Vòng lặp SuS: %1$s -> %2$s</string>
|
||||
<string name="susfs_no_loop_paths_configured">Không có Đường dẫn Vòng lặp SuS nào được cấu hình</string>
|
||||
<string name="susfs_reset_loop_paths_title">Khôi phục Đường dẫn Vòng lặp SuS</string>
|
||||
<string name="susfs_reset_loop_paths_message">Bạn có chắc chắn muốn xóa tất cả các Đường dẫn Vòng lặp SuS không? Thao tác này không thể hoàn tác</string>
|
||||
<string name="susfs_loop_path_label">Đường dẫn Vòng lặp</string>
|
||||
<string name="susfs_loop_path_placeholder">/data/example/path</string>
|
||||
<string name="susfs_loop_path_restriction_warning">Lưu ý: Chỉ những đường dẫn KHÔNG nằm trong /storage/ và /sdcard/ mới có thể được thêm vào thông qua Đường dẫn Vòng lặp</string>
|
||||
<string name="susfs_loop_path_invalid_location">Lỗi: Đường dẫn Vòng lặp không thể nằm trong thư mục /storage/ hoặc /sdcard/</string>
|
||||
<string name="loop_paths_section">Đường dẫn Vòng lặp</string>
|
||||
<string name="add_loop_path">Thêm Đường dẫn Vòng lặp</string>
|
||||
<!-- 循环路径功能描述 -->
|
||||
<string name="sus_loop_path_feature_label">Đường dẫn Vòng lặp SuS</string>
|
||||
<string name="sus_loop_paths_description_title">Cấu hình Đường dẫn Vòng lặp</string>
|
||||
<string name="sus_loop_paths_description_text">Đường dẫn Vòng lặp được đổi tên thành SUS_PATH mỗi khi một ứng dụng không phải root hoặc dịch vụ cô lập được khởi động. Điều này giúp giải quyết vấn đề đường dẫn đã thêm có thể trở nên không hợp lệ do trạng thái inode được đặt lại hoặc inode được tạo lại trong Kernel</string>
|
||||
<string name="avc_log_spoofing">Giả mạo nhật ký AVC</string>
|
||||
<string name="avc_log_spoofing_enabled">Giả mạo nhật ký AVC đã kích hoạt</string>
|
||||
<string name="avc_log_spoofing_disabled">Giả mạo nhật ký AVC đã bị vô hiệu hoá</string>
|
||||
<string name="avc_log_spoofing_description">Tắt: Vô hiệu hoá tính năng giả mạo sus tcontext của \'su\' trong nhật ký AVC của kernel\n
|
||||
Bật: Kích hoạt tính năng giả mạo sus tcontext của \'su\' thành \'kernel\' trong nhật ký AVC của kernel</string>
|
||||
<string name="avc_log_spoofing_warning">Lưu ý quan trọng:\n
|
||||
- Giá trị này được đặt thành \'0\' theo mặc định trong kernel\n
|
||||
- Việc bật tính năng này đôi khi có thể khiến các nhà phát triển khó xác định nguyên nhân của các vấn đề về quyền hoặc SELinux khi gỡ lỗi, vì vậy người dùng nên tắt tính năng này khi gỡ lỗi</string>
|
||||
<!-- 模块签名功能描述 -->
|
||||
<string name="module_verified">Đã xác minh</string>
|
||||
<string name="module_signature_verified">Chữ ký module đã được xác minh</string>
|
||||
<string name="module_signature_verification">Xác minh chữ ký</string>
|
||||
<string name="module_signature_verification_summary">Buộc xác minh chữ ký khi cài đặt module (Chỉ khả dụng cho kiến trúc ARM)</string>
|
||||
<string name="module_signature_invalid">Tác giả không xác định</string>
|
||||
<string name="module_signature_invalid_message">Các module chưa được ký có thể chưa hoàn chỉnh. Để bảo vệ thiết bị của bạn, module này đã bị chặn cài đặt</string>
|
||||
<string name="module_signature_verification_failed">Các module chưa được ký có thể chưa hoàn chỉnh. Bạn có muốn cài đặt module này từ một tác giả chưa xác định không?</string>
|
||||
</resources>
|
||||
|
||||
@@ -50,7 +50,7 @@
|
||||
<string name="home_click_to_learn_kernelsu">了解如何安装 KernelSU 以及如何开发模块</string>
|
||||
<string name="home_support_title">支持开发</string>
|
||||
<string name="home_support_content">KernelSU 将保持免费开源,向开发者捐赠以表示支持。</string>
|
||||
<string name="about_source_code"><![CDATA[在 %1$s 查看源码<br/>加入我们的 %2$s 频道]]></string>
|
||||
<string name="about_source_code"><![CDATA[在 %1$s 查看源码<br/>加入我们的 %2$s 频道<br/><br/>有动漫人物图片表情包的图像版权为%3$s所有,图像中的知识产权由%4$s 所有。在使用这些文件之前,除了必须遵守 %5$s 以外,还需要遵守向前两者索要使用这些艺术内容的授权。]]></string>
|
||||
<string name="profile_default">默认</string>
|
||||
<string name="profile_template">模版</string>
|
||||
<string name="profile_custom">自定义</string>
|
||||
@@ -111,7 +111,7 @@
|
||||
<string name="install_inactive_slot_warning">将在重启后强制切换到另一个槽位!\n注意只能在 OTA 更新完成后的重启之前使用。\n确认?</string>
|
||||
<string name="install_next">下一步</string>
|
||||
<string name="select_file_tip">建议选择 %1$s 分区镜像</string>
|
||||
<string name="select_file_tip_vendor">(实验性的)</string>
|
||||
<string name="select_file_tip_vendor">(实验性的)</string>
|
||||
<string name="select_kmi">选择 KMI</string>
|
||||
<string name="settings_uninstall">卸载</string>
|
||||
<string name="settings_uninstall_temporary">临时卸载</string>
|
||||
@@ -177,6 +177,8 @@
|
||||
<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="hide_zygisk_implement">隐藏 Zygisk 状态信息</string>
|
||||
<string name="hide_zygisk_implement_summary">隐藏主页上的 Zygisk 实现状态信息</string>
|
||||
<string name="hide_link_card">隐藏链接卡片</string>
|
||||
<string name="hide_link_card_summary">隐藏主页上的链接卡片信息</string>
|
||||
<string name="hide_tag_card">隐藏模块标签行</string>
|
||||
@@ -331,8 +333,8 @@
|
||||
<!-- 排序相关 -->
|
||||
<string name="sort_name_asc">名称升序</string>
|
||||
<string name="sort_name_desc">名称降序</string>
|
||||
<string name="sort_install_time_new">安装时间(新)</string>
|
||||
<string name="sort_install_time_old">安装时间(旧)</string>
|
||||
<string name="sort_install_time_new">安装时间(新)</string>
|
||||
<string name="sort_install_time_old">安装时间(旧)</string>
|
||||
<string name="sort_size_desc">大小降序</string>
|
||||
<string name="sort_size_asc">大小升序</string>
|
||||
<string name="sort_usage_freq">使用频率</string>
|
||||
@@ -384,27 +386,27 @@
|
||||
<string name="susfs_no_config_to_autostart">没有可用的配置进行开机自启动</string>
|
||||
<!-- SuSFS Tab Titles -->
|
||||
<string name="susfs_tab_basic_settings">基本设置</string>
|
||||
<string name="susfs_tab_sus_paths">SUS路径</string>
|
||||
<string name="susfs_tab_sus_mounts">SUS挂载</string>
|
||||
<string name="susfs_tab_sus_paths">SuS 路径</string>
|
||||
<string name="susfs_tab_sus_mounts">SuS 挂载</string>
|
||||
<string name="susfs_tab_try_umount">尝试卸载</string>
|
||||
<string name="susfs_tab_path_settings">路径设置</string>
|
||||
<string name="susfs_tab_enabled_features">启用功能状态</string>
|
||||
<!-- SuSFS Path Management -->
|
||||
<string name="susfs_add_sus_path">添加SUS路径</string>
|
||||
<string name="susfs_add_sus_mount">添加SUS挂载</string>
|
||||
<string name="susfs_add_sus_path">添加 SuS 路径</string>
|
||||
<string name="susfs_add_sus_mount">添加 SuS 挂载</string>
|
||||
<string name="susfs_add_try_umount">添加尝试卸载</string>
|
||||
<string name="susfs_sus_path_added_success">SUS 路径添加成功</string>
|
||||
<string name="susfs_sus_path_added_success">SuS 路径添加成功</string>
|
||||
<string name="susfs_path_not_found_error">路径未找到错误</string>
|
||||
<string name="susfs_path_label">路径</string>
|
||||
<string name="susfs_mount_path_label">挂载路径</string>
|
||||
<string name="susfs_path_placeholder">例如: /system/addon.d</string>
|
||||
<string name="susfs_no_paths_configured">暂无 SUS 路径配置</string>
|
||||
<string name="susfs_no_mounts_configured">暂无 SUS 挂载配置</string>
|
||||
<string name="susfs_no_paths_configured">暂无 SuS 路径配置</string>
|
||||
<string name="susfs_no_mounts_configured">暂无 SuS 挂载配置</string>
|
||||
<string name="susfs_no_umounts_configured">暂无尝试卸载配置</string>
|
||||
<!-- SuSFS Umount Mode -->
|
||||
<string name="susfs_umount_mode_label">卸载模式</string>
|
||||
<string name="susfs_umount_mode_normal">普通卸载 (0)</string>
|
||||
<string name="susfs_umount_mode_detach">分离卸载 (1)</string>
|
||||
<string name="susfs_umount_mode_normal">普通卸载(0)</string>
|
||||
<string name="susfs_umount_mode_detach">分离卸载(1)</string>
|
||||
<string name="susfs_umount_mode_normal_short">普通</string>
|
||||
<string name="susfs_umount_mode_detach_short">分离</string>
|
||||
<string name="susfs_umount_mode_display">模式: %1$s (%2$s)</string>
|
||||
@@ -414,26 +416,26 @@
|
||||
<string name="susfs_run_umount_confirm_title">确认运行尝试卸载</string>
|
||||
<string name="susfs_run_umount_confirm_message">这将立即执行所有已配置的尝试卸载操作,确定要继续吗?</string>
|
||||
<!-- SuSFS Reset Categories -->
|
||||
<string name="susfs_reset_paths_title">重置 SUS 路径</string>
|
||||
<string name="susfs_reset_paths_message">这将清除所有 SUS 路径配置,确定要继续吗?</string>
|
||||
<string name="susfs_reset_mounts_title">重置 SUS 挂载</string>
|
||||
<string name="susfs_reset_mounts_message">这将清除所有 SUS 挂载配置,确定要继续吗?</string>
|
||||
<string name="susfs_reset_paths_title">重置 SuS 路径</string>
|
||||
<string name="susfs_reset_paths_message">这将清除所有 SuS 路径配置,确定要继续吗?</string>
|
||||
<string name="susfs_reset_mounts_title">重置 SuS 挂载</string>
|
||||
<string name="susfs_reset_mounts_message">这将清除所有 SuS 挂载配置,确定要继续吗?</string>
|
||||
<string name="susfs_reset_umounts_title">重置尝试卸载</string>
|
||||
<string name="susfs_reset_umounts_message">这将清除所有尝试卸载配置,确定要继续吗?</string>
|
||||
<string name="susfs_reset_path_title">重置路径设置</string>
|
||||
<!-- SuSFS Path Settings -->
|
||||
<string name="susfs_android_data_path_label">Android Data 路径</string>
|
||||
<string name="susfs_sdcard_path_label">SD 卡路径</string>
|
||||
<string name="susfs_sdcard_path_label">SDCard 路径</string>
|
||||
<string name="susfs_set_android_data_path">设置 Android Data 路径</string>
|
||||
<string name="susfs_set_sdcard_path">设置 SD 卡路径</string>
|
||||
<string name="susfs_set_sdcard_path">设置 SDCard 路径</string>
|
||||
<!-- SuSFS Enabled Features -->
|
||||
<string name="susfs_enabled_features_description">显示当前 SuSFS 启用的功能状态</string>
|
||||
<string name="susfs_no_features_found">未找到功能状态信息</string>
|
||||
<string name="susfs_feature_enabled">已启用</string>
|
||||
<string name="susfs_feature_disabled">已禁用</string>
|
||||
<!-- Feature Labels -->
|
||||
<string name="sus_path_feature_label">SUS 路径支持</string>
|
||||
<string name="sus_mount_feature_label">SUS 挂载支持</string>
|
||||
<string name="sus_path_feature_label">SuS 路径支持</string>
|
||||
<string name="sus_mount_feature_label">SuS 挂载支持</string>
|
||||
<string name="try_umount_feature_label">尝试卸载支持</string>
|
||||
<string name="spoof_uname_feature_label">欺骗 uname 支持</string>
|
||||
<string name="spoof_cmdline_feature_label">欺骗 Cmdline/Bootconfig</string>
|
||||
@@ -442,10 +444,10 @@
|
||||
<string name="auto_default_mount_feature_label">自动默认挂载</string>
|
||||
<string name="auto_bind_mount_feature_label">自动绑定挂载</string>
|
||||
<string name="auto_try_umount_bind_feature_label">自动尝试卸载绑定挂载</string>
|
||||
<string name="hide_symbols_feature_label">隐藏 KSU SUSFS 符号</string>
|
||||
<string name="hide_symbols_feature_label">隐藏 KSU SuSFS 符号</string>
|
||||
<string name="magic_mount_feature_label">魔法坐骑支持</string>
|
||||
<string name="sus_kstat_feature_label">SUS Kstat 支持</string>
|
||||
<string name="sus_su_feature_label">SUS SU 模式切换功能</string>
|
||||
<string name="sus_kstat_feature_label">SuS Kstat 支持</string>
|
||||
<string name="sus_su_feature_label">SuS SU 模式切换功能</string>
|
||||
<!-- 可切换状态 -->
|
||||
<string name="susfs_feature_configurable">可配置的 SuSFS 功能</string>
|
||||
<string name="susfs_enable_log_label">SuSFS 启用日志</string>
|
||||
@@ -500,13 +502,13 @@
|
||||
<string name="kstat_path_management">Kstat 路径管理</string>
|
||||
<string name="no_kstat_config_message">暂无 Kstat 配置,点击下方按钮添加配置</string>
|
||||
<!-- SuSFS Mount Hiding Control Related Strings -->
|
||||
<string name="susfs_hide_mounts_control_title">SUS挂载隐藏控制</string>
|
||||
<string name="susfs_hide_mounts_control_description">控制SUS挂载对进程的隐藏行为</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_label">对所有进程隐藏SUS挂载</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_enabled_description">启用后,SUS挂载将对所有进程隐藏,包括KSU进程</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_disabled_description">禁用后,SUS挂载仅对非KSU进程隐藏,KSU进程可以看到挂载</string>
|
||||
<string name="susfs_hide_mounts_all_enabled">已启用对所有进程隐藏SUS挂载</string>
|
||||
<string name="susfs_hide_mounts_all_disabled">已禁用对所有进程隐藏SUS挂载</string>
|
||||
<string name="susfs_hide_mounts_control_title">SuS 挂载隐藏控制</string>
|
||||
<string name="susfs_hide_mounts_control_description">控制 SuS 挂载对进程的隐藏行为</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_label">对所有进程隐藏 SuS 挂载</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_enabled_description">启用后,SuS 挂载将对所有进程隐藏,包括 KSU 进程</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_disabled_description">禁用后,SuS 挂载仅对非 KSU 进程隐藏,KSU 进程可以看到挂载</string>
|
||||
<string name="susfs_hide_mounts_all_enabled">已启用对所有进程隐藏 SuS 挂载</string>
|
||||
<string name="susfs_hide_mounts_all_disabled">已禁用对所有进程隐藏 SuS 挂载</string>
|
||||
<string name="susfs_hide_mounts_recommendation">建议在屏幕解锁后或在 service.sh 或 boot-completed.sh 阶段设置为禁用,这可以修复一些依赖 KSU 进程挂载的 root 应用的问题</string>
|
||||
<string name="susfs_hide_mounts_current_setting">当前设置: %s</string>
|
||||
<string name="susfs_hide_mounts_setting_all">对所有进程隐藏</string>
|
||||
@@ -515,8 +517,8 @@
|
||||
<string name="kernel_simple_kernel">内核版本简洁模式</string>
|
||||
<string name="kernel_simple_kernel_summary">启用或禁用 SukiSU 内核版本显示的简洁模式</string>
|
||||
<string name="susfs_android_data_path_set">Android Data 路径已设置为: %s</string>
|
||||
<string name="susfs_sdcard_path_set">SD卡路径已设置为: %s</string>
|
||||
<string name="susfs_path_setup_warning">路径设置可能未完全成功,但将继续添加SUS路径</string>
|
||||
<string name="susfs_sdcard_path_set">SDCard 路径已设置为: %s</string>
|
||||
<string name="susfs_path_setup_warning">路径设置可能未完全成功,但将继续添加 SuS 路径</string>
|
||||
<!-- 备份和还原相关字符串 -->
|
||||
<string name="susfs_backup_title">备份</string>
|
||||
<string name="susfs_backup_description">创建所有 SuSFS 配置的备份。备份文件将包含所有设置、路径和配置信息。</string>
|
||||
@@ -537,12 +539,12 @@
|
||||
<string name="susfs_backup_info_date">备份日期:%s</string>
|
||||
<string name="susfs_backup_info_device">设备:%s</string>
|
||||
<string name="susfs_backup_info_version">版本:%s</string>
|
||||
<string name="hide_bl_script">隐藏BL脚本</string>
|
||||
<string name="hide_bl_script_description">启用隐藏Bootloader解锁状态脚本</string>
|
||||
<string name="hide_bl_script">上锁状态</string>
|
||||
<string name="hide_bl_script_description">覆盖引导锁状态属性于 late_start 服务模式</string>
|
||||
<string name="cleanup_residue">清理工具残留</string>
|
||||
<string name="cleanup_residue_description">清理各种模块以及工具的残留文件和目录(可能会误删导致丢失以及无法启动,谨慎使用)</string>
|
||||
<string name="susfs_edit_sus_path">编辑 SUS 路径</string>
|
||||
<string name="susfs_edit_sus_mount">编辑 SUS 挂载</string>
|
||||
<string name="cleanup_residue_description">清理各种模块以及工具的残留文件和目录(可能会误删导致丢失以及无法启动,谨慎使用)</string>
|
||||
<string name="susfs_edit_sus_path">编辑 SuS 路径</string>
|
||||
<string name="susfs_edit_sus_mount">编辑 SuS 挂载</string>
|
||||
<string name="susfs_edit_try_umount">编辑尝试卸载</string>
|
||||
<string name="edit_kstat_statically_title">编辑 Kstat 静态配置</string>
|
||||
<string name="edit_kstat_path_title">编辑 Kstat 路径</string>
|
||||
@@ -557,4 +559,67 @@
|
||||
<string name="umount_zygote_iso_service_description">启用此选项将在系统启动时卸载 Zygote 隔离服务挂载点</string>
|
||||
<string name="umount_zygote_iso_service_enabled">Zygote 隔离服务卸载已启用</string>
|
||||
<string name="umount_zygote_iso_service_disabled">Zygote 隔离服务卸载已禁用</string>
|
||||
<string name="app_paths_section">应用路径</string>
|
||||
<string name="other_paths_section">其他路径</string>
|
||||
<string name="add_custom_path">其他</string>
|
||||
<string name="add_app_path">应用</string>
|
||||
<string name="susfs_add_app_path">添加应用路径</string>
|
||||
<string name="search_apps">搜索应用</string>
|
||||
<string name="selected_apps_count">%1$d 个已选应用</string>
|
||||
<string name="already_added_apps_count">%1$d 个已添加应用</string>
|
||||
<string name="all_apps_already_added">所有应用均已添加</string>
|
||||
<string name="dynamic_manager_title">动态管理器配置</string>
|
||||
<string name="dynamic_manager_enabled_summary">已启用(大小: %s)</string>
|
||||
<string name="dynamic_manager_disabled">未启用</string>
|
||||
<string name="enable_dynamic_manager">启用动态管理器</string>
|
||||
<string name="signature_size">动态管理器签名大小</string>
|
||||
<string name="signature_hash">动态管理器签名哈希值</string>
|
||||
<string name="hash_must_be_64_chars">哈希值必须是 64 位十六进制字符</string>
|
||||
<string name="dynamic_manager_set_success">动态管理器配置设置成功</string>
|
||||
<string name="dynamic_manager_set_failed">动态管理器配置设置失败</string>
|
||||
<string name="invalid_sign_config">无效的签名配置</string>
|
||||
<string name="dynamic_manager_disabled_success">动态管理器已禁用</string>
|
||||
<string name="dynamic_manager_clear_failed">清除动态管理器错误</string>
|
||||
<string name="dynamic_managerature">动态</string>
|
||||
<string name="signature_index">签名 %1$d</string>
|
||||
<string name="unknown_signature">未知</string>
|
||||
<string name="multi_manager_list">活跃管理器</string>
|
||||
<string name="no_active_manager">无活跃管理器</string>
|
||||
<string name="home_zygisk_implement">Zygisk 实现</string>
|
||||
<!-- 循环路径相关 -->
|
||||
<string name="susfs_tab_sus_loop_paths">SuS 循环路径</string>
|
||||
<string name="susfs_add_sus_loop_path">添加 SuS 循环路径</string>
|
||||
<string name="susfs_edit_sus_loop_path">编辑 SuS 循环路径</string>
|
||||
<string name="susfs_loop_path_added_success">SuS 循环路径添加成功: %1$s</string>
|
||||
<string name="susfs_loop_path_removed">SuS 循环路径已移除: %1$s</string>
|
||||
<string name="susfs_loop_path_updated">SuS 循环路径已更新: %1$s -> %2$s</string>
|
||||
<string name="susfs_no_loop_paths_configured">未配置 SuS 循环路径</string>
|
||||
<string name="susfs_reset_loop_paths_title">重置循环路径</string>
|
||||
<string name="susfs_reset_loop_paths_message">确定要清空所有 SuS 循环路径吗?此操作无法撤销。</string>
|
||||
<string name="susfs_loop_path_label">循环路径</string>
|
||||
<string name="susfs_loop_path_restriction_warning">注意:只有不在 /storage/ 和 /sdcard/ 内的路径才能通过循环路径添加。</string>
|
||||
<string name="susfs_loop_path_invalid_location">错误:循环路径不能位于 /storage/ 或 /sdcard/ 目录内</string>
|
||||
<string name="loop_paths_section">循环路径</string>
|
||||
<string name="add_loop_path">添加循环路径</string>
|
||||
<!-- 循环路径功能描述 -->
|
||||
<string name="sus_loop_path_feature_label">SuS 循环路径</string>
|
||||
<string name="sus_loop_paths_description_title">循环路径配置</string>
|
||||
<string name="sus_loop_paths_description_text">循环路径会在每次非 root 用户应用或隔离服务启动时重新标记为 SUS_PATH。这有助于解决添加的路径可能因 inode 状态重置或内核中 inode 重新创建而失效的问题</string>
|
||||
<string name="avc_log_spoofing">AVC 日志欺骗</string>
|
||||
<string name="avc_log_spoofing_enabled">AVC 日志欺骗已启用</string>
|
||||
<string name="avc_log_spoofing_disabled">AVC 日志欺骗已禁用</string>
|
||||
<string name="avc_log_spoofing_description">禁用: 禁用在内核 AVC 日志中欺骗 \'su\' 的 sus tcontext。\n
|
||||
启用: 启用在内核 AVC 日志中将 \'su\' 的 sus tcontext 欺骗为 \'kernel\'</string>
|
||||
<string name="avc_log_spoofing_warning">重要提示:\n
|
||||
- 内核中默认设置为 \'0\'\n
|
||||
- 启用此功能有时会使开发人员在调试权限或 SELinux 问题时难以识别原因,因此建议用户在调试时禁用此功能。</string>
|
||||
<!-- 模块签名功能描述 -->
|
||||
<string name="module_verified">已验证</string>
|
||||
<string name="module_signature_verified">模块签名已验证</string>
|
||||
<string name="module_signature_verification">验证签名</string>
|
||||
<string name="module_signature_verification_summary">模块安装时,强制验证签名。(仅 ARM架构 可用)</string>
|
||||
<string name="module_signature_invalid">未知发布者</string>
|
||||
<string name="module_signature_invalid_message">未经签名的模块可能不完整。为了对设备进行保护,已阻止安装此模块。</string>
|
||||
<string name="module_signature_verification_failed">未经签名的模块可能不完整。你想安装来自未知发布者的模块吗?</string>
|
||||
<string name="home_hook_type">钩子类型</string>
|
||||
</resources>
|
||||
|
||||
@@ -25,13 +25,13 @@
|
||||
<string name="uninstall">卸載</string>
|
||||
<string name="module_install">安裝</string>
|
||||
<string name="install">安裝</string>
|
||||
<string name="reboot">重啟</string>
|
||||
<string name="settings">設定</string>
|
||||
<string name="reboot">重新啟動</string>
|
||||
<string name="settings">配置</string>
|
||||
<string name="reboot_userspace">軟重啟</string>
|
||||
<string name="reboot_recovery">重啟到 Recovery</string>
|
||||
<string name="reboot_bootloader">重啟到 Bootloader</string>
|
||||
<string name="reboot_download">重啟到 Download</string>
|
||||
<string name="reboot_edl">重啟到 EDL</string>
|
||||
<string name="reboot_recovery">重新啟動到 Recovery</string>
|
||||
<string name="reboot_bootloader">重新啟動到 Bootloader</string>
|
||||
<string name="reboot_download">重新啟動到 Download</string>
|
||||
<string name="reboot_edl">重新啟動到 EDL</string>
|
||||
<string name="about">關於</string>
|
||||
<string name="module_uninstall_confirm">確定要卸載模組 %s 嗎?</string>
|
||||
<string name="module_uninstall_success">%s 已卸載</string>
|
||||
@@ -43,26 +43,26 @@
|
||||
<string name="hide_system_apps">隱藏系統應用</string>
|
||||
<string name="send_log">發送日誌</string>
|
||||
<string name="safe_mode">安全模式</string>
|
||||
<string name="reboot_to_apply">重啟生效</string>
|
||||
<string name="module_magisk_conflict">因同 Magisk 有衝突,所有模組不可用!</string>
|
||||
<string name="reboot_to_apply">重新啟動後生效</string>
|
||||
<string name="module_magisk_conflict">因同 Magisk 有衝突,所有模組唔可用!</string>
|
||||
<string name="home_learn_kernelsu">了解 KernelSU</string>
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/zh_CN/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">了解如何安裝 KernelSU 以及如何開發模組</string>
|
||||
<string name="home_support_title">支持開發</string>
|
||||
<string name="home_support_content">KernelSU 將保持免費開源,向開發者捐贈以表示支持。</string>
|
||||
<string name="about_source_code"><![CDATA[在 %1$s 查看源碼<br/>加入我哋嘅 %2$s 頻道]]></string>
|
||||
<string name="home_support_title">支援開發</string>
|
||||
<string name="home_support_content">KernelSU 將保持免費開源,向開發者捐贈以表示支援。</string>
|
||||
<string name="about_source_code"><![CDATA[喺 %1$s 查看源碼<br/>加入我哋嘅 %2$s 頻道]]<br/><br/>有動漫人物圖片表情包嘅圖像版權為%3$s所有,圖像中嘅知識產權由%4$s 所有。喺使用這些文件之前,除了必須遵守 %5$s 以外,還需要遵守向前兩者索要使用這些藝術內容嘅授權。]]></string>
|
||||
<string name="profile_default">默認</string>
|
||||
<string name="profile_template">模版</string>
|
||||
<string name="profile_custom">自定義</string>
|
||||
<string name="profile_name">名稱</string>
|
||||
<string name="profile_groups">組</string>
|
||||
<string name="profile_capabilities">權能</string>
|
||||
<string name="profile_capabilities">權限</string>
|
||||
<string name="profile_selinux_context">SELinux</string>
|
||||
<string name="profile_umount_modules">卸載模組</string>
|
||||
<string name="failed_to_update_app_profile">為 %s 更新 App Profile 失敗</string>
|
||||
<string name="require_kernel_version" formatted="false">當前 KernelSU 版本 %s 過低,管理器無法正常工作,請將核心 KernelSU 版本升級至 %s 或以上!</string>
|
||||
<string name="settings_umount_modules_default">默認卸載模組</string>
|
||||
<string name="settings_umount_modules_default_summary">App Profile 中\"卸載模組\"嘅全局默認值,如果啟用,將會為冇設置 Profile 嘅應用移除所有模組針對系統嘅修改。</string>
|
||||
<string name="settings_umount_modules_default_summary">App Profile 中\"卸載模組\"嘅全局默認值,如果啟用,將會為冇設定 Profile 嘅應用移除所有模組針對系統嘅修改。</string>
|
||||
<string name="settings_susfs_toggle">禁用 Kprobe Hook</string>
|
||||
<string name="profile_umount_modules_summary">啟用該選項後將允許 KernelSU 為本應用還原被模組修改過嘅文件。</string>
|
||||
<string name="profile_selinux_domain">域</string>
|
||||
@@ -81,10 +81,10 @@
|
||||
<string name="app_profile_template_create">創建模版</string>
|
||||
<string name="app_profile_template_edit">編輯模版</string>
|
||||
<string name="app_profile_template_id">模版 ID</string>
|
||||
<string name="app_profile_template_id_invalid">模版 ID 不合法</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_save">保存</string>
|
||||
<string name="app_profile_template_save">存儲</string>
|
||||
<string name="app_profile_template_delete">刪除</string>
|
||||
<string name="app_profile_template_view">查看模版</string>
|
||||
<string name="app_profile_template_readonly">只讀</string>
|
||||
@@ -95,20 +95,20 @@
|
||||
<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_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="settings_check_update_summary">喺應用啟動後自動檢查是否有最新版</string>
|
||||
<string name="grant_root_failed">獲取 root 失敗!</string>
|
||||
<string name="action">執行</string>
|
||||
<string name="close">關閉</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="direct_install">直接安裝(推薦)</string>
|
||||
<string name="select_file">選擇一個需要修補嘅鏡像</string>
|
||||
<string name="install_inactive_slot">安裝到未使用嘅槽位(OTA 後)</string>
|
||||
<string name="install_inactive_slot_warning">將在重啟後強制切換到另一個槽位!\n注意只能在 OTA 更新完成後嘅重啟之前使用。\n確認?</string>
|
||||
<string name="install_inactive_slot_warning">將喺重新啟動後強制切換到另一個槽位!\n注意只能喺 OTA 更新完成後嘅重新啟動之前使用。\n確認?</string>
|
||||
<string name="install_next">下一步</string>
|
||||
<string name="select_file_tip">建議選擇 %1$s 分區鏡像</string>
|
||||
<string name="select_kmi">選擇 KMI</string>
|
||||
@@ -116,15 +116,15 @@
|
||||
<string name="settings_uninstall_temporary">臨時卸載</string>
|
||||
<string name="settings_uninstall_permanent">永久卸載</string>
|
||||
<string name="settings_restore_stock_image">恢復原廠鏡像</string>
|
||||
<string name="settings_uninstall_temporary_message">臨時卸載 KernelSU,下次重啟後恢復至原始狀態。</string>
|
||||
<string name="settings_uninstall_temporary_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="flash_success">刷寫完成</string>
|
||||
<string name="flash_failed">刷寫失敗</string>
|
||||
<string name="selected_lkm">選擇嘅 LKM:%s</string>
|
||||
<string name="save_log">保存日誌</string>
|
||||
<string name="log_saved">日誌已保存</string>
|
||||
<string name="save_log">存儲日誌</string>
|
||||
<string name="log_saved">日誌已存儲</string>
|
||||
<string name="sus_su_mode">SuS SU 模式:</string>
|
||||
<!-- Module related -->
|
||||
<string name="module_install_confirm">確認安裝模組 %1$s?</string>
|
||||
@@ -140,9 +140,9 @@
|
||||
<string name="backup_modules">備份模組</string>
|
||||
<string name="restore_modules">恢復模組</string>
|
||||
<!-- Restore related messages -->
|
||||
<string name="restore_success">模組已成功還原,需重啟生效</string>
|
||||
<string name="restore_success">模組已成功還原,需重新啟動生效</string>
|
||||
<string name="restore_failed">還原失敗:%1$s</string>
|
||||
<string name="restart_now">立即重啟</string>
|
||||
<string name="restart_now">立即重新啟動</string>
|
||||
<string name="unknown_error">未知錯誤</string>
|
||||
<!-- Command related -->
|
||||
<string name="command_execution_failed">命令執行失敗:%1$s</string>
|
||||
@@ -157,19 +157,19 @@
|
||||
<string name="restore_allowlist">還原應用列表</string>
|
||||
<string name="settings_custom_background">自定義背景</string>
|
||||
<string name="settings_custom_background_summary">選擇一張圖片作為應用背景</string>
|
||||
<string name="settings_card_alpha">卡片不透明度</string>
|
||||
<string name="settings_card_alpha">卡片唔透明度</string>
|
||||
<string name="home_android_version">Android 版本</string>
|
||||
<string name="home_device_model">設備</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="settings_disable_su_summary">臨時禁止任何應用程式通過 su 命令獲取 Root 權限(現有嘅 Root 進程唔受影響)</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>
|
||||
<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">開啟後將隱藏不必要嘅卡片</string>
|
||||
<string name="simple_mode_summary">開啟後將隱藏冇必要嘅卡片</string>
|
||||
<string name="hide_kernel_kernelsu_version">隱藏核心版本號</string>
|
||||
<string name="hide_kernel_kernelsu_version_summary">隱藏核心部分嘅 KernelSU 版本號</string>
|
||||
<string name="hide_other_info">強迫症開關</string>
|
||||
@@ -199,10 +199,10 @@
|
||||
<string name="horizon_kernel_summary">刷入 Anykernel3 核心</string>
|
||||
<string name="root_required">需要 root 權限</string>
|
||||
<string name="reboot_complete_title">刷寫完成</string>
|
||||
<string name="reboot_complete_msg">是否立即重啟?</string>
|
||||
<string name="yes">系</string>
|
||||
<string name="reboot_complete_msg">是否立即重新啟動?</string>
|
||||
<string name="yes">係</string>
|
||||
<string name="no">否</string>
|
||||
<string name="failed_reboot">重啟失敗</string>
|
||||
<string name="failed_reboot">重新啟動失敗</string>
|
||||
<string name="kpm_title">核心模組</string>
|
||||
<string name="kpm_empty">暫冇已安裝嘅核心模組</string>
|
||||
<string name="kpm_version">版本</string>
|
||||
@@ -220,22 +220,22 @@
|
||||
<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="home_click_to_ContributionCard_kernelsu">SukiSU Ultra 未來將會成為一個相對獨立嘅 KSU 分支,但仲係會感謝官方 KernelSU 同 MKSU 等做出嘅貢獻</string>
|
||||
<string name="not_supported">唔支援</string>
|
||||
<string name="supported">支持</string>
|
||||
<string name="supported">支援</string>
|
||||
<string name="kernel_patched">核心未進行補丁</string>
|
||||
<string name="kernel_not_enabled">核心未配置</string>
|
||||
<string name="custom_settings">個性化設定</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">請選擇: %1\$s 模組嘅安裝模式 \n\n加載:臨時加載模組\n嵌入:永久安裝到系統</string>
|
||||
<string name="snackbar_failed_to_check_module_file">無法檢查模組文件是否存在</string>
|
||||
<string name="theme_color">主題顏色</string>
|
||||
<string name="invalid_file_type">文件類型不正確,請選擇 .kpm 文件</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>
|
||||
<string name="settings_susfs_toggle_summary">禁用由 KernelSU 創建嘅 Kprobe Hook,並使用非 Kprobe 內嘅聯鈎子代替,實現方式類似於不支持 Kprobe 嘅非 GKI 核心。</string>
|
||||
<string name="settings_susfs_toggle_summary">禁用由 KernelSU 創建嘅 Kprobe Hook,並使用非 Kprobe 內嘅聯鈎子代替,實現方式類似於唔支援 Kprobe 嘅非 GKI 核心。</string>
|
||||
<string name="image_editor_hint">使用雙指縮放圖片,單指拖動調整位置</string>
|
||||
<string name="reprovision">重置</string>
|
||||
<!-- Kernel Flash Progress Related -->
|
||||
@@ -257,7 +257,7 @@
|
||||
<string name="slot_b">B 槽位</string>
|
||||
<string name="selected_slot">已選擇槽位: %1$s</string>
|
||||
<string name="horizon_getting_original_slot">獲取原有槽位</string>
|
||||
<string name="horizon_setting_target_slot">設置指定槽位</string>
|
||||
<string name="horizon_setting_target_slot">設定指定槽位</string>
|
||||
<string name="horizon_restoring_original_slot">恢復默認槽位</string>
|
||||
<string name="current_slot">當前系統默認槽位:%1$s </string>
|
||||
<!-- Error Messages -->
|
||||
@@ -270,33 +270,33 @@
|
||||
<string name="kernel_version_log">核心版本:%1$s</string>
|
||||
<string name="tool_version_log">使用修補工具:%1$s</string>
|
||||
<string name="configuration">配置</string>
|
||||
<string name="app_settings">應用設定</string>
|
||||
<string name="app_settings">應用配置</string>
|
||||
<string name="tools">工具</string>
|
||||
<!-- String resources used in SuperUser -->
|
||||
<string name="no_apps_found">未找到應用</string>
|
||||
<string name="selinux_enabled_toast">SELinux 已設置為啟用狀態</string>
|
||||
<string name="selinux_disabled_toast">SELinux 已設置為禁用狀態</string>
|
||||
<string name="selinux_enabled_toast">SELinux 已設定為啟用狀態</string>
|
||||
<string name="selinux_disabled_toast">SELinux 已設定為禁用狀態</string>
|
||||
<string name="selinux_change_failed">SELinux 狀態更改失敗</string>
|
||||
<string name="advanced_settings">高級設定</string>
|
||||
<string name="appearance_settings">外觀設定</string>
|
||||
<string name="advanced_settings">高級配置</string>
|
||||
<string name="appearance_settings">外觀配置</string>
|
||||
<string name="back">返回</string>
|
||||
<string name="susfs_enabled">SuSFS 已啟用</string>
|
||||
<string name="susfs_disabled">SuSFS 已禁用</string>
|
||||
<string name="background_set_success">背景設置成功</string>
|
||||
<string name="background_set_success">背景設定成功</string>
|
||||
<string name="background_removed">已移除自定義背景</string>
|
||||
<string name="icon_switch_title">備選圖標</string>
|
||||
<string name="icon_switch_summary">更換為 KernelSU 圖標</string>
|
||||
<string name="icon_switched">已切換圖標</string>
|
||||
<!-- KPM display settings -->
|
||||
<string name="show_kpm_info">隱藏 KPM 功能</string>
|
||||
<string name="show_kpm_info_summary">在主頁同底欄隱藏 KPM 相關功能同信息</string>
|
||||
<string name="show_kpm_info_summary">喺主頁同底欄隱藏 KPM 相關功能同信息</string>
|
||||
<!-- Webui X settings -->
|
||||
<string name="use_webuix">選擇使用嘅 WebUI 引擎</string>
|
||||
<string name="engine_auto_select">自動選擇</string>
|
||||
<string name="engine_force_webuix">強制使用 WebUI X</string>
|
||||
<string name="engine_force_ksu">強制使用 KSU 嘅 WebUI</string>
|
||||
<string name="use_webuix_eruda">將 Eruda 注入 WebUI X</string>
|
||||
<string name="use_webuix_eruda_summary">在 WebUI X 中注入調試控制台,使調試更容易,需要啟用 WebView 調試</string>
|
||||
<string name="use_webuix_eruda_summary">喺 WebUI X 中注入調試控制台,使調試更容易,需要啟用 WebView 調試</string>
|
||||
<!-- DPI setting related strings -->
|
||||
<string name="app_dpi_title">應用 DPI</string>
|
||||
<string name="app_dpi_summary">僅調整當前應用嘅屏幕顯示密度</string>
|
||||
@@ -305,15 +305,15 @@
|
||||
<string name="dpi_size_large">大</string>
|
||||
<string name="dpi_size_extra_large">超大</string>
|
||||
<string name="dpi_size_custom">自定義</string>
|
||||
<string name="dpi_apply_settings">應用DPI設定</string>
|
||||
<string name="dpi_apply_settings">應用 DPI 配置</string>
|
||||
<string name="dpi_confirm_title">確認更改 DPI</string>
|
||||
<string name="dpi_confirm_message">您確定要將應用 DPI 從 %1$d 更改為 %2$d 嗎?</string>
|
||||
<string name="dpi_confirm_summary">應用需要重啟以應用新嘅DPI設定,不會影響系統狀態欄或其他應用</string>
|
||||
<string name="dpi_applied_success">DPI 已設置為 %1$d,重啟應用後生效</string>
|
||||
<string name="dpi_confirm_summary">應用需要重新啟動以應用新嘅 DPI 配置,唔會影響系統狀態欄或其他應用</string>
|
||||
<string name="dpi_applied_success">DPI 已設定為 %1$d,重新啟動應用後生效</string>
|
||||
<!-- Language settings related strings -->
|
||||
<string name="language_setting">應用語言</string>
|
||||
<string name="language_follow_system">跟隨系統</string>
|
||||
<string name="language_changed">語言已更改,重啟應用以應用更改</string>
|
||||
<string name="language_changed">語言已更改,重新啟動應用以應用更改</string>
|
||||
<string name="settings_card_dim">卡片暗度調節</string>
|
||||
<!-- Flash related -->
|
||||
<string name="error_code">錯誤代碼</string>
|
||||
@@ -355,7 +355,7 @@
|
||||
<!-- SuSFS Configuration -->
|
||||
<string name="susfs_config_title">SuSFS 配置</string>
|
||||
<string name="susfs_config_description">配置說明</string>
|
||||
<string name="susfs_config_description_text">此功能允許您自定義 SuSFS 嘅 uname 值同構建時間偽裝。輸入您想要設置嘅值,點擊應用即可生效</string>
|
||||
<string name="susfs_config_description_text">此功能允許您自定義 SuSFS 嘅 uname 值同構建時間偽裝。輸入您想要設定嘅值,點擊應用即可生效</string>
|
||||
<string name="susfs_uname_label">Uname 值</string>
|
||||
<string name="susfs_uname_placeholder">請輸入自定義 uname 值</string>
|
||||
<string name="susfs_build_time_label">構建時間偽裝</string>
|
||||
@@ -370,35 +370,35 @@
|
||||
<string name="susfs_binary_not_found">無法找到 ksu_susfs 文件</string>
|
||||
<string name="susfs_command_failed">SuSFS 命令執行失敗</string>
|
||||
<string name="susfs_command_error">執行 SuSFS 命令時出錯: %s</string>
|
||||
<string name="susfs_uname_set_success" formatted="false">SuSFS 核心名稱同構建時間設置成功: %s, %s</string>
|
||||
<string name="susfs_uname_set_success" formatted="false">SuSFS 核心名稱同構建時間設定成功: %s, %s</string>
|
||||
<!-- SuSFS Settings Item -->
|
||||
<string name="susfs_config_setting_title">SuSFS 配置</string>
|
||||
<!-- 开机自启动相关 -->
|
||||
<string name="susfs_autostart_title">開機自啟動</string>
|
||||
<string name="susfs_autostart_description">重啟時自動應用所有非默認配置</string>
|
||||
<string name="susfs_autostart_description">重新啟動時自動應用所有非默認配置</string>
|
||||
<string name="susfs_autostart_requirement">需要添加配置後才能啟用</string>
|
||||
<string name="susfs_autostart_enable_failed">啟用開機自啟動失敗</string>
|
||||
<string name="susfs_autostart_disable_failed">禁用開機自啟動失敗</string>
|
||||
<string name="susfs_autostart_error">開機自啟動配置錯誤: %s</string>
|
||||
<string name="susfs_no_config_to_autostart">冇可用嘅配置進行開機自啟動</string>
|
||||
<!-- SuSFS Tab Titles -->
|
||||
<string name="susfs_tab_basic_settings">基本設定</string>
|
||||
<string name="susfs_tab_sus_paths">SUS路徑</string>
|
||||
<string name="susfs_tab_sus_mounts">SUS掛載</string>
|
||||
<string name="susfs_tab_basic_settings">基本配置</string>
|
||||
<string name="susfs_tab_sus_paths">SuS 路徑</string>
|
||||
<string name="susfs_tab_sus_mounts">SuS 掛載</string>
|
||||
<string name="susfs_tab_try_umount">嘗試卸載</string>
|
||||
<string name="susfs_tab_path_settings">路徑設定</string>
|
||||
<string name="susfs_tab_path_settings">路徑配置</string>
|
||||
<string name="susfs_tab_enabled_features">啟用功能狀態</string>
|
||||
<!-- SuSFS Path Management -->
|
||||
<string name="susfs_add_sus_path">添加SUS路徑</string>
|
||||
<string name="susfs_add_sus_mount">添加SUS掛載</string>
|
||||
<string name="susfs_add_sus_path">添加 SuS 路徑</string>
|
||||
<string name="susfs_add_sus_mount">添加 SuS 掛載</string>
|
||||
<string name="susfs_add_try_umount">嘗試添加卸載</string>
|
||||
<string name="susfs_sus_path_added_success">SUS 路徑添加成功</string>
|
||||
<string name="susfs_path_not_found_error">錯誤沒有此找到路徑</string>
|
||||
<string name="susfs_sus_path_added_success">SuS 路徑添加成功</string>
|
||||
<string name="susfs_path_not_found_error">錯誤冇此找到路徑</string>
|
||||
<string name="susfs_path_label">路徑</string>
|
||||
<string name="susfs_mount_path_label">掛載路徑</string>
|
||||
<string name="susfs_path_placeholder">例如: /system/addon.d</string>
|
||||
<string name="susfs_no_paths_configured">暫冇 SUS 路徑配置</string>
|
||||
<string name="susfs_no_mounts_configured">暫冇 SUS 掛載配置</string>
|
||||
<string name="susfs_no_paths_configured">暫冇 SuS 路徑配置</string>
|
||||
<string name="susfs_no_mounts_configured">暫冇 SuS 掛載配置</string>
|
||||
<string name="susfs_no_umounts_configured">暫冇嘗試卸載配置</string>
|
||||
<!-- SuSFS Umount Mode -->
|
||||
<string name="susfs_umount_mode_label">卸載模式</string>
|
||||
@@ -408,43 +408,43 @@
|
||||
<string name="susfs_umount_mode_detach_short">分離</string>
|
||||
<string name="susfs_umount_mode_display">模式: %1$s (%2$s)</string>
|
||||
<string name="susfs_try_umount_added_success">嘗試 umount 路徑添加成功: %s</string>
|
||||
<string name="susfs_try_umount_added_saved">嘗試 umount 路徑保存成功: %s</string>
|
||||
<string name="susfs_try_umount_added_saved">嘗試 umount 路徑存儲成功: %s</string>
|
||||
<!-- SuSFS Run Umount -->
|
||||
<string name="susfs_run_umount_confirm_title">確認運行嘗試卸載</string>
|
||||
<string name="susfs_run_umount_confirm_message">這將立即執行所有已配置嘅嘗試卸載操作,確定要繼續嗎?</string>
|
||||
<!-- SuSFS Reset Categories -->
|
||||
<string name="susfs_reset_paths_title">重置 SUS 路徑</string>
|
||||
<string name="susfs_reset_paths_message">這將清除所有 SUS 路徑配置,確定要繼續嗎?</string>
|
||||
<string name="susfs_reset_mounts_title">重置 SUS 掛載</string>
|
||||
<string name="susfs_reset_mounts_message">這將清除所有 SUS 掛載配置,確定要繼續嗎?</string>
|
||||
<string name="susfs_reset_paths_title">重置 SuS 路徑</string>
|
||||
<string name="susfs_reset_paths_message">這將清除所有 SuS 路徑配置,確定要繼續嗎?</string>
|
||||
<string name="susfs_reset_mounts_title">重置 SuS 掛載</string>
|
||||
<string name="susfs_reset_mounts_message">這將清除所有 SuS 掛載配置,確定要繼續嗎?</string>
|
||||
<string name="susfs_reset_umounts_title">重置嘗試卸載</string>
|
||||
<string name="susfs_reset_umounts_message">這將清除所有嘗試卸載配置,確定要繼續嗎?</string>
|
||||
<string name="susfs_reset_path_title">重置路徑設定</string>
|
||||
<string name="susfs_reset_path_title">重置路徑配置</string>
|
||||
<!-- SuSFS Path Settings -->
|
||||
<string name="susfs_android_data_path_label">Android Data 路徑</string>
|
||||
<string name="susfs_sdcard_path_label">SD 卡路徑</string>
|
||||
<string name="susfs_set_android_data_path">設置 Android Data 路徑</string>
|
||||
<string name="susfs_set_sdcard_path">設置 SD 卡路徑</string>
|
||||
<string name="susfs_set_android_data_path">設定 Android Data 路徑</string>
|
||||
<string name="susfs_set_sdcard_path">設定 SD 卡路徑</string>
|
||||
<!-- SuSFS Enabled Features -->
|
||||
<string name="susfs_enabled_features_description">顯示當前 SuSFS 啟用嘅功能狀態</string>
|
||||
<string name="susfs_no_features_found">未找到功能狀態信息</string>
|
||||
<string name="susfs_feature_enabled">已啟用</string>
|
||||
<string name="susfs_feature_disabled">已禁用</string>
|
||||
<!-- Feature Labels -->
|
||||
<string name="sus_path_feature_label">SUS 路徑支持</string>
|
||||
<string name="sus_mount_feature_label">SUS 掛載支持</string>
|
||||
<string name="try_umount_feature_label">嘗試卸載支持</string>
|
||||
<string name="spoof_uname_feature_label">欺騙 uname 支持</string>
|
||||
<string name="sus_path_feature_label">SuS 路徑支援</string>
|
||||
<string name="sus_mount_feature_label">SuS 掛載支援</string>
|
||||
<string name="try_umount_feature_label">嘗試卸載支援</string>
|
||||
<string name="spoof_uname_feature_label">欺騙 uname 支援</string>
|
||||
<string name="spoof_cmdline_feature_label">欺騙 Cmdline/Bootconfig</string>
|
||||
<string name="open_redirect_feature_label">開放重定向支持</string>
|
||||
<string name="enable_log_feature_label">日誌記錄支持</string>
|
||||
<string name="open_redirect_feature_label">開放重定向支援</string>
|
||||
<string name="enable_log_feature_label">日誌記錄支援</string>
|
||||
<string name="auto_default_mount_feature_label">自動默認掛載</string>
|
||||
<string name="auto_bind_mount_feature_label">自動綁定掛載</string>
|
||||
<string name="auto_try_umount_bind_feature_label">自動嘗試卸載綁定掛載</string>
|
||||
<string name="hide_symbols_feature_label">隱藏 KSU SUSFS 符號</string>
|
||||
<string name="magic_mount_feature_label">魔法坐騎支持</string>
|
||||
<string name="sus_kstat_feature_label">SUS Kstat 支持</string>
|
||||
<string name="sus_su_feature_label">SUS SU 模式切換功能</string>
|
||||
<string name="hide_symbols_feature_label">隱藏 KSU SuSFS 符號</string>
|
||||
<string name="magic_mount_feature_label">魔法坐騎支援</string>
|
||||
<string name="sus_kstat_feature_label">SuS Kstat 支援</string>
|
||||
<string name="sus_su_feature_label">SuS SU 模式切換功能</string>
|
||||
<!-- 可切换状态 -->
|
||||
<string name="susfs_feature_configurable">可配置嘅 SuSFS 功能</string>
|
||||
<string name="susfs_enable_log_label">SuSFS 啟用日誌</string>
|
||||
@@ -461,8 +461,8 @@
|
||||
<string name="susfs_current_execution_location">當前執行位置:%s</string>
|
||||
<string name="susfs_execution_location_service">Service</string>
|
||||
<string name="susfs_execution_location_post_fs_data">Post-FS-Data</string>
|
||||
<string name="susfs_execution_location_service_description">在系統服務啟動後執行</string>
|
||||
<string name="susfs_execution_location_post_fs_data_description">在文件系統掛載後但系統完全啟動前執行,可能會導致循環重啟</string>
|
||||
<string name="susfs_execution_location_service_description">喺系統服務啟動後執行</string>
|
||||
<string name="susfs_execution_location_post_fs_data_description">喺文件系統掛載後但系統完全啟動前執行,可能會導致循環重新啟動</string>
|
||||
<string name="susfs_slot_info_title">槽位信息</string>
|
||||
<string name="susfs_slot_info_description">查看當前啟動槽位信息並複製數值</string>
|
||||
<string name="susfs_current_active_slot">當前活動槽位:%s</string>
|
||||
@@ -473,31 +473,135 @@
|
||||
<string name="susfs_slot_use_build_time">使用構建時間</string>
|
||||
<string name="susfs_slot_info_unavailable">無法獲取槽位信息</string>
|
||||
<!-- SuSFS 自启动相关字符串 -->
|
||||
<string name="susfs_autostart_enabled_success">SuSFS 自啟動模組已啟用,模組路徑:%s</string>
|
||||
<string name="susfs_autostart_disabled_success">SuSFS 自啟動模組已禁用</string>
|
||||
<string name="susfs_autostart_enabled_success">SuSFS 自動開啟模組已啟用,模組路徑:%s</string>
|
||||
<string name="susfs_autostart_disabled_success">SuSFS 自動開啟模組已停用</string>
|
||||
<!-- SuSFS Kstat相关字符串 -->
|
||||
<string name="susfs_tab_kstat_config">Kstat 配置</string>
|
||||
<string name="kstat_static_config_added">Kstat 靜態配置已添加:%1$s</string>
|
||||
<string name="kstat_static_config_added">Kstat 靜態配置已新增:%1$s</string>
|
||||
<string name="kstat_config_removed">已移除 Kstat 配置:%1$s</string>
|
||||
<string name="kstat_path_added">Kstat 路徑已添加:%1$s</string>
|
||||
<string name="kstat_path_added">Kstat 路徑已新增:%1$s</string>
|
||||
<string name="kstat_path_removed">已移除 Kstat 路徑:%1$s</string>
|
||||
<string name="kstat_updated">Kstat 已更新:%1$s</string>
|
||||
<string name="kstat_full_clone_updated">Kstat 完整克隆已更新:%1$s</string>
|
||||
<string name="add_kstat_statically_title">添加 Kstat 靜態配置</string>
|
||||
<string name="file_or_directory_path_label">文件/目錄路徑</string>
|
||||
<string name="hint_use_default_value">提示:可以使用 “default” 來使用原始值</string>
|
||||
<string name="add_kstat_path_title">添加 Kstat 路徑</string>
|
||||
<string name="add">添加</string>
|
||||
<string name="reset_kstat_config_title">重置 Kstat 配置</string>
|
||||
<string name="reset_kstat_config_message">確定要清除所有 Kstat 配置嗎?此操作不可撤銷</string>
|
||||
<string name="kstat_full_clone_updated">Kstat 完整複製已更新:%1$s</string>
|
||||
<string name="add_kstat_statically_title">新增 Kstat 靜態配置</string>
|
||||
<string name="file_or_directory_path_label">檔案/目錄路徑</string>
|
||||
<string name="hint_use_default_value">提示:可以用「default」嚟用返原本嘅值</string>
|
||||
<string name="add_kstat_path_title">新增 Kstat 路徑</string>
|
||||
<string name="add">新增</string>
|
||||
<string name="reset_kstat_config_title">重設 Kstat 配置</string>
|
||||
<string name="reset_kstat_config_message">你確定要清除晒所有 Kstat 配置咩?呢個動作係唔可以撤銷嘅</string>
|
||||
<string name="kstat_config_description_title">Kstat 配置說明</string>
|
||||
<string name="kstat_config_description_add_statically">• add_sus_kstat_statically: 靜態配置文件/目錄嘅 stat 信息</string>
|
||||
<string name="kstat_config_description_add">• add_sus_kstat: 在綁定掛載前添加路徑,存儲原始 stat 信息</string>
|
||||
<string name="kstat_config_description_update">• update_sus_kstat: 更新目標 ino,保持 size 同 blocks 不變</string>
|
||||
<string name="kstat_config_description_update_full_clone">• update_sus_kstat_full_clone: 僅更新 ino,其他保持原始值</string>
|
||||
<string name="kstat_config_description_add_statically">• add_sus_kstat_statically:靜態配置檔案/目錄嘅 stat 資訊</string>
|
||||
<string name="kstat_config_description_add">• add_sus_kstat:喺綁定掛載之前新增路徑,儲存原本嘅 stat 資訊</string>
|
||||
<string name="kstat_config_description_update">• update_sus_kstat:更新目標 ino,保持 size 同 blocks 唔變</string>
|
||||
<string name="kstat_config_description_update_full_clone">• update_sus_kstat_full_clone:淨係更新 ino,其他保持原本嘅值</string>
|
||||
<string name="static_kstat_config">靜態 Kstat 配置</string>
|
||||
<string name="kstat_path_management">Kstat 路徑管理</string>
|
||||
<string name="no_kstat_config_message">暫冇 Kstat 配置,點擊上方按鈕添加配置</string>
|
||||
<string name="no_kstat_config_message">暫時冇 Kstat 配置,請撳上面個掣嚟新增配置</string>
|
||||
<!-- SuSFS Mount Hiding Control Related Strings -->
|
||||
<string name="susfs_hide_mounts_control_title">SuS 掛載隱藏控制</string>
|
||||
<string name="susfs_hide_mounts_control_description">控制 SuS 掛載對程序嘅隱藏行為</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_label">對所有程序隱藏 SuS 掛載</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_enabled_description">啟用之後,SuS 掛載會對所有程序隱藏,包括 KSU 程序</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_disabled_description">禁用之後,SuS 掛載只會對非 KSU 程序隱藏,KSU 程序可以睇到掛載</string>
|
||||
<string name="susfs_hide_mounts_all_enabled">已啟用對所有程序隱藏 SuS 掛載</string>
|
||||
<string name="susfs_hide_mounts_all_disabled">已禁用對所有程序隱藏 SuS 掛載</string>
|
||||
<string name="susfs_hide_mounts_recommendation">建議喺解鎖螢幕之後或者喺 service.sh 或 boot-completed.sh 階段設為禁用,可以修復一啲依賴 KSU 程序掛載嘅 root 應用問題</string>
|
||||
<string name="susfs_hide_mounts_current_setting">而家嘅配置: %s</string>
|
||||
<string name="susfs_hide_mounts_setting_all">對所有程序隱藏</string>
|
||||
<string name="susfs_hide_mounts_setting_non_ksu">淨係對非 KS> 程序隱藏</string>
|
||||
<string name="susfs_run">執行</string>
|
||||
<string name="kernel_simple_kernel">核心版本簡潔模式</string>
|
||||
<string name="kernel_simple_kernel_summary">啟用或者禁用 SukiSU 核心版本顯示嘅簡潔模式</string>
|
||||
<string name="susfs_android_data_path_set">Android Data 路徑已配置為: %s</string>
|
||||
<string name="susfs_sdcard_path_set">SD 卡路徑已配置為: %s</string>
|
||||
<string name="susfs_path_setup_warning">路徑配置可能未完全成功,但會繼續新增 SuS 路徑</string>
|
||||
<!-- 备份和还原相关字符串 -->
|
||||
<string name="susfs_backup_title">備份</string>
|
||||
<string name="susfs_backup_description">建立所有 SuSFS 配置嘅備份。備份檔案會包含所有配置、路徑同配置信息。</string>
|
||||
<string name="susfs_backup_create">建立備份</string>
|
||||
<string name="susfs_backup_success">備份成功建立:%s</string>
|
||||
<string name="susfs_backup_failed">備份建立失敗:%s</string>
|
||||
<string name="susfs_backup_file_not_found">搵唔到備份檔案</string>
|
||||
<string name="susfs_backup_invalid_format">備份檔案格式無效</string>
|
||||
<string name="susfs_backup_version_mismatch">備份版本唔一樣,但會嘗試還原</string>
|
||||
<string name="susfs_restore_title">還原</string>
|
||||
<string name="susfs_restore_description">由備份檔案還原 SuSFS 配置。呢個動作會覆蓋晒而家所有配置。</string>
|
||||
<string name="susfs_restore_select_file">揀備份檔案</string>
|
||||
<string name="susfs_restore_success" formatted="false">配置還原成功,備份建立於 %s,嚟自裝置:%s</string>
|
||||
<string name="susfs_restore_failed">還原失敗:%s</string>
|
||||
<string name="susfs_restore_confirm_title">確認還原</string>
|
||||
<string name="susfs_restore_confirm_description">呢個動作會覆蓋晒而家所有 SuSFS 配置。你確定要繼續咩?</string>
|
||||
<string name="susfs_restore_confirm">還原</string>
|
||||
<string name="susfs_backup_info_date">備份日期:%s</string>
|
||||
<string name="susfs_backup_info_device">裝置:%s</string>
|
||||
<string name="susfs_backup_info_version">版本:%s</string>
|
||||
<string name="hide_bl_script">隱藏 BL 指令碼</string>
|
||||
<string name="hide_bl_script_description">啟用隱藏 Bootloader 解鎖狀態指令碼</string>
|
||||
<string name="cleanup_residue">清理工具殘留</string>
|
||||
<string name="cleanup_residue_description">清理各種模組以及工具嘅殘留檔案同目錄(可能會誤刪導致丟失以及無法啟動,謹慎使用)</string>
|
||||
<string name="susfs_edit_sus_path">編輯 SuS 路徑</string>
|
||||
<string name="susfs_edit_sus_mount">編輯 SuS 掛載</string>
|
||||
<string name="susfs_edit_try_umount">編輯嘗試解除安裝</string>
|
||||
<string name="edit_kstat_statically_title">編輯 Kstat 靜態配置</string>
|
||||
<string name="edit_kstat_path_title">編輯 Kstat 路徑</string>
|
||||
<string name="susfs_save">儲存</string>
|
||||
<string name="edit">編輯</string>
|
||||
<string name="delete">刪除</string>
|
||||
<string name="update">更新</string>
|
||||
<string name="kstat_config_updated">Kstat 配置更新</string>
|
||||
<string name="kstat_path_updated">Kstat 路徑更新</string>
|
||||
<string name="susfs_update_full_clone">Susfs 完整克隆更新</string>
|
||||
<string name="umount_zygote_iso_service">解除安裝 Zygote 隔離服務</string>
|
||||
<string name="umount_zygote_iso_service_description">啟用此選項將喺系統啟動時解除安裝 Zygote 隔離服務掛載點</string>
|
||||
<string name="umount_zygote_iso_service_enabled">Zygote 隔離服務解除安裝已啟用</string>
|
||||
<string name="umount_zygote_iso_service_disabled">Zygote 隔離服務解除安裝已禁用</string>
|
||||
<string name="app_paths_section">應用路徑</string>
|
||||
<string name="other_paths_section">其他路徑</string>
|
||||
<string name="add_custom_path">其他</string>
|
||||
<string name="add_app_path">應用</string>
|
||||
<string name="susfs_add_app_path">添加應用程式路徑</string>
|
||||
<string name="search_apps">搜尋應用程式</string>
|
||||
<string name="selected_apps_count">%1$d 個已選應用程式</string>
|
||||
<string name="already_added_apps_count">%1$d 個已添加應用程式</string>
|
||||
<string name="all_apps_already_added">所有应用均已添加</string>
|
||||
<string name="dynamic_manager_title">動態簽名配置</string>
|
||||
<string name="dynamic_manager_enabled_summary">已啟用(大小: %s)</string>
|
||||
<string name="dynamic_manager_disabled">未啟用</string>
|
||||
<string name="enable_dynamic_manager">啟用動態簽名</string>
|
||||
<string name="signature_size">簽名大小</string>
|
||||
<string name="signature_hash">簽名哈希值</string>
|
||||
<string name="hash_must_be_64_chars">哈希值必須是 64 位十六進制字符</string>
|
||||
<string name="dynamic_manager_set_success">動態簽名配置設定成功</string>
|
||||
<string name="dynamic_manager_set_failed">動態簽名配置設定失敗</string>
|
||||
<string name="invalid_sign_config">無效嘅簽名配置</string>
|
||||
<string name="dynamic_manager_disabled_success">動態簽名已禁用</string>
|
||||
<string name="dynamic_manager_clear_failed">清除動態簽名錯誤</string>
|
||||
<string name="dynamic_managerature">動態</string>
|
||||
<string name="signature_index">簽名 %1$d</string>
|
||||
<string name="unknown_signature">未知</string>
|
||||
<string name="multi_manager_list">活躍管理器</string>
|
||||
<string name="no_active_manager">唔活躍管理器</string>
|
||||
<string name="home_zygisk_implement">Zygisk 實現</string>
|
||||
<!-- 循环路径相关 -->
|
||||
<!-- 循环路径功能描述 -->
|
||||
<string name="sus_loop_path_feature_label">SuS 循環路徑</string>
|
||||
<string name="sus_loop_paths_description_title">循環路徑配置</string>
|
||||
<string name="sus_loop_paths_description_text">循環路徑會喺每次非 root 用戶應用程式或者隔離服務啟動時,重新標記做 SUS_PATH。咁樣可以解決因為 inode 狀態重設或者核心重新建立 inode 而令到添加嘅路徑失效嘅問題。</string>
|
||||
<string name="avc_log_spoofing">AVC 日誌欺騙</string>
|
||||
<string name="avc_log_spoofing_enabled">AVC 日誌欺騙已經啟用</string>
|
||||
<string name="avc_log_spoofing_disabled">AVC 日誌欺騙已經禁用</string>
|
||||
<string name="avc_log_spoofing_description">禁用:喺核心 AVC 日誌入面禁用對 \'su\' 嘅 sus tcontext 進行欺騙。\n
|
||||
啟用:喺核心 AVC 日誌入面將 \'su\' 嘅 sus tcontext 欺騙成 \'kernel\'。</string>
|
||||
<string name="avc_log_spoofing_warning">重要提示:\n
|
||||
- 核心入面嘅預設設定係 \'0\'。\n
|
||||
- 啟用呢個功能有時會令到開發人員喺除錯權限或者 SELinux 問題嗰陣難以搵出原因,所以建議用戶喺除錯時禁用呢個功能。</string>
|
||||
<!-- 模块签名功能描述 -->
|
||||
<string name="module_verified">已驗證</string>
|
||||
<string name="module_signature_verified">模組簽名已驗證</string>
|
||||
<string name="module_signature_verification">驗證簽名</string>
|
||||
<string name="module_signature_verification_summary">模組安裝嗰陣,會強制驗證個簽名。(淨係 ARM架構 用得)</string>
|
||||
<string name="module_signature_invalid">未知發布者</string>
|
||||
<string name="module_signature_invalid_message">未經簽名嘅模組可能唔完整。為咗保護裝置,已經阻止安裝呢個模組。</string>
|
||||
<string name="module_signature_verification_failed">未經簽名嘅模組可能唔完整。你想唔想安裝嚟自未知發布者嘅模組?</string>
|
||||
</resources>
|
||||
|
||||
@@ -177,6 +177,8 @@
|
||||
<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="hide_zygisk_implement">隱藏 Zygisk 狀態資訊</string>
|
||||
<string name="hide_zygisk_implement_summary">隱藏主頁上的 Zygisk 實現狀態資訊</string>
|
||||
<string name="hide_link_card">隱藏連結卡片</string>
|
||||
<string name="hide_link_card_summary">隱藏首頁上的連結卡片資訊</string>
|
||||
<string name="hide_tag_card">隱藏模組標籤行</string>
|
||||
@@ -289,8 +291,8 @@
|
||||
<string name="icon_switch_summary">更換為 KernelSU 圖示</string>
|
||||
<string name="icon_switched">已切換圖示</string>
|
||||
<!-- KPM display settings -->
|
||||
<string name="show_kpm_info">顯示 KPM 功能</string>
|
||||
<string name="show_kpm_info_summary">在首頁和底欄顯示 KPM 相關功能與資訊(需重新開啟應用程式)</string>
|
||||
<string name="show_kpm_info">隱藏 KPM 功能</string>
|
||||
<string name="show_kpm_info_summary">在首頁和底欄隱藏 KPM 相關功能與資訊</string>
|
||||
<!-- Webui X settings -->
|
||||
<string name="use_webuix">選擇使用的 WebUI 引擎</string>
|
||||
<string name="engine_auto_select">自動選擇</string>
|
||||
@@ -384,22 +386,22 @@
|
||||
<string name="susfs_no_config_to_autostart">無可用設定進行開機自動啟動</string>
|
||||
<!-- SuSFS Tab Titles -->
|
||||
<string name="susfs_tab_basic_settings">基本設定</string>
|
||||
<string name="susfs_tab_sus_paths">SUS 路徑</string>
|
||||
<string name="susfs_tab_sus_mounts">SUS 掛載</string>
|
||||
<string name="susfs_tab_sus_paths">SuS 路徑</string>
|
||||
<string name="susfs_tab_sus_mounts">SuS 掛載</string>
|
||||
<string name="susfs_tab_try_umount">嘗試卸載</string>
|
||||
<string name="susfs_tab_path_settings">路徑設定</string>
|
||||
<string name="susfs_tab_enabled_features">啟用功能狀態</string>
|
||||
<!-- SuSFS Path Management -->
|
||||
<string name="susfs_add_sus_path">新增 SUS 路徑</string>
|
||||
<string name="susfs_add_sus_mount">新增 SUS 掛載</string>
|
||||
<string name="susfs_add_sus_path">新增 SuS 路徑</string>
|
||||
<string name="susfs_add_sus_mount">新增 SuS 掛載</string>
|
||||
<string name="susfs_add_try_umount">新增嘗試卸載</string>
|
||||
<string name="susfs_sus_path_added_success">成功添加 SUS 路径</string>
|
||||
<string name="susfs_sus_path_added_success">成功添加 SuS 路径</string>
|
||||
<string name="susfs_path_not_found_error">未找到路径</string>
|
||||
<string name="susfs_path_label">路徑</string>
|
||||
<string name="susfs_mount_path_label">掛載路徑</string>
|
||||
<string name="susfs_path_placeholder">例如:/system/addon.d</string>
|
||||
<string name="susfs_no_paths_configured">暫無 SUS 路徑設定</string>
|
||||
<string name="susfs_no_mounts_configured">暫無 SUS 掛載設定</string>
|
||||
<string name="susfs_no_paths_configured">暫無 SuS 路徑設定</string>
|
||||
<string name="susfs_no_mounts_configured">暫無 SuS 掛載設定</string>
|
||||
<string name="susfs_no_umounts_configured">暫無嘗試卸載設定</string>
|
||||
<!-- SuSFS Umount Mode -->
|
||||
<string name="susfs_umount_mode_label">卸載模式</string>
|
||||
@@ -414,10 +416,10 @@
|
||||
<string name="susfs_run_umount_confirm_title">確認執行嘗試卸載</string>
|
||||
<string name="susfs_run_umount_confirm_message">這將立即執行所有已設定的嘗試卸載操作,確定要繼續嗎?</string>
|
||||
<!-- SuSFS Reset Categories -->
|
||||
<string name="susfs_reset_paths_title">重設 SUS 路徑</string>
|
||||
<string name="susfs_reset_paths_message">這將清除所有 SUS 路徑設定,確定要繼續嗎?</string>
|
||||
<string name="susfs_reset_mounts_title">重設 SUS 掛載</string>
|
||||
<string name="susfs_reset_mounts_message">這將清除所有 SUS 掛載設定,確定要繼續嗎?</string>
|
||||
<string name="susfs_reset_paths_title">重設 SuS 路徑</string>
|
||||
<string name="susfs_reset_paths_message">這將清除所有 SuS 路徑設定,確定要繼續嗎?</string>
|
||||
<string name="susfs_reset_mounts_title">重設 SuS 掛載</string>
|
||||
<string name="susfs_reset_mounts_message">這將清除所有 SuS 掛載設定,確定要繼續嗎?</string>
|
||||
<string name="susfs_reset_umounts_title">重設嘗試卸載</string>
|
||||
<string name="susfs_reset_umounts_message">這將清除所有嘗試卸載設定,確定要繼續嗎?</string>
|
||||
<string name="susfs_reset_path_title">重置路徑設定</string>
|
||||
@@ -432,8 +434,8 @@
|
||||
<string name="susfs_feature_enabled">已啟用</string>
|
||||
<string name="susfs_feature_disabled">已停用</string>
|
||||
<!-- Feature Labels -->
|
||||
<string name="sus_path_feature_label">SUS 路徑支援</string>
|
||||
<string name="sus_mount_feature_label">SUS 掛載支援</string>
|
||||
<string name="sus_path_feature_label">SuS 路徑支援</string>
|
||||
<string name="sus_mount_feature_label">SuS 掛載支援</string>
|
||||
<string name="try_umount_feature_label">嘗試卸載支援</string>
|
||||
<string name="spoof_uname_feature_label">偽裝 uname 支援</string>
|
||||
<string name="spoof_cmdline_feature_label">偽裝 Cmdline/Bootconfig</string>
|
||||
@@ -442,10 +444,10 @@
|
||||
<string name="auto_default_mount_feature_label">自動預設掛載</string>
|
||||
<string name="auto_bind_mount_feature_label">自動綁定掛載</string>
|
||||
<string name="auto_try_umount_bind_feature_label">自動嘗試卸載綁定掛載</string>
|
||||
<string name="hide_symbols_feature_label">隱藏 KSU SUSFS 符號</string>
|
||||
<string name="hide_symbols_feature_label">隱藏 KSU SuSFS 符號</string>
|
||||
<string name="magic_mount_feature_label">魔法掛載支援</string>
|
||||
<string name="sus_kstat_feature_label">SUS 核心統計支援</string>
|
||||
<string name="sus_su_feature_label">SUS SU 模式切換功能</string>
|
||||
<string name="sus_kstat_feature_label">SuS 核心統計支援</string>
|
||||
<string name="sus_su_feature_label">SuS SU 模式切換功能</string>
|
||||
<!-- 可切换状态 -->
|
||||
<string name="susfs_feature_configurable">可設定的 SuSFS 功能</string>
|
||||
<string name="susfs_enable_log_label">SuSFS 啟用日誌</string>
|
||||
@@ -500,13 +502,13 @@
|
||||
<string name="kstat_path_management">Kstat 路徑管理</string>
|
||||
<string name="no_kstat_config_message">暫無 Kstat 設定,請點擊上方按鈕新增設定</string>
|
||||
<!-- SuSFS Mount Hiding Control Related Strings -->
|
||||
<string name="susfs_hide_mounts_control_title">SUS掛載隱藏控制</string>
|
||||
<string name="susfs_hide_mounts_control_description">控制SUS掛載對程序的隱藏行為</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_label">對所有程序隱藏SUS掛載</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_enabled_description">啟用後,SUS掛載將對所有程序隱藏,包括KSU程序</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_disabled_description">禁用後,SUS掛載僅對非KSU程序隱藏,KSU程序可以看到掛載</string>
|
||||
<string name="susfs_hide_mounts_all_enabled">已啟用對所有程序隱藏SUS掛載</string>
|
||||
<string name="susfs_hide_mounts_all_disabled">已禁用對所有程序隱藏SUS掛載</string>
|
||||
<string name="susfs_hide_mounts_control_title">SuS 掛載隱藏控制</string>
|
||||
<string name="susfs_hide_mounts_control_description">控制 SuS 掛載對程序的隱藏行為</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_label">對所有程序隱藏 SuS 掛載</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_enabled_description">啟用後,SuS 掛載將對所有程序隱藏,包括 KSU 程序</string>
|
||||
<string name="susfs_hide_mounts_for_all_procs_disabled_description">禁用後,SuS 掛載僅對非 KSU 程序隱藏,KSU 程序可以看到掛載</string>
|
||||
<string name="susfs_hide_mounts_all_enabled">已啟用對所有程序隱藏 SuS 掛載</string>
|
||||
<string name="susfs_hide_mounts_all_disabled">已禁用對所有程序隱藏 SuS 掛載</string>
|
||||
<string name="susfs_hide_mounts_recommendation">建議在螢幕解鎖後或在 service.sh 或 boot-completed.sh 階段設定為禁用,這可以修復一些依賴 KSU 程序掛載的 root 應用的問題</string>
|
||||
<string name="susfs_hide_mounts_current_setting">當前設定: %s</string>
|
||||
<string name="susfs_hide_mounts_setting_all">對所有程序隱藏</string>
|
||||
@@ -514,47 +516,110 @@
|
||||
<string name="susfs_run">執行</string>
|
||||
<string name="kernel_simple_kernel">核心版本簡潔模式</string>
|
||||
<string name="kernel_simple_kernel_summary">啟用或禁用 SukiSU 核心版本顯示的簡潔模式</string>
|
||||
<string name="susfs_android_data_path_set">Android Data 路徑已設定為:%s</string>
|
||||
<string name="susfs_sdcard_path_set">SD 卡路徑已設定為:%s</string>
|
||||
<string name="susfs_path_setup_warning">路徑設定可能沒有完全成功,但仍會繼續新增 SUS 路徑</string>
|
||||
<!-- 備份和還原相關字串 -->
|
||||
<string name="susfs_android_data_path_set">Android Data 路徑已設定為: %s</string>
|
||||
<string name="susfs_sdcard_path_set">SD 卡路徑已設定為: %s</string>
|
||||
<string name="susfs_path_setup_warning">路徑設定可能未完全成功,但將繼續新增 SuS 路徑</string>
|
||||
<!-- 备份和还原相关字符串 -->
|
||||
<string name="susfs_backup_title">備份</string>
|
||||
<string name="susfs_backup_description">建立所有 SuSFS 設定的備份。備份檔案會包含所有設定、路徑與配置信息</string>
|
||||
<string name="susfs_backup_description">建立所有 SuSFS 配置的備份。備份檔案將包含所有設定、路徑和配置資訊。</string>
|
||||
<string name="susfs_backup_create">建立備份</string>
|
||||
<string name="susfs_backup_success">備份成功建立:%s</string>
|
||||
<string name="susfs_backup_success">備份建立成功:%s</string>
|
||||
<string name="susfs_backup_failed">備份建立失敗:%s</string>
|
||||
<string name="susfs_backup_file_not_found">找不到備份檔案</string>
|
||||
<string name="susfs_backup_invalid_format">備份檔案格式無效</string>
|
||||
<string name="susfs_backup_version_mismatch">備份版本不相容,將嘗試進行還原</string>
|
||||
<string name="susfs_backup_file_not_found">備份檔案未找到</string>
|
||||
<string name="susfs_backup_invalid_format">無效的備份檔案格式</string>
|
||||
<string name="susfs_backup_version_mismatch">備份版本不匹配,但將嘗試還原</string>
|
||||
<string name="susfs_restore_title">還原</string>
|
||||
<string name="susfs_restore_description">從備份檔案還原 SuSFS 設定,這會覆蓋目前所有設定</string>
|
||||
<string name="susfs_restore_select_file">選取備份檔案</string>
|
||||
<string name="susfs_restore_success" formatted="false">設定還原成功,備份建立於 %s,來源裝置:%s</string>
|
||||
<string name="susfs_restore_description">從備份檔案還原 SuSFS 配置。這將覆蓋所有當前設定。</string>
|
||||
<string name="susfs_restore_select_file">選擇備份檔案</string>
|
||||
<string name="susfs_restore_success" formatted="false">配置還原成功,備份創建於 %s,來自裝置:%s</string>
|
||||
<string name="susfs_restore_failed">還原失敗:%s</string>
|
||||
<string name="susfs_restore_confirm_title">確認還原</string>
|
||||
<string name="susfs_restore_confirm_description">這將會覆蓋目前所有的 SuSFS 設定,確定要繼續嗎?</string>
|
||||
<string name="susfs_restore_confirm_description">這將覆蓋所有當前的 SuSFS 配置。您確定要繼續嗎?</string>
|
||||
<string name="susfs_restore_confirm">還原</string>
|
||||
<string name="susfs_backup_info_date">備份日期:%s</string>
|
||||
<string name="susfs_backup_info_device">裝置:%s</string>
|
||||
<string name="susfs_backup_info_version">版本:%s</string>
|
||||
<string name="hide_bl_script">隱藏 BL 腳本</string>
|
||||
<string name="hide_bl_script_description">啟用此選項可隱藏 Bootloader 解鎖狀態的腳本</string>
|
||||
<string name="cleanup_residue">清除工具殘留</string>
|
||||
<string name="cleanup_residue_description">清除各種模組與工具殘留的檔案與資料夾(可能會誤刪導致資料遺失或無法開機,請小心使用)</string>
|
||||
<string name="susfs_edit_sus_path">編輯 SUS 路徑</string>
|
||||
<string name="susfs_edit_sus_mount">編輯 SUS 掛載</string>
|
||||
<string name="susfs_edit_try_umount">編輯嘗試卸載</string>
|
||||
<string name="edit_kstat_statically_title">編輯 Kstat 靜態設定</string>
|
||||
<string name="hide_bl_script">隱藏 BL 指令碼</string>
|
||||
<string name="hide_bl_script_description">啟用隱藏 Bootloader 解鎖狀態指令碼</string>
|
||||
<string name="cleanup_residue">清理工具殘留</string>
|
||||
<string name="cleanup_residue_description">清理各種模組以及工具的殘留檔案和目錄(可能會誤刪導致丟失以及無法啟動,謹慎使用)</string>
|
||||
<string name="susfs_edit_sus_path">編輯 SuS 路徑</string>
|
||||
<string name="susfs_edit_sus_mount">編輯 SuS 掛載</string>
|
||||
<string name="susfs_edit_try_umount">編輯嘗試解除安裝</string>
|
||||
<string name="edit_kstat_statically_title">編輯 Kstat 靜態配置</string>
|
||||
<string name="edit_kstat_path_title">編輯 Kstat 路徑</string>
|
||||
<string name="susfs_save">儲存</string>
|
||||
<string name="edit">編輯</string>
|
||||
<string name="delete">刪除</string>
|
||||
<string name="update">更新</string>
|
||||
<string name="kstat_config_updated">Kstat 設定已更新</string>
|
||||
<string name="kstat_path_updated">Kstat 路徑已更新</string>
|
||||
<string name="susfs_update_full_clone">SusFS 完整複製更新</string>
|
||||
<string name="umount_zygote_iso_service">卸載 Zygote 隔離服務</string>
|
||||
<string name="umount_zygote_iso_service_description">啟用此選項後,系統開機時會自動卸載 Zygote 隔離服務的掛載點</string>
|
||||
<string name="umount_zygote_iso_service_enabled">已啟用卸載 Zygote 隔離服務</string>
|
||||
<string name="umount_zygote_iso_service_disabled">已停用卸載 Zygote 隔離服務</string>
|
||||
<string name="kstat_config_updated">Kstat 配置更新</string>
|
||||
<string name="kstat_path_updated">Kstat 路徑更新</string>
|
||||
<string name="susfs_update_full_clone">Susfs 完整克隆更新</string>
|
||||
<string name="umount_zygote_iso_service">解除安裝 Zygote 隔離服務</string>
|
||||
<string name="umount_zygote_iso_service_description">啟用此選項將在系統啟動時解除安裝 Zygote 隔離服務掛載點</string>
|
||||
<string name="umount_zygote_iso_service_enabled">Zygote 隔離服務解除安裝已啟用</string>
|
||||
<string name="umount_zygote_iso_service_disabled">Zygote 隔離服務解除安裝已禁用</string>
|
||||
<string name="app_paths_section">應用路徑</string>
|
||||
<string name="other_paths_section">其他路徑</string>
|
||||
<string name="add_custom_path">其他</string>
|
||||
<string name="add_app_path">應用</string>
|
||||
<string name="susfs_add_app_path">添加應用程式路徑</string>
|
||||
<string name="search_apps">搜尋應用程式</string>
|
||||
<string name="selected_apps_count">%1$d 個已選應用程式</string>
|
||||
<string name="already_added_apps_count">%1$d 個已添加應用程式</string>
|
||||
<string name="all_apps_already_added">所有应用均已添加</string>
|
||||
<string name="dynamic_manager_title">动态签名配置</string>
|
||||
<string name="dynamic_manager_enabled_summary">已启用(大小: %s)</string>
|
||||
<string name="dynamic_manager_disabled">未启用</string>
|
||||
<string name="enable_dynamic_manager">啟用動態簽名</string>
|
||||
<string name="signature_size">簽名大小</string>
|
||||
<string name="signature_hash">簽名哈希值</string>
|
||||
<string name="hash_must_be_64_chars">哈希值必須是 64 位十六進制字符</string>
|
||||
<string name="dynamic_manager_set_success">動態簽名配置設定成功</string>
|
||||
<string name="dynamic_manager_set_failed">動態簽名配置設定失敗</string>
|
||||
<string name="invalid_sign_config">無效的簽名配置</string>
|
||||
<string name="dynamic_manager_disabled_success">動態簽名已禁用</string>
|
||||
<string name="dynamic_manager_clear_failed">清除動態簽名錯誤</string>
|
||||
<string name="dynamic_managerature">動態</string>
|
||||
<string name="signature_index">簽名 %1$d</string>
|
||||
<string name="unknown_signature">未知</string>
|
||||
<string name="multi_manager_list">活躍管理器</string>
|
||||
<string name="no_active_manager">無活躍管理器</string>
|
||||
<string name="home_zygisk_implement">Zygisk 實現</string>
|
||||
<!-- 循环路径相关 -->
|
||||
<string name="susfs_tab_sus_loop_paths">SuS 迴圈路徑</string>
|
||||
<string name="susfs_add_sus_loop_path">添加 SuS 迴圈路徑</string>
|
||||
<string name="susfs_edit_sus_loop_path">編輯 SuS 迴圈路徑</string>
|
||||
<string name="susfs_loop_path_added_success">SuS 迴圈路徑添加成功: %1$s</string>
|
||||
<string name="susfs_loop_path_removed">SuS 迴圈路徑已移除: %1$s</string>
|
||||
<string name="susfs_loop_path_updated">SuS 迴圈路徑已更新: %1$s -> %2$s</string>
|
||||
<string name="susfs_no_loop_paths_configured">未配置 SuS 迴圈路徑</string>
|
||||
<string name="susfs_reset_loop_paths_title">重置迴圈路徑</string>
|
||||
<string name="susfs_reset_loop_paths_message">確定要清空所有 SuS 循環路徑嗎?此操作無法撤銷。</string>
|
||||
<string name="susfs_loop_path_label">循環路徑</string>
|
||||
<string name="susfs_loop_path_restriction_warning">注意:只有不在 /storage/ 和 /sdcard/ 內的路徑才能通過循環路徑添加。</string>
|
||||
<string name="susfs_loop_path_invalid_location">錯誤:循環路徑不能位於 /storage/ 或 /sdcard/ 目錄內</string>
|
||||
<string name="loop_paths_section">循環路徑</string>
|
||||
<string name="add_loop_path">添加循環路徑</string>
|
||||
<!-- 循环路径功能描述 -->
|
||||
<string name="sus_loop_path_feature_label">SuS 迴圈路徑</string>
|
||||
<string name="sus_loop_paths_description_title">迴圈路徑配置</string>
|
||||
<string name="sus_loop_paths_description_text">迴圈路徑會在每次非 root 使用者應用程式或隔離服務啟動時重新標記為 SUS_PATH。這有助於解決添加的路徑可能因 inode 狀態重置或內核中 inode 重新建立而失效的問題</string>
|
||||
<string name="avc_log_spoofing">AVC 日志欺騙</string>
|
||||
<string name="avc_log_spoofing_enabled">AVC 日志欺騙已啟用</string>
|
||||
<string name="avc_log_spoofing_disabled">AVC 日志欺騙已禁用</string>
|
||||
<string name="avc_log_spoofing_description">禁用: 禁用在核心 AVC 日誌中欺騙 \'su\' 的 sus tcontext。\n
|
||||
啟用: 啟用在核心 AVC 日誌中將 \'su\' 的 sus tcontext 欺騙為 \'kernel\'</string>
|
||||
<string name="avc_log_spoofing_warning">重要提示:\n
|
||||
- 核心中預設設定為 \'0\'\n
|
||||
- 啟用此功能有時會使開發人員在除錯許可權或 SELinux 問題時難以識別原因,因此建議使用者在除錯時禁用此功能。</string>
|
||||
<!-- 模块签名功能描述 -->
|
||||
<string name="module_verified">已驗證</string>
|
||||
<string name="module_signature_verified">模組簽名已驗證</string>
|
||||
<string name="module_signature_verification">驗證簽名</string>
|
||||
<string name="module_signature_verification_summary">模組安裝時,強制驗證簽名。(僅 ARM架構 可用)</string>
|
||||
<string name="module_signature_invalid">未知發布者</string>
|
||||
<string name="module_signature_invalid_message">未經簽名的模組可能不完整。為了對設備進行保護,已阻止安裝此模組。</string>
|
||||
<string name="module_signature_verification_failed">未經簽名的模組可能不完整。你想安裝來自未知發布者的模組嗎?</string>
|
||||
<string name="home_hook_type">鉤子類型</string>
|
||||
</resources>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<string name="home_working">Working</string>
|
||||
<string name="home_working_version">Version: %s</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 version</string>
|
||||
<string name="home_susfs_version">SuSFS Version</string>
|
||||
<string name="home_manager_version">Manager version</string>
|
||||
@@ -50,8 +50,8 @@
|
||||
<string name="home_learn_kernelsu_url">https://kernelsu.org/guide/what-is-kernelsu.html</string>
|
||||
<string name="home_click_to_learn_kernelsu">Learn how to install KernelSU and use modules</string>
|
||||
<string name="home_support_title">Support Us</string>
|
||||
<string name="home_support_content">KernelSU is, and always will be, free, and open source. You can however show us that you care by making a donation.</string>
|
||||
<string name="about_source_code"><![CDATA[View source code at %1$s<br/>Join our %2$s channel]]></string>
|
||||
<string name="home_support_content">KernelSU is, and always will be, free, and open source. You can however show us that you care by making a donation</string>
|
||||
<string name="about_source_code"><![CDATA[View source code at %1$s<br/>Join our %2$s channel<br/><br/>The images of the files with anime character sticker are copyrighted by %3$s, the Brand Intellectual Property in the images is owned by %4$s. Before using these files, in addition to complying with %5$s, you also need to comply with the authorization of the two authors to use these artistic contents]]></string>
|
||||
<string name="profile" translatable="false">App Profile</string>
|
||||
<string name="profile_default">Default</string>
|
||||
<string name="profile_template">Template</string>
|
||||
@@ -64,15 +64,15 @@
|
||||
<string name="failed_to_update_app_profile">Failed to update App Profile for %s</string>
|
||||
<string name="require_kernel_version" formatted="false">The current KernelSU version %s is too low for the manager to work properly. Please upgrade to version %s or higher!</string>
|
||||
<string name="settings_umount_modules_default">Umount modules by default</string>
|
||||
<string name="settings_umount_modules_default_summary">The global default value for \"Umount modules\" in App Profile. If enabled, it will remove all module modifications to the system for apps that don\'t have a profile set.</string>
|
||||
<string name="settings_susfs_toggle">Disable kprobe hooks</string>
|
||||
<string name="profile_umount_modules_summary">Enabling this option will allow KernelSU to restore any modified files by the modules for this app.</string>
|
||||
<string name="settings_umount_modules_default_summary">The global default value for \"Umount modules\" in App Profile. If enabled, it will remove all module modifications to the system for apps that don\'t have a profile set</string>
|
||||
<string name="settings_susfs_toggle">Disable kprobes hook</string>
|
||||
<string name="profile_umount_modules_summary">Enabling this option will allow KernelSU to restore any modified files by the modules for this app</string>
|
||||
<string name="profile_selinux_domain">Domain</string>
|
||||
<string name="profile_selinux_rules">Rules</string>
|
||||
<string name="module_update">Update</string>
|
||||
<string name="module_downloading">Downloading module: %s</string>
|
||||
<string name="module_start_downloading">Start downloading: %s</string>
|
||||
<string name="new_version_available">New version %s is available, click to upgrade.</string>
|
||||
<string name="new_version_available">New version %s is available, click to upgrade</string>
|
||||
<string name="launch_app">Launch</string>
|
||||
<string name="force_stop_app" formatted="false">Force stop</string>
|
||||
<string name="restart_app">Restart</string>
|
||||
@@ -100,28 +100,28 @@
|
||||
<string name="app_profile_template_save_failed">Failed to save template</string>
|
||||
<string name="app_profile_template_import_empty">Clipboard is empty!</string>
|
||||
<string name="module_changelog_failed">Fetch changelog failed: %s</string>
|
||||
<string name="settings_check_update">Check update</string>
|
||||
<string name="settings_check_update">Check for updates</string>
|
||||
<string name="settings_check_update_summary">Automatically check for updates when opening the app</string>
|
||||
<string name="grant_root_failed">Failed to grant root!</string>
|
||||
<string name="action">Action</string>
|
||||
<string name="close">Close</string>
|
||||
<string name="enable_web_debugging">Enable WebView debugging</string>
|
||||
<string name="enable_web_debugging_summary">Can be used to debug WebUI. Please enable only when needed.</string>
|
||||
<string name="enable_web_debugging_summary">Can be used to debug WebUI. Please enable only when needed</string>
|
||||
<string name="direct_install">Direct install (Recommended)</string>
|
||||
<string name="select_file">Select a image that needs to be patched</string>
|
||||
<string name="install_inactive_slot">Install to inactive slot (After OTA)</string>
|
||||
<string name="install_inactive_slot_warning">Your device will be **FORCED** to boot to the current inactive slot after a reboot!\nOnly use this option after OTA is done.\nContinue?</string>
|
||||
<string name="install_next">Next</string>
|
||||
<string name="select_file_tip">%1$s partition image is recommended</string>
|
||||
<string name="select_file_tip_vendor">(unstable)</string>
|
||||
<string name="select_file_tip_vendor">(Unstable)</string>
|
||||
<string name="select_kmi">Select KMI</string>
|
||||
<string name="settings_uninstall">Uninstall</string>
|
||||
<string name="settings_uninstall_temporary">Uninstall temporarily</string>
|
||||
<string name="settings_uninstall_permanent">Uninstall permanently</string>
|
||||
<string name="settings_restore_stock_image">Restore stock image</string>
|
||||
<string name="settings_uninstall_temporary_message">Temporarily uninstall KernelSU, restore to original state after next reboot.</string>
|
||||
<string name="settings_uninstall_permanent_message">Uninstalling KernelSU (Root and all modules) completely and permanently.</string>
|
||||
<string name="settings_restore_stock_image_message">Restore the stock factory image (If a backup exists), usually used before OTA; if you need to uninstall KernelSU, please use \"Uninstall permanently\".</string>
|
||||
<string name="settings_uninstall_temporary_message">Temporarily uninstall KernelSU, restore to original state after next reboot</string>
|
||||
<string name="settings_uninstall_permanent_message">Uninstalling KernelSU (Root and all modules) completely and permanently</string>
|
||||
<string name="settings_restore_stock_image_message">Restore the stock factory image (If a backup exists), usually used before OTA; if you need to uninstall KernelSU, please use \"Uninstall permanently\"</string>
|
||||
<string name="flashing">Flashing</string>
|
||||
<string name="flash_success">Flash success</string>
|
||||
<string name="flash_failed">Flash failed</string>
|
||||
@@ -130,8 +130,8 @@
|
||||
<string name="log_saved">Logs saved</string>
|
||||
<string name="sus_su_mode">SuS SU mode:</string>
|
||||
<!-- Module related -->
|
||||
<string name="module_install_confirm">confirm install module %1$s?</string>
|
||||
<string name="unknown_module">unknown module</string>
|
||||
<string name="module_install_confirm">Confirm install module %1$s?</string>
|
||||
<string name="unknown_module">Unknown module</string>
|
||||
<!-- Restore related -->
|
||||
<string name="restore_confirm_title">Confirm Module Restoration</string>
|
||||
<string name="restore_confirm_message">This operation will overwrite all existing modules. Continue?</string>
|
||||
@@ -140,8 +140,8 @@
|
||||
<!-- Backup related -->
|
||||
<string name="backup_success">Backup successful (tar.gz)</string>
|
||||
<string name="backup_failed">Backup failed: %1$s</string>
|
||||
<string name="backup_modules">backup modules</string>
|
||||
<string name="restore_modules">restore modules</string>
|
||||
<string name="backup_modules">Backup modules</string>
|
||||
<string name="restore_modules">Restore modules</string>
|
||||
<!-- Restore related messages -->
|
||||
<string name="restore_success">Modules restored successfully, restart required</string>
|
||||
<string name="restore_failed">Restore failed: %1$s</string>
|
||||
@@ -165,7 +165,7 @@
|
||||
<string name="home_device_model">Device model</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_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="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="selinux">SELinux</string>
|
||||
@@ -179,6 +179,8 @@
|
||||
<string name="hide_other_info_summary">Hides Red dot about the number of super users, modules and KPM modules on the navigation bar 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="hide_zygisk_implement">Hide Zygisk status</string>
|
||||
<string name="hide_zygisk_implement_summary">Hide Zygisk implementation information on the home page</string>
|
||||
<string name="hide_link_card">Hide Link Card Status</string>
|
||||
<string name="hide_link_card_summary">Hide link card information on the home page</string>
|
||||
<string name="hide_tag_card">Hide module label rows</string>
|
||||
@@ -235,10 +237,10 @@
|
||||
<string name="kpm_install_mode_description">Please select: %1\$s Module Installation Mode \n\nLoad: Temporarily load the module \nEmbedded: Permanently install into the system</string>
|
||||
<string name="snackbar_failed_to_check_module_file">Unable to check if module file exists</string>
|
||||
<string name="theme_color">Theme Color</string>
|
||||
<string name="invalid_file_type">Incorrect file type! Please select .kpm file.</string>
|
||||
<string name="invalid_file_type">Incorrect file type! Please select .kpm file</string>
|
||||
<string name="confirm_uninstall_title_with_filename">Uninstall</string>
|
||||
<string name="confirm_uninstall_content">The following KPM will be uninstalled: %s</string>
|
||||
<string name="settings_susfs_toggle_summary">Disable kprobe hooks created by KernelSU, using inline hooks instead, which is similar to non-GKI kernel hooking method.</string>
|
||||
<string name="settings_susfs_toggle_summary">Disable kprobes hook created by KernelSU, using inline hooks instead, which is similar to non-GKI kernel hooking method</string>
|
||||
<string name="image_editor_hint">Use two fingers to zoom the image, and one finger to drag it to adjust the position</string>
|
||||
<string name="reprovision">Reprovision</string>
|
||||
<!-- Kernel Flash Progress Related -->
|
||||
@@ -288,7 +290,7 @@
|
||||
<string name="background_set_success">Background set successfully</string>
|
||||
<string name="background_removed">Removed custom backgrounds</string>
|
||||
<string name="icon_switch_title">Alternate icon</string>
|
||||
<string name="icon_switch_summary">Change the launcher icon to KernelSU\'s icon.</string>
|
||||
<string name="icon_switch_summary">Change the launcher icon to KernelSU\'s icon</string>
|
||||
<string name="icon_switched">Icon switched</string>
|
||||
<!-- KPM display settings -->
|
||||
<string name="show_kpm_info">Hides KPM Function</string>
|
||||
@@ -299,15 +301,15 @@
|
||||
<string name="engine_force_webuix">Force the use of WebUI X</string>
|
||||
<string name="engine_force_ksu">Mandatory use of KSU WebUI</string>
|
||||
<string name="use_webuix_eruda">Inject Eruda into WebUI X</string>
|
||||
<string name="use_webuix_eruda_summary">Inject a debug console into WebUI X to make debugging easier. Requires web debugging to be on.</string>
|
||||
<string name="use_webuix_eruda_summary">Inject a debug console into WebUI X to make debugging easier. Requires web debugging to be on</string>
|
||||
<!-- DPI setting related strings -->
|
||||
<string name="app_dpi_title">Applied DPI</string>
|
||||
<string name="app_dpi_summary">Adjust the screen display density for the current application only</string>
|
||||
<string name="dpi_size_small">Small </string>
|
||||
<string name="dpi_size_medium">Medium </string>
|
||||
<string name="dpi_size_large">Big</string>
|
||||
<string name="dpi_size_extra_large">oversize</string>
|
||||
<string name="dpi_size_custom">customizable</string>
|
||||
<string name="dpi_size_extra_large">Oversize</string>
|
||||
<string name="dpi_size_custom">Customizable</string>
|
||||
<string name="dpi_apply_settings">Applying DPI settings</string>
|
||||
<string name="dpi_confirm_title">Confirm DPI change</string>
|
||||
<string name="dpi_confirm_message">Are you sure you want to change the application DPI from %1$d to %2$d?</string>
|
||||
@@ -333,11 +335,11 @@
|
||||
<!-- 排序相关 -->
|
||||
<string name="sort_name_asc">Ascending order of name</string>
|
||||
<string name="sort_name_desc">Name descending</string>
|
||||
<string name="sort_install_time_new">Installation time (new)</string>
|
||||
<string name="sort_install_time_old">Installation time (old)</string>
|
||||
<string name="sort_size_desc">descending order of size</string>
|
||||
<string name="sort_size_asc">ascending order of size</string>
|
||||
<string name="sort_usage_freq">frequency of use</string>
|
||||
<string name="sort_install_time_new">Installation time (New)</string>
|
||||
<string name="sort_install_time_old">Installation time (Old)</string>
|
||||
<string name="sort_size_desc">Descending order of size</string>
|
||||
<string name="sort_size_asc">Ascending order of size</string>
|
||||
<string name="sort_usage_freq">Frequency of use</string>
|
||||
<!-- 状态相关 -->
|
||||
<string name="no_apps_in_category">No application in this category</string>
|
||||
<!-- 标签相关 -->
|
||||
@@ -350,7 +352,7 @@
|
||||
<string name="scroll_to_top">Top</string>
|
||||
<string name="scroll_to_bottom">Bottom</string>
|
||||
<string name="selected">Selected</string>
|
||||
<string name="select">option</string>
|
||||
<string name="select">Select</string>
|
||||
<!-- BottomSheet相关 -->
|
||||
<string name="menu_options">Menu Options</string>
|
||||
<string name="sort_options">Sort by</string>
|
||||
@@ -358,7 +360,7 @@
|
||||
<!-- SuSFS Configuration -->
|
||||
<string name="susfs_config_title">SuSFS Configuration</string>
|
||||
<string name="susfs_config_description">Configuration Description</string>
|
||||
<string name="susfs_config_description_text">This feature allows you to customize the SuSFS uname value and build time spoofing. Enter the values you want to set and click Apply to take effect.</string>
|
||||
<string name="susfs_config_description_text">This feature allows you to customize the SuSFS uname value and build time spoofing. Enter the values you want to set and click Apply to take effect</string>
|
||||
<string name="susfs_uname_label">Uname Value</string>
|
||||
<string name="susfs_uname_placeholder">Please enter custom uname value</string>
|
||||
<string name="susfs_build_time_label">Build Time Spoofing</string>
|
||||
@@ -492,7 +494,7 @@
|
||||
<string name="add_kstat_path_title">Add Kstat Path</string>
|
||||
<string name="add">Add</string>
|
||||
<string name="reset_kstat_config_title">Reset Kstat Configuration</string>
|
||||
<string name="reset_kstat_config_message">Are you sure you want to clear all Kstat configurations? This action cannot be undone.</string>
|
||||
<string name="reset_kstat_config_message">Are you sure you want to clear all Kstat configurations? This action cannot be undone</string>
|
||||
<string name="kstat_config_description_title">Kstat Configuration Description</string>
|
||||
<string name="kstat_config_description_add_statically">• add_sus_kstat_statically: Static stat info of files/directories</string>
|
||||
<string name="kstat_config_description_add">• add_sus_kstat: Add path before bind mount, storing original stat info</string>
|
||||
@@ -500,7 +502,7 @@
|
||||
<string name="kstat_config_description_update_full_clone">• update_sus_kstat_full_clone: Update ino only, keep other original values</string>
|
||||
<string name="static_kstat_config">Static Kstat Configuration</string>
|
||||
<string name="kstat_path_management">Kstat Path Management</string>
|
||||
<string name="no_kstat_config_message">No Kstat configuration yet, click the button above to add</string>
|
||||
<string name="no_kstat_config_message">No Kstat configuration yet, click the button below to add</string>
|
||||
<!-- SuSFS Mount Hiding Control Related Strings -->
|
||||
<string name="susfs_hide_mounts_control_title">SUS Mount Hiding Control</string>
|
||||
<string name="susfs_hide_mounts_control_description">Control the hiding behavior of SUS mounts for processes</string>
|
||||
@@ -521,7 +523,7 @@
|
||||
<string name="susfs_path_setup_warning">Path setup may not be fully successful, but SUS paths will continue to be added</string>
|
||||
<!-- 备份和还原相关字符串 -->
|
||||
<string name="susfs_backup_title">Backup</string>
|
||||
<string name="susfs_backup_description">Create a backup of all SuSFS configurations. The backup file will include all settings, paths, and configurations.</string>
|
||||
<string name="susfs_backup_description">Create a backup of all SuSFS configurations. The backup file will include all settings, paths, and configurations</string>
|
||||
<string name="susfs_backup_create">Create Backup</string>
|
||||
<string name="susfs_backup_success">Backup created successfully: %s</string>
|
||||
<string name="susfs_backup_failed">Backup creation failed: %s</string>
|
||||
@@ -529,7 +531,7 @@
|
||||
<string name="susfs_backup_invalid_format">Invalid backup file format</string>
|
||||
<string name="susfs_backup_version_mismatch">Backup version mismatch, but will attempt to restore</string>
|
||||
<string name="susfs_restore_title">Restore</string>
|
||||
<string name="susfs_restore_description">Restore SuSFS configurations from a backup file. This will overwrite all current settings.</string>
|
||||
<string name="susfs_restore_description">Restore SuSFS configurations from a backup file. This will overwrite all current settings</string>
|
||||
<string name="susfs_restore_select_file">Select Backup File</string>
|
||||
<string name="susfs_restore_success" formatted="false">Configuration restored successfully from backup created on %s from device: %s</string>
|
||||
<string name="susfs_restore_failed">Restore failed: %s</string>
|
||||
@@ -539,10 +541,10 @@
|
||||
<string name="susfs_backup_info_date">Backup Date: %s</string>
|
||||
<string name="susfs_backup_info_device">Device: %s</string>
|
||||
<string name="susfs_backup_info_version">Version: %s</string>
|
||||
<string name="hide_bl_script">Hide BL Script</string>
|
||||
<string name="hide_bl_script_description">Enable Hide Bootloader Unlock Status Scripts</string>
|
||||
<string name="hide_bl_script">Lock state</string>
|
||||
<string name="hide_bl_script_description">Overwrite bootloader locking status attribute in late_start service mode</string>
|
||||
<string name="cleanup_residue">Cleanup Residue</string>
|
||||
<string name="cleanup_residue_description">Clean up the residual files and directories of various modules and tools (may be deleted by mistake, resulting in loss and failure to start, use with caution)</string>
|
||||
<string name="cleanup_residue_description">Clean up the residual files and directories of various modules and tools (May be deleted by mistake, resulting in loss and failure to start, use with caution)</string>
|
||||
<string name="susfs_edit_sus_path">Edit SUS Path</string>
|
||||
<string name="susfs_edit_sus_mount">Edit SUS Mount</string>
|
||||
<string name="susfs_edit_try_umount">Edit Try Umount</string>
|
||||
@@ -559,4 +561,73 @@
|
||||
<string name="umount_zygote_iso_service_description">Enable this option to unmount Zygote isolation service mount points at system startup</string>
|
||||
<string name="umount_zygote_iso_service_enabled">Zygote isolation service unmount enabled</string>
|
||||
<string name="umount_zygote_iso_service_disabled">Zygote isolation service unmount disabled</string>
|
||||
<string name="app_paths_section">Application Path</string>
|
||||
<string name="other_paths_section">Other paths</string>
|
||||
<string name="add_custom_path">Other</string>
|
||||
<string name="add_app_path">App</string>
|
||||
<string name="susfs_add_app_path">Add App Path</string>
|
||||
<string name="search_apps">Search Apps</string>
|
||||
<string name="selected_apps_count">%1$d apps selected</string>
|
||||
<string name="already_added_apps_count">%1$d apps already added</string>
|
||||
<string name="all_apps_already_added">All apps have been added</string>
|
||||
<string name="dynamic_manager_title">Dynamic Manager Configuration</string>
|
||||
<string name="dynamic_manager_enabled_summary">Enabled (Size: %s)</string>
|
||||
<string name="dynamic_manager_disabled">Disabled</string>
|
||||
<string name="enable_dynamic_manager">Enable Dynamic Manager</string>
|
||||
<string name="signature_size">Dynamic Manager Signature Size</string>
|
||||
<string name="signature_hash">Dynamic Manager Signature Hash</string>
|
||||
<string name="hash_must_be_64_chars">Hash must be 64 hexadecimal characters</string>
|
||||
<string name="dynamic_manager_set_success">Dynamic Manager configuration set successfully</string>
|
||||
<string name="dynamic_manager_set_failed">Failed to set dynamic Manager configuration</string>
|
||||
<string name="invalid_sign_config">Invalid Manager configuration</string>
|
||||
<string name="dynamic_manager_disabled_success">Dynamic Manager disabled</string>
|
||||
<string name="dynamic_manager_clear_failed">Failed to clear dynamic Manager</string>
|
||||
<string name="dynamic_managerature">Dynamic</string>
|
||||
<string name="signature_index">Signature %1$d</string>
|
||||
<string name="unknown_signature">Unknown</string>
|
||||
<string name="multi_manager_list">Active Manager</string>
|
||||
<string name="no_active_manager">No active manager</string>
|
||||
<string name="default_signature">SukiSU</string>
|
||||
<string name="home_zygisk_implement">Zygisk implement</string>
|
||||
<!-- 循环路径相关 -->
|
||||
<string name="susfs_tab_sus_loop_paths">SUS Loop Paths</string>
|
||||
<string name="susfs_add_sus_loop_path">Add SUS Loop Path</string>
|
||||
<string name="susfs_edit_sus_loop_path">Edit SUS Loop Path</string>
|
||||
<string name="susfs_loop_path_added_success">SUS loop path added successfully: %1$s</string>
|
||||
<string name="susfs_loop_path_removed">SUS loop path removed: %1$s</string>
|
||||
<string name="susfs_loop_path_updated">SUS loop path updated: %1$s -> %2$s</string>
|
||||
<string name="susfs_no_loop_paths_configured">No SUS loop paths configured</string>
|
||||
<string name="susfs_reset_loop_paths_title">Reset Loop Paths</string>
|
||||
<string name="susfs_reset_loop_paths_message">Are you sure you want to clear all SUS loop paths? This action cannot be undone</string>
|
||||
<string name="susfs_loop_path_label">Loop Path</string>
|
||||
<string name="susfs_loop_path_placeholder">/data/example/path</string>
|
||||
<string name="susfs_loop_path_restriction_warning">Note: Only paths NOT inside /storage/ and /sdcard/ can be added via loop paths</string>
|
||||
<string name="susfs_loop_path_invalid_location">Error: Loop paths cannot be inside /storage/ or /sdcard/ directories</string>
|
||||
<string name="loop_paths_section">Loop Paths</string>
|
||||
<string name="add_loop_path">Add Loop Path</string>
|
||||
<!-- 循环路径功能描述 -->
|
||||
<string name="sus_loop_path_feature_label">SUS Loop Path</string>
|
||||
<string name="sus_loop_paths_description_title">Loop Path Configuration</string>
|
||||
<string name="sus_loop_paths_description_text">Loop paths are re-flagged as SUS_PATH on each non-root user app or isolated service startup. This helps address issues where added paths may have their inode status reset or inode re-created in the kernel</string>
|
||||
<string name="avc_log_spoofing">AVC Log Spoofing</string>
|
||||
<string name="avc_log_spoofing_enabled">AVC log spoofing has been enabled</string>
|
||||
<string name="avc_log_spoofing_disabled">AVC log spoofing has been disabled</string>
|
||||
<string name="avc_log_spoofing_description">
|
||||
Disabled: Disable spoofing the sus tcontext of \'su\' shown in avc log in kernel\n
|
||||
Enabled: Enable spoofing the sus tcontext of \'su\' with \'kernel\' shown in avc log in kernel
|
||||
</string>
|
||||
<string name="avc_log_spoofing_warning">
|
||||
Important Note:\n
|
||||
- It is set to \'0\' by default in kernel\n
|
||||
- Enabling this will sometimes make developers hard to identify the cause when they are debugging with some permission or SELinux issue, so users are advised to disable this when doing
|
||||
</string>
|
||||
<!-- 模块签名功能描述 -->
|
||||
<string name="module_verified">Validated</string>
|
||||
<string name="module_signature_verified">Module signature verified</string>
|
||||
<string name="module_signature_verification">Signature Verification</string>
|
||||
<string name="module_signature_verification_summary">Force signature verification when installing modules (Only available for ARM architecture)</string>
|
||||
<string name="module_signature_invalid">Unknown publisher</string>
|
||||
<string name="module_signature_invalid_message">Unsigned modules may be incomplete. To protect your device, installation of this module has been blocked</string>
|
||||
<string name="module_signature_verification_failed">Unsigned modules may be incomplete. Do you want to allow the following module from an unknown publisher to install in this device?</string>
|
||||
<string name="home_hook_type">Hook type</string>
|
||||
</resources>
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
[versions]
|
||||
agp = "8.11.0"
|
||||
gson = "2.11.0"
|
||||
kotlin = "2.1.20"
|
||||
ksp = "2.1.20-2.0.0"
|
||||
compose-bom = "2025.05.01"
|
||||
lifecycle = "2.9.0"
|
||||
navigation = "2.9.0"
|
||||
accompanist-drawablepainter = "0.37.3"
|
||||
agp = "8.12.0"
|
||||
gson = "2.13.1"
|
||||
kotlin = "2.1.21"
|
||||
ksp = "2.1.21-2.0.1"
|
||||
compose-bom = "2025.07.00"
|
||||
lifecycle = "2.9.2"
|
||||
navigation = "2.9.3"
|
||||
activity-compose = "1.10.1"
|
||||
kotlinx-coroutines = "1.10.2"
|
||||
coil-compose = "2.7.0"
|
||||
compose-destination = "2.1.0"
|
||||
compose-destination = "2.2.0"
|
||||
sheets-compose-dialogs = "1.3.0"
|
||||
markdown = "4.6.2"
|
||||
webkit = "1.13.0"
|
||||
webkit = "1.14.0"
|
||||
appiconloader-coil = "1.5.0"
|
||||
parcelablelist = "2.0.1"
|
||||
libsu = "6.0.0"
|
||||
apksign = "1.4"
|
||||
cmaker = "1.2"
|
||||
compose-material = "1.8.2"
|
||||
compose-material = "1.8.3"
|
||||
compose-material3 = "1.3.2"
|
||||
compose-ui = "1.8.2"
|
||||
compose-ui = "1.8.3"
|
||||
documentfile = "1.1.0"
|
||||
mmrl = "2bb00b3c2b"
|
||||
|
||||
@@ -37,6 +38,7 @@ lsplugin-apksign = { id = "org.lsposed.lsplugin.apksign", version.ref = "apksign
|
||||
lsplugin-cmaker = { id = "org.lsposed.lsplugin.cmaker", version.ref = "cmaker" }
|
||||
|
||||
[libraries]
|
||||
accompanist-drawablepainter = { module = "com.google.accompanist:accompanist-drawablepainter", version.ref = "accompanist-drawablepainter" }
|
||||
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activity-compose" }
|
||||
|
||||
androidx-foundation = { module = "androidx.compose.foundation:foundation" }
|
||||
@@ -79,7 +81,7 @@ sheet-compose-dialogs-input = { group = "com.maxkeppeler.sheets-compose-dialogs"
|
||||
|
||||
markdown = { group = "io.noties.markwon", name = "core", version.ref = "markdown" }
|
||||
|
||||
lsposed-cxx = { module = "org.lsposed.libcxx:libcxx", version = "27.0.12077973" }
|
||||
lsposed-cxx = { module = "org.lsposed.libcxx:libcxx", version = "29.0.13599879-beta2" }
|
||||
androidx-documentfile = { group = "androidx.documentfile", name = "documentfile", version.ref = "documentfile" }
|
||||
|
||||
|
||||
|
||||
172
userspace/ksud/Cargo.lock
generated
172
userspace/ksud/Cargo.lock
generated
@@ -29,7 +29,7 @@ version = "0.8.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"once_cell",
|
||||
"version_check",
|
||||
"zerocopy",
|
||||
@@ -61,9 +61,9 @@ checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d"
|
||||
|
||||
[[package]]
|
||||
name = "android_logger"
|
||||
version = "0.14.1"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05b07e8e73d720a1f2e4b6014766e6039fd2e96a4fa44e2a78d0e1fa2ff49826"
|
||||
checksum = "f6f39be698127218cca460cb624878c9aa4e2b47dba3b277963d2bf00bad263b"
|
||||
dependencies = [
|
||||
"android_log-sys",
|
||||
"env_filter",
|
||||
@@ -168,7 +168,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
|
||||
dependencies = [
|
||||
"addr2line",
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"miniz_oxide",
|
||||
"object",
|
||||
@@ -184,9 +184,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.9.0"
|
||||
version = "2.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd"
|
||||
checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
@@ -224,6 +224,12 @@ dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
@@ -246,9 +252,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.37"
|
||||
version = "4.5.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eccb054f56cbd38340b380d4a8e69ef1f02f1af43db2f0cc817a4774d80ae071"
|
||||
checksum = "ed93b9805f8ba930df42c2590f05453d5ec36cbb85d018868a5b24d31f6ac000"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@@ -256,9 +262,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.37"
|
||||
version = "4.5.38"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efd9466fac8543255d3b1fcad4762c5e116ffe808c8a3043d4263cd4fd4862a2"
|
||||
checksum = "379026ff283facf611b0ea629334361c4211d1b12ee01024eec1591133b04120"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@@ -355,7 +361,7 @@ version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -489,7 +495,7 @@ version = "0.8.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -536,9 +542,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "errno"
|
||||
version = "0.3.11"
|
||||
version = "0.3.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "976dd42dc7e85965fe702eb8164f21f450704bdde31faefd6471dba214cb594e"
|
||||
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
@@ -578,9 +584,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"libz-rs-sys",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fs4"
|
||||
version = "0.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8640e34b88f7652208ce9e88b1a37a2ae95227d84abec377ccd3c5cfeb141ed4"
|
||||
dependencies = [
|
||||
"rustix 1.0.7",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
@@ -606,7 +623,7 @@ version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasi",
|
||||
@@ -630,9 +647,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.3"
|
||||
version = "0.15.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3"
|
||||
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
@@ -646,6 +663,19 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "hole-punch"
|
||||
version = "0.0.4-alpha.0"
|
||||
source = "git+https://github.com/tiann/hole-punch#11ab7a61bfb98682b72fd7f58a47d8e5d997328e"
|
||||
dependencies = [
|
||||
"cfg-if 0.1.10",
|
||||
"errno 0.2.8",
|
||||
"libc",
|
||||
"memmap",
|
||||
"thiserror",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "humansize"
|
||||
version = "2.1.3"
|
||||
@@ -709,7 +739,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown 0.15.3",
|
||||
"hashbrown 0.15.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -777,13 +807,16 @@ dependencies = [
|
||||
"encoding_rs",
|
||||
"env_logger",
|
||||
"extattr",
|
||||
"fs4",
|
||||
"getopts",
|
||||
"hole-punch",
|
||||
"humansize",
|
||||
"is_executable",
|
||||
"java-properties",
|
||||
"jwalk",
|
||||
"libc",
|
||||
"log",
|
||||
"loopdev",
|
||||
"nom",
|
||||
"procfs",
|
||||
"regex-lite",
|
||||
@@ -840,6 +873,15 @@ version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
|
||||
|
||||
[[package]]
|
||||
name = "libz-rs-sys"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "172a788537a2221661b480fee8dc5f96c580eb34fa88764d3205dc356c7e4221"
|
||||
dependencies = [
|
||||
"zlib-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.4.15"
|
||||
@@ -858,6 +900,15 @@ version = "0.4.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
|
||||
|
||||
[[package]]
|
||||
name = "loopdev"
|
||||
version = "0.5.0"
|
||||
source = "git+https://github.com/Kernel-SU/loopdev#7a921f8d966477a645b1188732fac486c71a68ef"
|
||||
dependencies = [
|
||||
"errno 0.2.8",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lzma-rs"
|
||||
version = "0.3.0"
|
||||
@@ -885,6 +936,16 @@ version = "2.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
|
||||
|
||||
[[package]]
|
||||
name = "memmap"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.8"
|
||||
@@ -966,7 +1027,7 @@ version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cc5b72d8145275d844d4b5f6d4e1eef00c8cd889edb6035c21675d1bb1f45c9f"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"bitflags 2.8.0",
|
||||
"chrono",
|
||||
"flate2",
|
||||
"hex",
|
||||
@@ -980,7 +1041,7 @@ version = "0.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "239df02d8349b06fc07398a3a1697b06418223b1c7725085e801e7c0fc6a12ec"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"bitflags 2.8.0",
|
||||
"chrono",
|
||||
"hex",
|
||||
]
|
||||
@@ -1034,9 +1095,9 @@ checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422"
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "8.7.1"
|
||||
version = "8.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60e425e204264b144d4c929d126d0de524b40a961686414bab5040f7465c71be"
|
||||
checksum = "025908b8682a26ba8d12f6f2d66b987584a4a87bc024abc5bbc12553a8cd178a"
|
||||
dependencies = [
|
||||
"include-flate",
|
||||
"rust-embed-impl",
|
||||
@@ -1046,9 +1107,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-impl"
|
||||
version = "8.7.0"
|
||||
version = "8.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bf418c9a2e3f6663ca38b8a7134cc2c2167c9d69688860e8961e3faa731702e"
|
||||
checksum = "6065f1a4392b71819ec1ea1df1120673418bf386f50de1d6f54204d836d4349c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1059,9 +1120,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-utils"
|
||||
version = "8.7.0"
|
||||
version = "8.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d55b95147fe01265d06b3955db798bdaed52e60e2211c41137701b3aba8e21"
|
||||
checksum = "f6cc0c81648b20b70c491ff8cce00c1c3b223bb8ed2b5d41f0e54c6c4c0a3594"
|
||||
dependencies = [
|
||||
"sha2",
|
||||
"walkdir",
|
||||
@@ -1078,8 +1139,8 @@ name = "rustix"
|
||||
version = "0.38.34"
|
||||
source = "git+https://github.com/Kernel-SU/rustix.git?branch=main#4a53fbc7cb7a07cabe87125cc21dbc27db316259"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"errno 0.3.11",
|
||||
"bitflags 2.8.0",
|
||||
"errno 0.3.10",
|
||||
"itoa",
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.15",
|
||||
@@ -1093,8 +1154,8 @@ version = "0.38.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"errno 0.3.11",
|
||||
"bitflags 2.8.0",
|
||||
"errno 0.3.10",
|
||||
"libc",
|
||||
"linux-raw-sys 0.4.15",
|
||||
"windows-sys 0.59.0",
|
||||
@@ -1106,8 +1167,8 @@ version = "1.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"errno 0.3.11",
|
||||
"bitflags 2.8.0",
|
||||
"errno 0.3.10",
|
||||
"libc",
|
||||
"linux-raw-sys 0.9.4",
|
||||
"windows-sys 0.59.0",
|
||||
@@ -1172,7 +1233,7 @@ version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
@@ -1183,7 +1244,7 @@ version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
@@ -1232,9 +1293,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.19.1"
|
||||
version = "3.20.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf"
|
||||
checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
@@ -1243,6 +1304,26 @@ dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.69"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "time"
|
||||
version = "0.3.41"
|
||||
@@ -1334,7 +1415,7 @@ version = "0.2.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cfg-if 1.0.0",
|
||||
"once_cell",
|
||||
"rustversion",
|
||||
"wasm-bindgen-macro",
|
||||
@@ -1582,7 +1663,7 @@ version = "0.39.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
|
||||
dependencies = [
|
||||
"bitflags 2.9.0",
|
||||
"bitflags 2.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1616,13 +1697,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
version = "2.6.1"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1dcb24d0152526ae49b9b96c1dcf71850ca1e0b882e4e28ed898a93c41334744"
|
||||
checksum = "12598812502ed0105f607f941c386f43d441e00148fce9dec3ca5ffb0bde9308"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"crc32fast",
|
||||
"crossbeam-utils",
|
||||
"deflate64",
|
||||
"flate2",
|
||||
"indexmap",
|
||||
@@ -1635,13 +1715,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zip-extensions"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79cdbf826e5a6eec81fc5a0d33cd7c09c31fd8f9918f15434f74c42d39ef337a"
|
||||
checksum = "9f105becb0a5da773e655775dd05fee454ca1475bcc980ec9d940a02f42cee40"
|
||||
dependencies = [
|
||||
"zip",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zlib-rs"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "626bd9fa9734751fc50d6060752170984d7053f5a39061f524cda68023d4db8a"
|
||||
|
||||
[[package]]
|
||||
name = "zopfli"
|
||||
version = "0.8.2"
|
||||
|
||||
Binary file not shown.
BIN
userspace/ksud/bin/arm/resetprop
Normal file → Executable file
BIN
userspace/ksud/bin/arm/resetprop
Normal file → Executable file
Binary file not shown.
@@ -23,12 +23,7 @@ fn get_git_version() -> Result<(u32, String), std::io::Error> {
|
||||
.output()?
|
||||
.stdout,
|
||||
)
|
||||
.map_err(|_| {
|
||||
std::io::Error::new(
|
||||
std::io::ErrorKind::Other,
|
||||
"Failed to read git describe stdout",
|
||||
)
|
||||
})?;
|
||||
.map_err(|_| std::io::Error::other("Failed to read git describe stdout"))?;
|
||||
let version_name = version_name.trim_start_matches('v').to_string();
|
||||
Ok((version_code, version_name))
|
||||
}
|
||||
|
||||
@@ -153,82 +153,40 @@ fn parse_kmi_from_boot(magiskboot: &Path, image: &PathBuf, workdir: &Path) -> Re
|
||||
parse_kmi_from_kernel(&image_path, workdir)
|
||||
}
|
||||
|
||||
fn do_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> {
|
||||
fn do_cpio_cmd(magiskboot: &Path, workdir: &Path, cpio_path: &Path, cmd: &str) -> Result<()> {
|
||||
let status = Command::new(magiskboot)
|
||||
.current_dir(workdir)
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.arg("cpio")
|
||||
.arg("ramdisk.cpio")
|
||||
.arg(cpio_path)
|
||||
.arg(cmd)
|
||||
.status()?;
|
||||
|
||||
ensure!(status.success(), "magiskboot cpio {} failed", cmd);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_vendor_init_boot_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> {
|
||||
let vendor_init_boot_cpio = workdir.join("vendor_ramdisk").join("init_boot.cpio");
|
||||
fn is_magisk_patched(magiskboot: &Path, workdir: &Path, cpio_path: &Path) -> Result<bool> {
|
||||
let status = Command::new(magiskboot)
|
||||
.current_dir(workdir)
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.arg("cpio")
|
||||
.arg(vendor_init_boot_cpio)
|
||||
.arg(cmd)
|
||||
.arg(cpio_path)
|
||||
.arg("test")
|
||||
.status()?;
|
||||
|
||||
ensure!(status.success(), "magiskboot cpio {} failed", cmd);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_magisk_patched(magiskboot: &Path, workdir: &Path) -> Result<bool> {
|
||||
let status = Command::new(magiskboot)
|
||||
.current_dir(workdir)
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.args(["cpio", "ramdisk.cpio", "test"])
|
||||
.status()?;
|
||||
|
||||
// 0: stock, 1: magisk
|
||||
Ok(status.code() == Some(1))
|
||||
}
|
||||
|
||||
fn is_magisk_patched_vendor_init_boot(magiskboot: &Path, workdir: &Path) -> Result<bool> {
|
||||
let vendor_init_boot_cpio = workdir.join("vendor_ramdisk").join("init_boot.cpio");
|
||||
fn is_kernelsu_patched(magiskboot: &Path, workdir: &Path, cpio_path: &Path) -> Result<bool> {
|
||||
let status = Command::new(magiskboot)
|
||||
.current_dir(workdir)
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.args(["cpio", vendor_init_boot_cpio.to_str().unwrap(), "test"])
|
||||
.status()?;
|
||||
|
||||
// 0: stock, 1: magisk
|
||||
Ok(status.code() == Some(1))
|
||||
}
|
||||
|
||||
fn is_kernelsu_patched(magiskboot: &Path, workdir: &Path) -> Result<bool> {
|
||||
let status = Command::new(magiskboot)
|
||||
.current_dir(workdir)
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.args(["cpio", "ramdisk.cpio", "exists kernelsu.ko"])
|
||||
.status()?;
|
||||
|
||||
Ok(status.success())
|
||||
}
|
||||
|
||||
fn is_kernelsu_patched_vendor_init_boot(magiskboot: &Path, workdir: &Path) -> Result<bool> {
|
||||
let vendor_ramdisk_cpio = workdir.join("vendor_ramdisk").join("init_boot.cpio");
|
||||
let status = Command::new(magiskboot)
|
||||
.current_dir(workdir)
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.args([
|
||||
"cpio",
|
||||
vendor_ramdisk_cpio.to_str().unwrap(),
|
||||
"exists kernelsu.ko",
|
||||
])
|
||||
.arg("cpio")
|
||||
.arg(cpio_path)
|
||||
.arg("exists kernelsu.ko")
|
||||
.status()?;
|
||||
|
||||
Ok(status.success())
|
||||
@@ -278,24 +236,37 @@ pub fn restore(
|
||||
.status()?;
|
||||
ensure!(status.success(), "magiskboot unpack failed");
|
||||
|
||||
let no_ramdisk = !workdir.join("ramdisk.cpio").exists();
|
||||
let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workdir)?;
|
||||
let is_kernelsu_patched_vendor_init_boot =
|
||||
is_kernelsu_patched_vendor_init_boot(&magiskboot, workdir)?;
|
||||
ensure!(
|
||||
is_kernelsu_patched || is_kernelsu_patched_vendor_init_boot,
|
||||
"boot image is not patched by KernelSU"
|
||||
);
|
||||
let mut ramdisk = workdir.join("ramdisk.cpio");
|
||||
if !ramdisk.exists() {
|
||||
ramdisk = workdir.join("vendor_ramdisk").join("init_boot.cpio")
|
||||
}
|
||||
if !ramdisk.exists() {
|
||||
ramdisk = workdir.join("vendor_ramdisk").join("ramdisk.cpio");
|
||||
}
|
||||
if !ramdisk.exists() {
|
||||
bail!("No compatible ramdisk found.")
|
||||
}
|
||||
let ramdisk = ramdisk.as_path();
|
||||
let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workdir, ramdisk)?;
|
||||
ensure!(is_kernelsu_patched, "boot image is not patched by KernelSU");
|
||||
|
||||
let mut new_boot = None;
|
||||
let mut from_backup = false;
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
if do_cpio_cmd(&magiskboot, workdir, &format!("exists {BACKUP_FILENAME}")).is_ok() {
|
||||
if do_cpio_cmd(
|
||||
&magiskboot,
|
||||
workdir,
|
||||
ramdisk,
|
||||
&format!("exists {BACKUP_FILENAME}"),
|
||||
)
|
||||
.is_ok()
|
||||
{
|
||||
do_cpio_cmd(
|
||||
&magiskboot,
|
||||
workdir,
|
||||
&format!("extract {0} {0}", BACKUP_FILENAME),
|
||||
ramdisk,
|
||||
&format!("extract {BACKUP_FILENAME} {BACKUP_FILENAME}"),
|
||||
)?;
|
||||
let sha = std::fs::read(workdir.join(BACKUP_FILENAME))?;
|
||||
let sha = String::from_utf8(sha)?;
|
||||
@@ -317,29 +288,13 @@ pub fn restore(
|
||||
}
|
||||
|
||||
if new_boot.is_none() {
|
||||
if no_ramdisk {
|
||||
// vendor init_boot restore
|
||||
do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?;
|
||||
// if init.real exists, restore it
|
||||
let status =
|
||||
do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "exists init.real").is_ok();
|
||||
if status {
|
||||
do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "mv init.real init")?;
|
||||
} else {
|
||||
let vendor_init_boot = workdir.join("vendor_ramdisk").join("init_boot.cpio");
|
||||
std::fs::remove_file(vendor_init_boot)?;
|
||||
}
|
||||
} else {
|
||||
// remove kernelsu.ko
|
||||
do_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?;
|
||||
do_cpio_cmd(&magiskboot, workdir, ramdisk, "rm kernelsu.ko")?;
|
||||
|
||||
// if init.real exists, restore it
|
||||
let status = do_cpio_cmd(&magiskboot, workdir, "exists init.real").is_ok();
|
||||
let status = do_cpio_cmd(&magiskboot, workdir, ramdisk, "exists init.real").is_ok();
|
||||
if status {
|
||||
do_cpio_cmd(&magiskboot, workdir, "mv init.real init")?;
|
||||
} else {
|
||||
let ramdisk = workdir.join("ramdisk.cpio");
|
||||
std::fs::remove_file(ramdisk)?;
|
||||
}
|
||||
do_cpio_cmd(&magiskboot, workdir, ramdisk, "mv init.real init")?;
|
||||
}
|
||||
|
||||
println!("- Repacking boot image");
|
||||
@@ -348,7 +303,7 @@ pub fn restore(
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.arg("repack")
|
||||
.arg(bootimage.display().to_string())
|
||||
.arg(&bootimage)
|
||||
.status()?;
|
||||
ensure!(status.success(), "magiskboot repack failed");
|
||||
new_boot = Some(workdir.join("new-boot.img"));
|
||||
@@ -447,7 +402,7 @@ fn do_patch(
|
||||
match get_current_kmi() {
|
||||
Ok(value) => value,
|
||||
Err(e) => {
|
||||
println!("- {}", e);
|
||||
println!("- {e}");
|
||||
if let Some(image_path) = &image {
|
||||
println!(
|
||||
"- Trying to auto detect KMI version for {}",
|
||||
@@ -472,7 +427,7 @@ fn do_patch(
|
||||
let (bootimage, bootdevice) =
|
||||
find_boot_image(&image, skip_init, ota, is_replace_kernel, workdir)?;
|
||||
|
||||
let bootimage = bootimage.display().to_string();
|
||||
let bootimage = bootimage.as_path();
|
||||
|
||||
// try extract magiskboot/bootctl
|
||||
let _ = assets::ensure_binaries(false);
|
||||
@@ -501,71 +456,54 @@ fn do_patch(
|
||||
assets::copy_assets_to_file("ksuinit", init_file).context("copy ksuinit failed")?;
|
||||
}
|
||||
|
||||
// magiskboot unpack boot.img
|
||||
// magiskboot cpio ramdisk.cpio 'cp init init.real'
|
||||
// magiskboot cpio ramdisk.cpio 'add 0755 ksuinit init'
|
||||
// magiskboot cpio ramdisk.cpio 'add 0755 <kmod> kernelsu.ko'
|
||||
|
||||
println!("- Unpacking boot image");
|
||||
let status = Command::new(&magiskboot)
|
||||
.current_dir(workdir)
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.arg("unpack")
|
||||
.arg(&bootimage)
|
||||
.arg(bootimage)
|
||||
.status()?;
|
||||
ensure!(status.success(), "magiskboot unpack failed");
|
||||
|
||||
let no_ramdisk = !workdir.join("ramdisk.cpio").exists();
|
||||
let no_vendor_init_boot = !workdir
|
||||
.join("vendor_ramdisk")
|
||||
.join("init_boot.cpio")
|
||||
.exists();
|
||||
if no_ramdisk && no_vendor_init_boot {
|
||||
let mut ramdisk = workdir.join("ramdisk.cpio");
|
||||
if !ramdisk.exists() {
|
||||
ramdisk = workdir.join("vendor_ramdisk").join("init_boot.cpio")
|
||||
}
|
||||
if !ramdisk.exists() {
|
||||
ramdisk = workdir.join("vendor_ramdisk").join("ramdisk.cpio");
|
||||
}
|
||||
if !ramdisk.exists() {
|
||||
bail!("No compatible ramdisk found.");
|
||||
}
|
||||
let is_magisk_patched = is_magisk_patched(&magiskboot, workdir)?;
|
||||
let is_magisk_patched_vendor_init_boot =
|
||||
is_magisk_patched_vendor_init_boot(&magiskboot, workdir)?;
|
||||
ensure!(
|
||||
!is_magisk_patched || !is_magisk_patched_vendor_init_boot,
|
||||
"Cannot work with Magisk patched image"
|
||||
);
|
||||
let ramdisk = ramdisk.as_path();
|
||||
let is_magisk_patched = is_magisk_patched(&magiskboot, workdir, ramdisk)?;
|
||||
ensure!(!is_magisk_patched, "Cannot work with Magisk patched image");
|
||||
|
||||
println!("- Adding KernelSU LKM");
|
||||
let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workdir)?;
|
||||
let is_kernelsu_patched_vendor_init_boot =
|
||||
is_kernelsu_patched_vendor_init_boot(&magiskboot, workdir)?;
|
||||
let is_kernelsu_patched = is_kernelsu_patched(&magiskboot, workdir, ramdisk)?;
|
||||
|
||||
let mut need_backup = false;
|
||||
if !is_kernelsu_patched || (no_ramdisk && !is_kernelsu_patched_vendor_init_boot) {
|
||||
if no_ramdisk {
|
||||
// vendor init_boot patching
|
||||
let status = do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "exists init");
|
||||
if status.is_ok() {
|
||||
do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "mv init init.real")?;
|
||||
}
|
||||
} else {
|
||||
if !is_kernelsu_patched {
|
||||
// kernelsu.ko is not exist, backup init if necessary
|
||||
let status = do_cpio_cmd(&magiskboot, workdir, "exists init");
|
||||
let status = do_cpio_cmd(&magiskboot, workdir, ramdisk, "exists init");
|
||||
if status.is_ok() {
|
||||
do_cpio_cmd(&magiskboot, workdir, "mv init init.real")?;
|
||||
do_cpio_cmd(&magiskboot, workdir, ramdisk, "mv init init.real")?;
|
||||
}
|
||||
need_backup = flash;
|
||||
}
|
||||
}
|
||||
|
||||
if no_ramdisk {
|
||||
do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "add 0755 init init")?;
|
||||
do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "add 0755 kernelsu.ko kernelsu.ko")?;
|
||||
} else {
|
||||
do_cpio_cmd(&magiskboot, workdir, "add 0755 init init")?;
|
||||
do_cpio_cmd(&magiskboot, workdir, "add 0755 kernelsu.ko kernelsu.ko")?;
|
||||
}
|
||||
do_cpio_cmd(&magiskboot, workdir, ramdisk, "add 0755 init init")?;
|
||||
do_cpio_cmd(
|
||||
&magiskboot,
|
||||
workdir,
|
||||
ramdisk,
|
||||
"add 0755 kernelsu.ko kernelsu.ko",
|
||||
)?;
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
if need_backup {
|
||||
if let Err(e) = do_backup(&magiskboot, workdir, &bootimage) {
|
||||
if let Err(e) = do_backup(&magiskboot, workdir, ramdisk, bootimage) {
|
||||
println!("- Backup stock image failed: {e}");
|
||||
}
|
||||
}
|
||||
@@ -577,7 +515,7 @@ fn do_patch(
|
||||
.stdout(Stdio::null())
|
||||
.stderr(Stdio::null())
|
||||
.arg("repack")
|
||||
.arg(&bootimage)
|
||||
.arg(bootimage)
|
||||
.status()?;
|
||||
ensure!(status.success(), "magiskboot repack failed");
|
||||
let new_boot = workdir.join("new-boot.img");
|
||||
@@ -587,7 +525,7 @@ fn do_patch(
|
||||
let output_dir = out.unwrap_or(std::env::current_dir()?);
|
||||
let now = chrono::Utc::now();
|
||||
let output_image = output_dir.join(format!(
|
||||
"SukiSU_patched_{}.img",
|
||||
"kernelsu_patched_{}.img",
|
||||
now.format("%Y%m%d_%H%M%S")
|
||||
));
|
||||
|
||||
@@ -628,11 +566,11 @@ fn calculate_sha1(file_path: impl AsRef<Path>) -> Result<String> {
|
||||
}
|
||||
|
||||
let result = hasher.finalize();
|
||||
Ok(format!("{:x}", result))
|
||||
Ok(format!("{result:x}"))
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
fn do_backup(magiskboot: &Path, workdir: &Path, image: &str) -> Result<()> {
|
||||
fn do_backup(magiskboot: &Path, workdir: &Path, cpio_path: &Path, image: &Path) -> Result<()> {
|
||||
let sha1 = calculate_sha1(image)?;
|
||||
let filename = format!("{KSU_BACKUP_FILE_PREFIX}{sha1}");
|
||||
|
||||
@@ -644,7 +582,8 @@ fn do_backup(magiskboot: &Path, workdir: &Path, image: &str) -> Result<()> {
|
||||
do_cpio_cmd(
|
||||
magiskboot,
|
||||
workdir,
|
||||
&format!("add 0755 {0} {0}", BACKUP_FILENAME),
|
||||
cpio_path,
|
||||
&format!("add 0755 {BACKUP_FILENAME} {BACKUP_FILENAME}"),
|
||||
)?;
|
||||
println!("- Stock image has been backup to");
|
||||
println!("- {target}");
|
||||
@@ -654,7 +593,7 @@ fn do_backup(magiskboot: &Path, workdir: &Path, image: &str) -> Result<()> {
|
||||
#[cfg(target_os = "android")]
|
||||
fn clean_backup(sha1: &str) -> Result<()> {
|
||||
println!("- Clean up backup");
|
||||
let backup_name = format!("{}{}", KSU_BACKUP_FILE_PREFIX, sha1);
|
||||
let backup_name = format!("{KSU_BACKUP_FILE_PREFIX}{sha1}");
|
||||
let dir = std::fs::read_dir(defs::KSU_BACKUP_DIR)?;
|
||||
for entry in dir.flatten() {
|
||||
let path = entry.path();
|
||||
|
||||
@@ -366,13 +366,13 @@ pub fn run() -> Result<()> {
|
||||
Commands::BootInfo { command } => match command {
|
||||
BootInfo::CurrentKmi => {
|
||||
let kmi = crate::boot_patch::get_current_kmi()?;
|
||||
println!("{}", kmi);
|
||||
println!("{kmi}");
|
||||
// return here to avoid printing the error message
|
||||
return Ok(());
|
||||
}
|
||||
BootInfo::SupportedKmi => {
|
||||
let kmi = crate::assets::list_supported_kmi()?;
|
||||
kmi.iter().for_each(|kmi| println!("{}", kmi));
|
||||
kmi.iter().for_each(|kmi| println!("{kmi}"));
|
||||
return Ok(());
|
||||
}
|
||||
},
|
||||
@@ -384,7 +384,7 @@ pub fn run() -> Result<()> {
|
||||
};
|
||||
|
||||
if let Err(e) = &result {
|
||||
log::error!("Error: {:?}", e);
|
||||
log::error!("Error: {e:?}");
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ fn set_kernel_param(uid: u32) -> Result<()> {
|
||||
fn get_pkg_uid(pkg: &str) -> Result<u32> {
|
||||
// stat /data/data/<pkg>
|
||||
let uid = rustix::fs::stat(format!("/data/data/{pkg}"))
|
||||
.with_context(|| format!("stat /data/data/{}", pkg))?
|
||||
.with_context(|| format!("stat /data/data/{pkg}"))?
|
||||
.st_uid;
|
||||
Ok(uid)
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ pub fn on_post_data_fs() -> Result<()> {
|
||||
} else {
|
||||
// Then exec common post-fs-data scripts
|
||||
if let Err(e) = crate::module::exec_common_scripts("post-fs-data.d", true) {
|
||||
warn!("exec common post-fs-data scripts failed: {}", e);
|
||||
warn!("exec common post-fs-data scripts failed: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ pub fn on_post_data_fs() -> Result<()> {
|
||||
if safe_mode {
|
||||
warn!("safe mode, skip post-fs-data scripts and disable all modules!");
|
||||
if let Err(e) = crate::module::disable_all_modules() {
|
||||
warn!("disable all modules failed: {}", e);
|
||||
warn!("disable all modules failed: {e}");
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
@@ -58,7 +58,7 @@ pub fn on_post_data_fs() -> Result<()> {
|
||||
}
|
||||
|
||||
if let Err(e) = restorecon::restorecon() {
|
||||
warn!("restorecon failed: {}", e);
|
||||
warn!("restorecon failed: {e}");
|
||||
}
|
||||
|
||||
// load sepolicy.rule
|
||||
@@ -67,7 +67,7 @@ pub fn on_post_data_fs() -> Result<()> {
|
||||
}
|
||||
|
||||
if let Err(e) = crate::profile::apply_sepolies() {
|
||||
warn!("apply root profile sepolicy failed: {}", e);
|
||||
warn!("apply root profile sepolicy failed: {e}");
|
||||
}
|
||||
|
||||
// mount temp dir
|
||||
@@ -88,12 +88,12 @@ pub fn on_post_data_fs() -> Result<()> {
|
||||
// exec modules post-fs-data scripts
|
||||
// TODO: Add timeout
|
||||
if let Err(e) = crate::module::exec_stage_script("post-fs-data", true) {
|
||||
warn!("exec post-fs-data scripts failed: {}", e);
|
||||
warn!("exec post-fs-data scripts failed: {e}");
|
||||
}
|
||||
|
||||
// load system.prop
|
||||
if let Err(e) = crate::module::load_system_prop() {
|
||||
warn!("load system.prop failed: {}", e);
|
||||
warn!("load system.prop failed: {e}");
|
||||
}
|
||||
|
||||
// mount module systemlessly by magic mount
|
||||
@@ -192,7 +192,7 @@ fn catch_bootlog(logname: &str, command: Vec<&str>) -> Result<()> {
|
||||
};
|
||||
|
||||
if let Err(e) = result {
|
||||
warn!("Failed to start logcat: {:#}", e);
|
||||
warn!("Failed to start logcat: {e:#}");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
||||
@@ -54,6 +54,7 @@ fn exec_install_script(module_file: &str) -> Result<()> {
|
||||
),
|
||||
)
|
||||
.env("KSU", "true")
|
||||
.env("KSU_SUKISU", "true")
|
||||
.env("KSU_KERNEL_VER_CODE", ksucalls::get_version().to_string())
|
||||
.env("KSU_VER", defs::VERSION_NAME)
|
||||
.env("KSU_VER_CODE", defs::VERSION_CODE)
|
||||
@@ -170,6 +171,7 @@ fn exec_script<T: AsRef<Path>>(path: T, wait: bool) -> Result<()> {
|
||||
.arg(path.as_ref())
|
||||
.env("ASH_STANDALONE", "1")
|
||||
.env("KSU", "true")
|
||||
.env("KSU_SUKISU", "true")
|
||||
.env("KSU_KERNEL_VER_CODE", ksucalls::get_version().to_string())
|
||||
.env("KSU_VER_CODE", defs::VERSION_CODE)
|
||||
.env("KSU_VER", defs::VERSION_NAME)
|
||||
@@ -399,7 +401,7 @@ pub fn restore_uninstall_module(id: &str) -> Result<()> {
|
||||
}
|
||||
|
||||
pub fn run_action(id: &str) -> Result<()> {
|
||||
let action_script_path = format!("/data/adb/modules/{}/action.sh", id);
|
||||
let action_script_path = format!("/data/adb/modules/{id}/action.sh");
|
||||
exec_script(&action_script_path, true)
|
||||
}
|
||||
|
||||
|
||||
@@ -70,9 +70,9 @@ pub fn apply_sepolies() -> Result<()> {
|
||||
};
|
||||
let sepolicy = sepolicy.path();
|
||||
if sepolicy::apply_file(&sepolicy).is_ok() {
|
||||
log::info!("profile sepolicy applied: {:?}", sepolicy);
|
||||
log::info!("profile sepolicy applied: {sepolicy:?}");
|
||||
} else {
|
||||
log::info!("profile sepolicy apply failed: {:?}", sepolicy);
|
||||
log::info!("profile sepolicy apply failed: {sepolicy:?}");
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
||||
@@ -697,7 +697,7 @@ fn apply_one_rule<'a>(statement: &'a PolicyStatement<'a>, strict: bool) -> Resul
|
||||
|
||||
for policy in policies {
|
||||
if !rustix::process::ksu_set_policy(&FfiPolicy::from(policy)) {
|
||||
log::warn!("apply rule: {:?} failed.", statement);
|
||||
log::warn!("apply rule: {statement:?} failed.");
|
||||
if strict {
|
||||
return Err(anyhow::anyhow!("apply rule {:?} failed.", statement));
|
||||
}
|
||||
|
||||
@@ -104,12 +104,12 @@ pub fn is_safe_mode() -> bool {
|
||||
|| getprop("ro.sys.safemode")
|
||||
.filter(|prop| prop == "1")
|
||||
.is_some();
|
||||
log::info!("safemode: {}", safemode);
|
||||
log::info!("safemode: {safemode}");
|
||||
if safemode {
|
||||
return true;
|
||||
}
|
||||
let safemode = ksucalls::check_kernel_safemode();
|
||||
log::info!("kernel_safemode: {}", safemode);
|
||||
log::info!("kernel_safemode: {safemode}");
|
||||
safemode
|
||||
}
|
||||
|
||||
|
||||
@@ -20,30 +20,12 @@
|
||||
#define SUS_SU_DISABLED 0
|
||||
#define SUS_SU_WITH_HOOKS 2
|
||||
|
||||
// Feature flags
|
||||
#define FEATURE_SUS_PATH (1 << 0)
|
||||
#define FEATURE_SUS_MOUNT (1 << 1)
|
||||
#define FEATURE_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT (1 << 2)
|
||||
#define FEATURE_AUTO_ADD_SUS_BIND_MOUNT (1 << 3)
|
||||
#define FEATURE_SUS_KSTAT (1 << 4)
|
||||
#define FEATURE_TRY_UMOUNT (1 << 5)
|
||||
#define FEATURE_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT (1 << 6)
|
||||
#define FEATURE_SPOOF_UNAME (1 << 7)
|
||||
#define FEATURE_ENABLE_LOG (1 << 8)
|
||||
#define FEATURE_HIDE_KSU_SUSFS_SYMBOLS (1 << 9)
|
||||
#define FEATURE_SPOOF_BOOTCONFIG (1 << 10)
|
||||
#define FEATURE_OPEN_REDIRECT (1 << 11)
|
||||
#define FEATURE_SUSFS_HAS_MAGIC_MOUNT (1 << 12)
|
||||
#define FEATURE_SUS_SU (1 << 13)
|
||||
|
||||
struct st_sus_su {
|
||||
int mode;
|
||||
};
|
||||
|
||||
// Function prototypes
|
||||
int enable_sus_su(int last_working_mode, int target_working_mode);
|
||||
void print_features(unsigned long enabled_features);
|
||||
bool is_feature_enabled(unsigned long enabled_features, int feature);
|
||||
int get_sus_su_working_mode(int* mode);
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
@@ -63,17 +45,31 @@ int main(int argc, char* argv[]) {
|
||||
prctl(KERNEL_SU_OPTION, CMD_SUSFS_SHOW_VARIANT, variant, NULL, &error);
|
||||
printf("%s\n", error ? "Invalid" : variant);
|
||||
} else if (strcmp(argv[1], "features") == 0) {
|
||||
unsigned long enabled_features;
|
||||
prctl(KERNEL_SU_OPTION, CMD_SUSFS_SHOW_ENABLED_FEATURES, &enabled_features, NULL, &error);
|
||||
char *enabled_features;
|
||||
size_t bufsize = getpagesize() * 2;
|
||||
enabled_features = (char *)malloc(bufsize);
|
||||
if (!enabled_features) {
|
||||
perror("malloc");
|
||||
return -ENOMEM;
|
||||
}
|
||||
prctl(KERNEL_SU_OPTION, CMD_SUSFS_SHOW_ENABLED_FEATURES, enabled_features, bufsize, &error);
|
||||
if (!error) {
|
||||
print_features(enabled_features);
|
||||
printf("%s", enabled_features);
|
||||
} else {
|
||||
printf("Invalid\n");
|
||||
}
|
||||
free(enabled_features);
|
||||
} else if (strcmp(argv[1], "support") == 0) {
|
||||
unsigned long enabled_features;
|
||||
prctl(KERNEL_SU_OPTION, CMD_SUSFS_SHOW_ENABLED_FEATURES, &enabled_features, NULL, &error);
|
||||
printf("%s\n", error || !enabled_features ? "Unsupported" : "Supported");
|
||||
char *enabled_features;
|
||||
size_t bufsize = getpagesize() * 2;
|
||||
enabled_features = (char *)malloc(bufsize);
|
||||
if (!enabled_features) {
|
||||
perror("malloc");
|
||||
return -ENOMEM;
|
||||
}
|
||||
prctl(KERNEL_SU_OPTION, CMD_SUSFS_SHOW_ENABLED_FEATURES, enabled_features, bufsize, &error);
|
||||
printf("%s\n", error || !strlen(enabled_features) ? "Unsupported" : "Supported");
|
||||
free(enabled_features);
|
||||
} else if (argc == 3 && strcmp(argv[1], "sus_su") == 0) {
|
||||
int last_working_mode, target_working_mode;
|
||||
char* endptr;
|
||||
@@ -134,55 +130,6 @@ int enable_sus_su(int last_working_mode, int target_working_mode) {
|
||||
return error;
|
||||
}
|
||||
|
||||
void print_features(unsigned long enabled_features) {
|
||||
if (is_feature_enabled(enabled_features, FEATURE_SUS_PATH)) {
|
||||
printf("CONFIG_KSU_SUSFS_SUS_PATH\n");
|
||||
}
|
||||
if (is_feature_enabled(enabled_features, FEATURE_SUS_MOUNT)) {
|
||||
printf("CONFIG_KSU_SUSFS_SUS_MOUNT\n");
|
||||
}
|
||||
if (is_feature_enabled(enabled_features, FEATURE_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT)) {
|
||||
printf("CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT\n");
|
||||
}
|
||||
if (is_feature_enabled(enabled_features, FEATURE_AUTO_ADD_SUS_BIND_MOUNT)) {
|
||||
printf("CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT\n");
|
||||
}
|
||||
if (is_feature_enabled(enabled_features, FEATURE_SUS_KSTAT)) {
|
||||
printf("CONFIG_KSU_SUSFS_SUS_KSTAT\n");
|
||||
}
|
||||
if (is_feature_enabled(enabled_features, FEATURE_TRY_UMOUNT)) {
|
||||
printf("CONFIG_KSU_SUSFS_TRY_UMOUNT\n");
|
||||
}
|
||||
if (is_feature_enabled(enabled_features, FEATURE_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT)) {
|
||||
printf("CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT\n");
|
||||
}
|
||||
if (is_feature_enabled(enabled_features, FEATURE_SPOOF_UNAME)) {
|
||||
printf("CONFIG_KSU_SUSFS_SPOOF_UNAME\n");
|
||||
}
|
||||
if (is_feature_enabled(enabled_features, FEATURE_ENABLE_LOG)) {
|
||||
printf("CONFIG_KSU_SUSFS_ENABLE_LOG\n");
|
||||
}
|
||||
if (is_feature_enabled(enabled_features, FEATURE_HIDE_KSU_SUSFS_SYMBOLS)) {
|
||||
printf("CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS\n");
|
||||
}
|
||||
if (is_feature_enabled(enabled_features, FEATURE_SPOOF_BOOTCONFIG)) {
|
||||
printf("CONFIG_KSU_SUSFS_SPOOF_BOOTCONFIG\n");
|
||||
}
|
||||
if (is_feature_enabled(enabled_features, FEATURE_OPEN_REDIRECT)) {
|
||||
printf("CONFIG_KSU_SUSFS_OPEN_REDIRECT\n");
|
||||
}
|
||||
if (is_feature_enabled(enabled_features, FEATURE_SUSFS_HAS_MAGIC_MOUNT)) {
|
||||
printf("CONFIG_KSU_SUSFS_HAS_MAGIC_MOUNT\n");
|
||||
}
|
||||
if (is_feature_enabled(enabled_features, FEATURE_SUS_SU)) {
|
||||
printf("CONFIG_KSU_SUSFS_SUS_SU\n");
|
||||
}
|
||||
}
|
||||
|
||||
bool is_feature_enabled(unsigned long enabled_features, int feature) {
|
||||
return (enabled_features & feature) != 0;
|
||||
}
|
||||
|
||||
int get_sus_su_working_mode(int* mode) {
|
||||
int error = -1;
|
||||
prctl(KERNEL_SU_OPTION, CMD_SUSFS_SHOW_SUS_SU_WORKING_MODE, mode, NULL, &error);
|
||||
|
||||
@@ -4,6 +4,7 @@ import { readdir, writeFile } from 'fs/promises'
|
||||
import { resolve } from 'path'
|
||||
|
||||
export default defineConfig( {
|
||||
base: '/SukiSU-Ultra/',
|
||||
title: 'KernelSU',
|
||||
locales: locales.locales,
|
||||
head: [
|
||||
|
||||
@@ -157,7 +157,7 @@ GKI 的安装方法有如下几种,各自适用于不同的场景,请按需
|
||||
|
||||
## 使用 KernelSU 提供的 boot.img 安装 {#install-by-kernelsu-boot-image}
|
||||
|
||||
如果你设备的 `boot.img` 采用常用的压缩格式,那么可以采用 KernelSU 提供的的通用内核镜像直接刷入,它不需要 TWRP 或者自行修补镜像。
|
||||
如果你设备的 `boot.img` 采用常用的压缩格式,那么可以采用 KernelSU 提供的通用内核镜像直接刷入,它不需要 TWRP 或者自行修补镜像。
|
||||
|
||||
### 找到合适的 boot.img {#found-propery-image}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user