Compare commits

...

35 Commits

Author SHA1 Message Date
igor
4270fd8b1e update translations in docs and manager (#571)
Co-authored-by: Rifat Azad <33044977+rifsxd@users.noreply.github.com>
2025-06-27 22:51:05 +06:00
роизен
764dbc3782 Improve README_UA (#569)
* Improve README_UA

* Update README_UA.md
2025-06-27 22:48:49 +06:00
роизен
080ab9a952 Update Uk (#566) 2025-06-27 22:48:32 +06:00
AxelPLN(Axel Yinjia Huang)
a9cab5ccfd fix: complete the text of the module labels (#558)
- replace the string Resource ID of uninstalled & those can be updated modules
2025-06-27 22:48:18 +06:00
Rifat Azad
3d44602537 manager: check additional zygisk 64 libs instead of only 32 bit lib for zygisk detection 2025-06-27 21:13:25 +06:00
Eren
88eb2a2723 Improve README (#562)
* Improve README

* Update README.md
2025-06-27 18:39:33 +06:00
NkBe
e272e557b0 manger: fix lkm detection (#2654)
原因请看
https://github.com/SukiSU-Ultra/SukiSU-Ultra/pull/217#issuecomment-3004461174

文中介绍的是lkm的问题 但实测下来gki也有这样的问题 但修复方法通用
2025-06-27 18:02:42 +06:00
Rifat Azad
2a4794e422 manager: implemented zygisk required label for module card 2025-06-27 18:02:34 +06:00
mr_vokintos
818bdbead6 Update Russian (#557) 2025-06-27 14:37:54 +06:00
Nhật Minh
76249fa67d Update strings.xml (#553) 2025-06-27 14:37:39 +06:00
AxelPLN(Axel Yinjia Huang)
ed50b57b57 fix: update manager translation for zh-rCN & zh-rTW (#556)
* docs: complete text for README_CN & README_TW

* fix: update manager translation for zh-rCN & zh-rTW

also complete some text for module label and language switching functionality
2025-06-27 14:37:10 +06:00
Rifat Azad
f05f776a08 manager: fix Condition is always 'true' 2025-06-27 12:02:16 +06:00
Axel Yinjia Huang
c9b79c3016 manager: add basic language switching functionality 2025-06-27 11:37:12 +06:00
Rifat Azad
3ff10d6622 susfsd: remove deprecated CONFIG_KSU_SUSFS_SUS_OVERLAYFS and add CONFIG_KSU_SUSFS_HAS_MAGIC_MOUNT 2025-06-26 23:55:29 +06:00
Rifat Azad
b95d2b69b6 manager: add vendor_boot suggestion for LKM patch 2025-06-26 23:44:46 +06:00
Rifat Azad
2cd8453877 Revert "manager: move the wx platform init to the Application class so it starts as soon as the app process launches"
This reverts commit d5c4f85d73.
2025-06-26 23:25:37 +06:00
роизен
b7300b0525 Update translation for UK (#552) 2025-06-26 22:02:09 +06:00
kam821
3a278d560f Update Polish translation (#521)
* Update Polish translation

- Improve cosmetics / context-sensitive lines

* Update Polish translation - backup/restore

- Cosmetics, worth considering changing it in the future to keep option name in one line.

* Update Polish translation - homepage

- Shortened translation to work around broken formatting
2025-06-26 20:01:55 +06:00
igor
03fa2eddb2 Update portuguese translation (#518) 2025-06-26 17:43:27 +06:00
Rifat Azad
b74e953ad2 ksud: implement patching LKM vendor_ramdisk/ramdisk.cpio for compatibily needed for some pixel devices 2025-06-26 10:33:58 +06:00
Rifat Azad
39717b0a3f Revert "ksud: rust FMT"
This reverts commit 78eb3b0b22.
2025-06-25 17:58:52 +06:00
Rifat Azad
7f0eccd3d5 ksud: handle errors and non compatible ramdisk 2025-06-25 17:43:59 +06:00
Rifat Azad
78eb3b0b22 ksud: rust FMT 2025-06-25 13:25:28 +06:00
Rifat Azad
39f20bf573 manager: fix uneven spacing for empty label for superuser app section with DEFAULT label (fix #547) 2025-06-25 12:54:13 +06:00
Rifat Azad
092eb1b23d manager: use busybox for tar and du commands as not all devices has it most likely and if module size is 0 bytes then show null 2025-06-24 17:56:50 +06:00
Rifat Azad
30e2ed5db5 ksud: third test properly check if vendor is already patched or not for lkm restoration and also handle magisk patched vendor boot 2025-06-24 16:47:44 +06:00
Rifat Azad
dc7ae2db5f manager: fix status card startActivity intent fallback to not allow lkm install on non gki 2025-06-24 09:53:20 +06:00
Rifat Azad
b3b7ef1cb3 manager: dont show developer options and banner toggle when ksuversion is null 2025-06-24 09:26:14 +06:00
Rifat Azad
4de4d1e091 ksud: second test of vendor_boot patching now handling vendor_boot partition and restore partition 2025-06-24 09:17:24 +06:00
Rifat Azad
0beea57ab7 ksud: test vendor_boot patching for some newer devices 2025-06-23 18:17:46 +06:00
Rifat Azad
49aee1ff4c manager: remove susfs info from status card and merge it to info card 2025-06-21 17:01:21 +06:00
Rifat Azad
f7a3699fe3 manager: show module update count as badge in bottom bar instead of status card 2025-06-21 12:44:29 +06:00
5ec1cff
66d42de599 app: persist show system app settings 2025-06-20 00:54:23 +06:00
Kaorun
d562594ae1 Update Module.kt 2025-06-19 13:35:05 +06:00
Rifat Azad
02afc6710c manager: show sucmpat when both sucompat is disabled and sus_su enabled 2025-06-18 20:23:35 +06:00
29 changed files with 1211 additions and 334 deletions

View File

@@ -1,63 +1,89 @@
**English** | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md) | [Українська](README_UA.md)
**English** | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Українська](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md)
# KernelSU Next
---
<img src="/assets/kernelsu_next.png" style="width: 96px;" alt="logo">
<div align="center">
<img src="/assets/kernelsu_next.png" width="96" alt="KernelSU Next Logo">
A kernel-based root solution for Android devices.
<h2>KernelSU Next</h2>
<p><strong>A kernel-based root solution for Android devices.</strong></p>
[![Latest Release](https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github)](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
[![Nightly Release](https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff)](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
[![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
[![GitHub License](https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu)](/LICENSE)
<p>
<a href="https://github.com/KernelSU-Next/KernelSU-Next/releases/latest">
<img src="https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github" alt="Latest Release">
</a>
<a href="https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager">
<img src="https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff" alt="Nightly Build">
</a>
<a href="https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html">
<img src="https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu" alt="License: GPL v2">
</a>
<a href="/LICENSE">
<img src="https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu" alt="GitHub License">
</a>
</p>
</div>
## Features
---
1. Kernel-based `su` and root access management.
2. Module system based on dynamic mount system [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
3. [App Profile](https://kernelsu.org/guide/app-profile.html): Lock up the root power in a cage.
## 🚀 Features
## Compatibility state
- Kernel-based `su` and root access management.
- Module system based on [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) and [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
- [App Profile](https://kernelsu.org/guide/app-profile.html): Limit root privileges per app.
KernelSU Next officially supports most Android kernels starting from 4.4 up to 6.6.
- GKI 2.0 (5.10+) kernels can run pre-built images and LKM/KMI.
- GKI 1.0 (4.19 - 5.4) kernels need to rebuilt with KernelSU driver.
- EOL (<4.14) kernels also need to be rebuilt with KernelSU driver (3.18+ is experimental and may need some function backports).
---
Currently, only the `arm64-v8a`, `armeabi-v7a` & `x86_64` architectures are supported.
## ✅ Compatibility
## Usage
KernelSU Next supports Android kernels from **4.4 up to 6.6**:
- [Installation instruction](https://kernelsu-next.github.io/webpage/pages/installation.html)
| Kernel version | Support notes |
|----------------------|-------------------------------------------------------------------------|
| 5.10+ (GKI 2.0) | Supports pre-built images and LKM/KMI |
| 4.19 5.4 (GKI 1.0) | Requires KernelSU driver built-in |
| < 4.14 (EOL) | Requires KernelSU driver (3.18+ is experimental and may need backports) |
## Security
**Supported architectures:** `arm64-v8a`, `armeabi-v7a` and `x86_64`
For information on reporting security vulnerabilities in KernelSU, see [SECURITY.md](/SECURITY.md).
---
## License
## 📦 Installation
- Files under the `kernel` directory are [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
- All other parts except the `kernel` directory are [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
Please refer to the [Installation](https://kernelsu-next.github.io/webpage/pages/installation.html) guide for setup instructions.
## Donations
---
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT BEP20 ]
## 🔐 Security
- TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh [ USDT TRC20 ]
To report security issues, please see [SECURITY.md](/SECURITY.md).
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT ERC20 ]
---
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ ETH ERC20 ]
## 📜 License
- Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL [ LTC ]
- **`/kernel` directory:** [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
- **All other files:** [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
- 19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6 [ BTC ]
---
## Credits
## 💸 Donations
- [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.
- [KernelSU](https://github.com/tiann/KernelSU): Thanks to tiann, or else KernelSU Next wouldn't even exist.
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff for saving KernelSU!
If youd like to support the project:
- **USDT (BEP20, ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
- **USDT (TRC20)**: `TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh`
- **ETH (ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
- **LTC**: `Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL`
- **BTC**: `19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6`
---
## 🙏 Credits
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/) Concept inspiration
- [Magisk](https://github.com/topjohnwu/Magisk) Core root implementation
- [Genuine](https://github.com/brevent/genuine/) APK v2 signature validation
- [Diamorphine](https://github.com/m0nad/Diamorphine) Rootkit techniques
- [KernelSU](https://github.com/tiann/KernelSU) The original base that made KernelSU Next possible
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs) 💜 to 5ec1cff for keeping KernelSU alive

View File

@@ -13,7 +13,7 @@
## 特性
1. 基于内核的 `SU` 和权限管理
1. 基于内核的 `su`超级用户权限管理
2. 基于动态挂载系统 [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS) 的模块系统。
3. [App Profile](https://kernelsu.org/zh_CN/guide/app-profile.html):把 Root 权限关进笼子里
@@ -46,4 +46,4 @@ KernelSU Next 支持从 4.4 到 6.6 的大多数安卓内核
- [genuine](https://github.com/brevent/genuine/): APK v2 签名验证。
- [Diamorphine](https://github.com/m0nad/Diamorphine): 一些 Rootkit 技巧。
- [KernelSU](https://github.com/tiann/KernelSU): 感谢 tiann否则 KernelSU Next 根本不会存在。
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff 了拯救 KernelSU
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff 了拯救 KernelSU

View File

@@ -1,63 +1,89 @@
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | **Português (Brasil)** | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [Український](README_UA.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md)
# KernelSU Next
---
<img src="/assets/kernelsu_next.png" style="width: 96px;" alt="logo">
<div align="center">
<img src="/assets/kernelsu_next.png" width="96" alt="KernelSU Next Logo">
Uma solução root baseada em kernel para dispositivos Android.
<h2>KernelSU Next</h2>
<p><strong>Uma solução root baseada em kernel para dispositivos Android.</strong></p>
[![Latest Release](https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github)](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
[![Nightly Release](https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff)](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
[![License: GPL v2](https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
[![GitHub License](https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu)](/LICENSE)
<p>
<a href="https://github.com/KernelSU-Next/KernelSU-Next/releases/latest">
<img src="https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github" alt="Latest Release">
</a>
<a href="https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager">
<img src="https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff" alt="Nightly Build">
</a>
<a href="https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html">
<img src="https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu" alt="License: GPL v2">
</a>
<a href="/LICENSE">
<img src="https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu" alt="GitHub License">
</a>
</p>
</div>
## Características
---
1. `su` e gerenciamento de acesso root baseado em kernel.
2. Sistema de módulos baseado em sistema de montagem dinâmica [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
3. [Perfil do Aplicativo](https://kernelsu.org/pt_BR/guide/app-profile.html): Tranque o poder root em uma gaiola.
## 🚀 Características
## Estado de compatibilidade
- `su` e gerenciamento de acesso root baseado em kernel.
- Sistema de módulos baseado em [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) e [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
- [Perfil do app](https://kernelsu.org/pt_BR/guide/app-profile.html): Limitar privilégios root por app.
KernelSU Next suporta oficialmente a maioria dos kernels Android a partir de 4.4 até 6.6.
- Os kernels GKI 2.0 (5.10+) podem executar imagens pré-construídas e LKM/KMI.
- Os kernels GKI 1.0 (4.19 - 5.4) precisam ser reconstruídos com o driver KernelSU.
- Os kernels EOL (<4.14) também precisam ser reconstruídos com o driver KernelSU (3.18+ é experimental e pode precisar portar algumas funções).
---
Atualmente, apenas as arquiteturas `arm64-v8a`, `armeabi-v7a` & `x86_64` são compatíveis.
## ✅ Compatibilidade
## Uso
O KernelSU Next oferece suporte a kernels Android **4.4 até 6.6**:
- [Instruções de instalação](https://ksunext.org/pages/installation.html)
| Versão do kernel | Notas de suporte |
|----------------------|-------------------------------------------------------------------------------|
| 5.10+ (GKI 2.0) | Suporta imagens pré-compiladas e LKM/KMI |
| 4.19 5.4 (GKI 1.0) | Requer driver do KernelSU integrado |
| < 4.14 (EOL) | Requer driver do KernelSU (3.18+ é experimental e pode precisar de backports) |
## Segurança
**Arquiteturas suportadas:** `arm64-v8a`, `armeabi-v7a` e `x86_64`
Para obter informações sobre como relatar vulnerabilidades de segurança do KernelSU, consulte [SECURITY.md](/SECURITY.md).
---
## Licença
## 📦 Instalação
- Os arquivos no diretório `kernel` são [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
- Todas as outras partes, exceto o diretório `kernel` são [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
Consulte o guia de [Instalação](https://kernelsu-next.github.io/webpage/pt_BR/pages/installation.html) para obter instruções de configuração.
## Doações
---
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT BEP20 ]
## 🔐 Segurança
- TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh [ USDT TRC20 ]
Para relatar problemas de segurança, consulte [SECURITY.md](/SECURITY.md).
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT ERC20 ]
---
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ ETH ERC20 ]
## 📜 Licença
- Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL [ LTC ]
- **Diretório `/kernel`:** [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
- **Todos os outros arquivos:** [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
- 19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6 [ BTC ]
---
## Créditos
## 💸 Doações
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): A ideia do KernelSU.
- [Magisk](https://github.com/topjohnwu/Magisk): A poderosa ferramenta root.
- [genuine](https://github.com/brevent/genuine/): Validação de assinatura APK v2.
- [Diamorphine](https://github.com/m0nad/Diamorphine): Algumas habilidades de rootkit.
- [KernelSU](https://github.com/tiann/KernelSU): Obrigado ao tiann, ou então o KernelSU Next nem existiria.
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff por salvar o KernelSU!
Se você quiser apoiar o projeto:
- **USDT (BEP20, ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
- **USDT (TRC20)**: `TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh`
- **ETH (ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
- **LTC**: `Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL`
- **BTC**: `19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6`
---
## 🙏 Créditos
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/) Inspiração do conceito
- [Magisk](https://github.com/topjohnwu/Magisk) Implementação root principal
- [Genuine](https://github.com/brevent/genuine/) Validação de assinatura APK v2
- [Diamorphine](https://github.com/m0nad/Diamorphine) Técnicas de rootkit
- [KernelSU](https://github.com/tiann/KernelSU) A base original que tornou o KernelSU Next possível
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs) 💜 para 5ec1cff por manter o KernelSU vivo

View File

@@ -47,4 +47,3 @@ KernelSU Next 正式支持大多數從 4.4 到 6.6 的 Android 內核
- [Diamorphine](https://github.com/m0nad/Diamorphine): 一些 Rootkit 技巧。
- [KernelSU](https://github.com/tiann/KernelSU): 感謝 tiann否則 KernelSU Next 根本不會存在。
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): 💜 5ec1cff 為了拯救 KernelSU

View File

@@ -1,63 +1,91 @@
**Languages**:
[English](README.md) | [简体中文](README_CN.md) | [繁體中文](README_TW.md) | [Türkçe](README_TR.md) | [Português (Brasil)](README_PT-BR.md) | [한국어](README_KO.md) | [Français](README_FR.md) | [Bahasa Indonesia](README_ID.md) | [Русский](README_RU.md) | [ภาษาไทย](README_TH.md) | [Tiếng Việt](README_VI.md) | [Italiano](README_IT.md) | [Polski](README_PL.md) | [Български](README_BG.md) | [日本語](README_JA.md) | **Українська**
# KernelSU Next
---
<img src="/assets/kernelsu_next.png" style="width: 96px;" alt="logo">
<div align="center">
<img src="/assets/kernelsu_next.png" width="96" alt="KernelSU Next Logo">
<h2>KernelSU Next</h2>
<p><strong>Рішення для root-прав на основі ядра для пристроїв Android.</strong></p>
Рут-рішення на основі ядра для пристроїв Android.
<p>
<a href="https://github.com/KernelSU-Next/KernelSU-Next/releases/latest">
<img src="https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github" alt="Latest Release">
</a>
<a href="https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager">
<img src="https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff" alt="Nightly Build">
</a>
<a href="https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html">
<img src="https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu" alt="License: GPL v2">
</a>
<a href="/LICENSE">
<img src="https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu" alt="GitHub License">
</a>
</p>
</div>
[![Останній реліз](https://img.shields.io/github/v/release/KernelSU-Next/KernelSU-Next?label=Release&logo=github)](https://github.com/KernelSU-Next/KernelSU-Next/releases/latest)
[![Нічний реліз (Нестабільний)](https://img.shields.io/badge/Nightly%20Release-gray?logo=hackthebox&logoColor=fff)](https://nightly.link/KernelSU-Next/KernelSU-Next/workflows/build-manager-ci/next/Manager)
[![Ліцензія: GPL v2](https://img.shields.io/badge/License-GPL%20v2-orange.svg?logo=gnu)](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
[![GitHub Ліцензія](https://img.shields.io/github/license/KernelSU-Next/KernelSU-Next?logo=gnu)](/LICENSE)
---
## Можливості
## 🚀 Особливості
1. `su` на основі ядра та можливість контролювати дозволи руту.
2. Module system based on dynamic mount system [Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount) / [OverlayFS](https://en.wikipedia.org/wiki/OverlayFS).
3. [Профілі додатків](https://kernelsu.org/guide/app-profile.html): Обмеж права руту для додатків.
- Керування `su` та root-доступом на основі ядра
- Модульна система на основі **[Magic Mount](https://topjohnwu.github.io/Magisk/details.html#magic-mount)** та **[OverlayFS](https://en.wikipedia.org/wiki/OverlayFS)**
- [Профілі програм](https://kernelsu.org/guide/app-profile.html): Обмеження root-прав для кожної програми
## Compatibility state
---
KernelSU Next офіційно підтримує більшість Android ядер починаючи з 4.4 і до 6.6.
- Користувачі GKI 2.0 (5.10+) ядра можуть використовувати готові образи та LKM/KMI.
- Користувачі GKI 1.0 (4.19 - 5.4) ядра мають бути перезібрані з драйвером KernelSU.
- Користувачі EOL (<4.14) ядра також мають бути перезібрані з драйвером KernelSU (Підтримка 3.18+ експерементальна і потребує бекпортів деяких функцій в ядрі).
## ✅ Сумісність
На даний момент підтримується лише архітектура `arm64-v8a`, `armeabi-v7a` & `x86_64`.
KernelSU Next підтримує ядра Android від **4.4 до 6.6**:
## Спосіб використання
| Версія ядра | Примітки підтримки |
|----------------|---------------|
| 5.10+ (GKI 2.0) | Підтримує попередньо створені образи та LKM/KMI |
| 4.19 5.4 (GKI 1.0) | Потрібен вбудований драйвер KernelSU |
| <4.14 (EOL) | Потрібен драйвер KernelSU (версія 3.18+ є експериментальною, може знадобитися портування) |
- [Інструкція для встановлення/інтеграції](https://ksunext.org/pages/installation.html)
**Підтримувані архітектури:**
`arm64-v8a`, `armeabi-v7a`, `x86_64`
## Безпека
---
Для інформації зв'язаною з безпекою дивіться [SECURITY.md](/SECURITY.md).
## 📦 Встановлення
## Ліцензія
Будь ласка, зверніться до [Посібника з встановлення](https://kernelsu-next.github.io/webpage/pages/installation.html) для отримання інструкцій з налаштування.
- Всі файли в директорії `kernel` мають ліцензію [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html).
- Всі інші файли виключаючи директорію `kernel` мають ліцензію [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html).
---
## Підтримка розробника
## 🔐 Безпека
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT BEP20 ]
Щоб повідомити про проблеми безпеки, див. [SECURITY.md](/SECURITY.md).
- TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh [ USDT TRC20 ]
---
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ USDT ERC20 ]
## 📜 Ліцензія
- 0x12b5224b7aca0121c2f003240a901e1d064371c1 [ ETH ERC20 ]
- **Каталог `/kernel`:** [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
- **Усі інші файли:** [GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html)
- Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL [ LTC ]
---
- 19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6 [ BTC ]
## 💸 Пожертви
## Подяки
Якщо ви хочете підтримати проєкт:
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/): Ідея KernelSU.
- [Magisk](https://github.com/topjohnwu/Magisk): Потужний засіб руту.
- [genuine](https://github.com/brevent/genuine/): Перевірка підпису APK v2.
- [Diamorphine](https://github.com/m0nad/Diamorphine): Деякі руткіт скіли.
- [KernelSU](https://github.com/tiann/KernelSU): Дякую tiann, інакше KernelSU Next ніколи б не існував.
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs): Дякую 💜 5ec1cff за збереження KernelSU!
- **USDT (BEP20, ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
- **USDT (TRC20)**: `TYUVMWGTcnR5svnDoX85DWHyqUAeyQcdjh`
- **ETH (ERC20)**: `0x12b5224b7aca0121c2f003240a901e1d064371c1`
- **LTC**: `Ld238uYBuRQdZB5YwdbkuU6ektBAAUByoL`
- **BTC**: `19QgifcjMjSr1wB2DJcea5cxitvWVcXMT6`
---
## 🙏 Подяки
- [Kernel-Assisted Superuser](https://git.zx2c4.com/kernel-assisted-superuser/about/) Натхнення для концепції
- [Magisk](https://github.com/topjohnwu/Magisk) Топовий інструмент для root
- [Genuine](https://github.com/brevent/genuine/) Перевірка підпису APK версії 2
- [Diamorphine](https://github.com/m0nad/Diamorphine) Деякі навики RootKit
- [KernelSU](https://github.com/tiann/KernelSU) Основа для KernelSU Next
- [Magic Mount Port](https://github.com/5ec1cff/KernelSU/blob/main/userspace/ksud/src/magic_mount.rs) 💜 до 5ec1cff за збереження KernelSU

View File

@@ -51,12 +51,13 @@ bool become_manager(const char* pkg) {
}
// cache the result to avoid unnecessary syscall
static bool is_lkm;
int get_version() {
static bool is_lkm = false;
int get_version(void) {
int32_t version = -1;
int32_t lkm = 0;
ksuctl(CMD_GET_VERSION, &version, &lkm);
if (!is_lkm && lkm != 0) {
int32_t flags = 0;
ksuctl(CMD_GET_VERSION, &version, &flags);
if (!is_lkm && (flags & 0x1)) {
is_lkm = true;
}
return version;
@@ -103,4 +104,4 @@ bool is_su_enabled() {
// if ksuctl failed, we assume su is enabled, and it cannot be disabled.
ksuctl(CMD_IS_SU_ENABLED, &enabled, nullptr);
return enabled;
}
}

View File

@@ -11,10 +11,6 @@ import okhttp3.Cache
import okhttp3.OkHttpClient
import java.io.File
import java.util.Locale
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import com.rifsxd.ksunext.ui.webui.initPlatform
lateinit var ksuApp: KernelSUApplication
@@ -28,9 +24,6 @@ class KernelSUApplication : Application() {
Platform.setHiddenApiExemptions()
// Pre-initialize WX Platform as early as possible
launchPlatformInit()
val context = this
val iconSize = resources.getDimensionPixelSize(android.R.dimen.app_icon_size)
Coil.setImageLoader(
@@ -61,11 +54,5 @@ class KernelSUApplication : Application() {
}.build()
}
private fun launchPlatformInit() {
// Use a coroutine to avoid blocking the main thread
GlobalScope.launch(Dispatchers.IO) {
initPlatform()
}
}
}

View File

@@ -24,6 +24,8 @@ import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.foundation.layout.union
import androidx.compose.material3.Badge
import androidx.compose.material3.BadgedBox
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
@@ -38,6 +40,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
@@ -53,14 +56,22 @@ import com.rifsxd.ksunext.Natives
import com.rifsxd.ksunext.ksuApp
import com.rifsxd.ksunext.ui.screen.BottomBarDestination
import com.rifsxd.ksunext.ui.theme.KernelSUTheme
import com.rifsxd.ksunext.ui.util.*
import com.rifsxd.ksunext.ui.util.LocalSnackbarHost
import com.rifsxd.ksunext.ui.util.LocaleHelper
import com.rifsxd.ksunext.ui.util.rootAvailable
import com.rifsxd.ksunext.ui.util.install
import com.rifsxd.ksunext.ui.util.isSuCompatDisabled
import com.rifsxd.ksunext.ui.screen.FlashIt
import com.rifsxd.ksunext.ui.viewmodel.ModuleViewModel
import com.rifsxd.ksunext.ui.webui.initPlatform
class MainActivity : ComponentActivity() {
override fun attachBaseContext(newBase: Context?) {
super.attachBaseContext(newBase?.let { LocaleHelper.applyLanguage(it) })
}
override fun onCreate(savedInstanceState: Bundle?) {
// Enable edge to edge
@@ -103,6 +114,11 @@ class MainActivity : ComponentActivity() {
val prefs = getSharedPreferences("settings", Context.MODE_PRIVATE)
val amoledMode = prefs.getBoolean("enable_amoled", false)
val moduleViewModel: ModuleViewModel = viewModel()
val moduleUpdateCount = moduleViewModel.moduleList.count {
moduleViewModel.checkUpdate(it).first.isNotEmpty()
}
KernelSUTheme (
amoledMode = amoledMode
) {
@@ -129,6 +145,11 @@ class MainActivity : ComponentActivity() {
else -> true
}
// pre-init platform to faster start WebUI X activities
LaunchedEffect(Unit) {
initPlatform()
}
Scaffold(
bottomBar = {
AnimatedVisibility(
@@ -136,7 +157,7 @@ class MainActivity : ComponentActivity() {
enter = slideInVertically(initialOffsetY = { it }) + fadeIn(),
exit = slideOutVertically(targetOffsetY = { it }) + fadeOut()
) {
BottomBar(navController)
BottomBar(navController, moduleUpdateCount)
}
},
contentWindowInsets = WindowInsets(0, 0, 0, 0)
@@ -163,11 +184,13 @@ class MainActivity : ComponentActivity() {
}
@Composable
private fun BottomBar(navController: NavHostController) {
private fun BottomBar(navController: NavHostController, moduleUpdateCount: Int) {
val navigator = navController.rememberDestinationsNavigator()
val isManager = Natives.becomeManager(ksuApp.packageName)
val fullFeatured = isManager && !Natives.requireNewKernel() && rootAvailable()
val suCompatDisabled = isSuCompatDisabled()
val suSFS = getSuSFS()
val susSUMode = susfsSUS_SU_Mode()
NavigationBar(
tonalElevation = 8.dp,
@@ -177,9 +200,14 @@ private fun BottomBar(navController: NavHostController) {
) {
BottomBarDestination.entries
.filter {
// Hide SuperUser and Module when su compat is enabled
// Hide SuperUser and Module when su compat is disabled
if (suCompatDisabled) {
it != BottomBarDestination.SuperUser && it != BottomBarDestination.Module
if (suSFS == "Supported" && susSUMode == "2") {
true
} else {
// hide SuperUser and Module
it != BottomBarDestination.SuperUser && it != BottomBarDestination.Module
}
} else true
}
.forEach { destination ->
@@ -200,10 +228,21 @@ private fun BottomBar(navController: NavHostController) {
}
},
icon = {
if (isCurrentDestOnBackStack) {
Icon(destination.iconSelected, stringResource(destination.label))
// Show badge for Module icon if moduleUpdateCount > 0
if (destination == BottomBarDestination.Module && moduleUpdateCount > 0) {
BadgedBox(badge = { Badge { Text(moduleUpdateCount.toString()) } }) {
if (isCurrentDestOnBackStack) {
Icon(destination.iconSelected, stringResource(destination.label))
} else {
Icon(destination.iconNotSelected, stringResource(destination.label))
}
}
} else {
Icon(destination.iconNotSelected, stringResource(destination.label))
if (isCurrentDestOnBackStack) {
Icon(destination.iconSelected, stringResource(destination.label))
} else {
Icon(destination.iconNotSelected, stringResource(destination.label))
}
}
},
label = { Text(stringResource(destination.label)) },

View File

@@ -3,6 +3,7 @@ package com.rifsxd.ksunext.ui.screen
import android.content.Context
import android.content.Intent
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
@@ -17,6 +18,7 @@ import androidx.compose.material.icons.filled.*
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.ListItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.Text
@@ -28,7 +30,9 @@ import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
@@ -39,6 +43,11 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.lifecycle.compose.dropUnlessResumed
import com.maxkeppeker.sheets.core.models.base.Header
import com.maxkeppeker.sheets.core.models.base.rememberUseCaseState
import com.maxkeppeler.sheets.list.ListDialog
import com.maxkeppeler.sheets.list.models.ListOption
import com.maxkeppeler.sheets.list.models.ListSelection
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
@@ -46,10 +55,12 @@ import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
import com.rifsxd.ksunext.Natives
import com.rifsxd.ksunext.ksuApp
import com.rifsxd.ksunext.R
import com.rifsxd.ksunext.ui.component.rememberCustomDialog
import com.rifsxd.ksunext.ui.component.SwitchItem
import com.rifsxd.ksunext.ui.util.LocaleHelper
import com.rifsxd.ksunext.ui.util.LocalSnackbarHost
import com.rifsxd.ksunext.ui.util.*
import java.util.Locale
/**
* @author rifsxd
@@ -90,19 +101,177 @@ fun CustomizationScreen(navigator: DestinationsNavigator) {
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
// Track language state with current app locale
var currentAppLocale by remember { mutableStateOf(LocaleHelper.getCurrentAppLocale(context)) }
// Listen for preference changes
LaunchedEffect(Unit) {
currentAppLocale = LocaleHelper.getCurrentAppLocale(context)
}
// Language setting with selection dialog
val languageDialog = rememberCustomDialog { dismiss ->
// Check if should use system language settings
if (LocaleHelper.useSystemLanguageSettings) {
// Android 13+ - Jump to system settings
LocaleHelper.launchSystemLanguageSettings(context)
dismiss()
} else {
// Android < 13 - Show app language selector
// Dynamically detect supported locales from resources
val supportedLocales = remember {
val locales = mutableListOf<java.util.Locale>()
// Add system default first
locales.add(java.util.Locale.ROOT) // This will represent "System Default"
// Dynamically detect available locales by checking resource directories
val resourceDirs = listOf(
"ar", "bg", "de", "fa", "fr", "hu", "in", "it",
"ja", "ko", "pl", "pt-rBR", "ru", "th", "tr",
"uk", "vi", "zh-rCN", "zh-rTW"
)
resourceDirs.forEach { dir ->
try {
val locale = when {
dir.contains("-r") -> {
val parts = dir.split("-r")
java.util.Locale.Builder()
.setLanguage(parts[0])
.setRegion(parts[1])
.build()
}
else -> java.util.Locale.Builder()
.setLanguage(dir)
.build()
}
// Test if this locale has translated resources
val config = android.content.res.Configuration()
config.setLocale(locale)
val localizedContext = context.createConfigurationContext(config)
// Try to get a translated string to verify the locale is supported
val testString = localizedContext.getString(R.string.settings_language)
val defaultString = context.getString(R.string.settings_language)
// If the string is different or it's English, it's supported
if (testString != defaultString || locale.language == "en") {
locales.add(locale)
}
} catch (e: Exception) {
// Skip unsupported locales
}
}
// Sort by display name
val sortedLocales = locales.drop(1).sortedBy { it.getDisplayName(it) }
mutableListOf<java.util.Locale>().apply {
add(locales.first()) // System default first
addAll(sortedLocales)
}
}
val allOptions = supportedLocales.map { locale ->
val tag = if (locale == java.util.Locale.ROOT) {
"system"
} else if (locale.country.isEmpty()) {
locale.language
} else {
"${locale.language}_${locale.country}"
}
val displayName = if (locale == java.util.Locale.ROOT) {
context.getString(R.string.system_default)
} else {
locale.getDisplayName(locale)
}
tag to displayName
}
val currentLocale = prefs.getString("app_locale", "system") ?: "system"
val options = allOptions.map { (tag, displayName) ->
ListOption(
titleText = displayName,
selected = currentLocale == tag
)
}
var selectedIndex by remember {
mutableIntStateOf(allOptions.indexOfFirst { (tag, _) -> currentLocale == tag })
}
ListDialog(
state = rememberUseCaseState(
visible = true,
onFinishedRequest = {
if (selectedIndex >= 0 && selectedIndex < allOptions.size) {
val newLocale = allOptions[selectedIndex].first
prefs.edit().putString("app_locale", newLocale).apply()
// Update local state immediately
currentAppLocale = LocaleHelper.getCurrentAppLocale(context)
// Apply locale change immediately for Android < 13
LocaleHelper.restartActivity(context)
}
dismiss()
},
onCloseRequest = {
dismiss()
}
),
header = Header.Default(
title = stringResource(R.string.settings_language),
),
selection = ListSelection.Single(
showRadioButtons = true,
options = options
) { index, _ ->
selectedIndex = index
}
)
}
}
val language = stringResource(id = R.string.settings_language)
// Compute display name based on current app locale (similar to the reference implementation)
val currentLanguageDisplay = remember(currentAppLocale) {
val locale = currentAppLocale
if (locale != null) {
locale.getDisplayName(locale)
} else {
context.getString(R.string.system_default)
}
}
ListItem(
leadingContent = { Icon(Icons.Filled.Translate, language) },
headlineContent = { Text(language) },
supportingContent = { Text(currentLanguageDisplay) },
modifier = Modifier.clickable {
languageDialog.show()
}
)
var useBanner by rememberSaveable {
mutableStateOf(
prefs.getBoolean("use_banner", true)
)
}
SwitchItem(
icon = Icons.Filled.ViewCarousel,
title = stringResource(id = R.string.settings_banner),
summary = stringResource(id = R.string.settings_banner_summary),
checked = useBanner
) {
prefs.edit().putBoolean("use_banner", it).apply()
useBanner = it
if (ksuVersion != null) {
SwitchItem(
icon = Icons.Filled.ViewCarousel,
title = stringResource(id = R.string.settings_banner),
summary = stringResource(id = R.string.settings_banner_summary),
checked = useBanner
) {
prefs.edit().putBoolean("use_banner", it).apply()
useBanner = it
}
}
var enableAmoled by rememberSaveable {

View File

@@ -111,11 +111,7 @@ fun HomeScreen(navigator: DestinationsNavigator) {
}
}
val moduleUpdateCount = moduleViewModel.moduleList.count {
moduleViewModel.checkUpdate(it).first.isNotEmpty()
}
StatusCard(kernelVersion, ksuVersion, lkmMode, moduleUpdateCount) {
StatusCard(kernelVersion, ksuVersion, lkmMode) {
navigator.navigate(InstallScreenDestination)
}
if (isManager && Natives.requireNewKernel()) {
@@ -332,8 +328,10 @@ private fun StatusCard(
intent.addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK)
if (ksuVersion != null) {
context.startActivity(intent)
} else {
} else if (kernelVersion.isGKI()) {
onClickInstall()
} else {
Toast.makeText(context, "Something weird happened... 🤔", Toast.LENGTH_SHORT).show()
}
} else if (ksuVersion == null && kernelVersion.isGKI()) {
onClickInstall()
@@ -428,22 +426,6 @@ private fun StatusCard(
text = stringResource(R.string.home_module_count, getModuleCount()),
style = MaterialTheme.typography.bodyMedium
)
if (moduleUpdateCount > 0) {
Text(
text = stringResource(R.string.home_module_update_count, moduleUpdateCount),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.primary
)
}
val suSFS = getSuSFS()
if (suSFS == "Supported") {
Text(
text = "SuSFS: " + stringResource(R.string.susfs_supported),
style = MaterialTheme.typography.bodyMedium
)
}
}
}
@@ -595,7 +577,7 @@ private fun InfoCard(autoExpand: Boolean = false) {
Spacer(Modifier.height(16.dp))
InfoCardItem(
label = stringResource(R.string.home_susfs_version),
content = "${getSuSFSVersion()} (${getSuSFSVariant()}) $susSUMode",
content = "${stringResource(R.string.susfs_supported)} | ${getSuSFSVersion()} (${getSuSFSVariant()}) $susSUMode",
icon = painterResource(R.drawable.ic_sus),
)
}

View File

@@ -234,7 +234,7 @@ private fun SelectInstallMethod(onSelected: (InstallMethod) -> Unit = {}) {
val rootAvailable = rootAvailable()
val isAbDevice = isAbDevice()
val selectFileTip = stringResource(
id = R.string.select_file_tip, if (isInitBoot()) "init_boot" else "boot"
id = R.string.select_file_tip, if (isInitBoot()) "init_boot/vendor_boot" else "boot"
)
val radioOptions =
mutableListOf<InstallMethod>(InstallMethod.SelectFile(summary = selectFileTip))

View File

@@ -123,6 +123,7 @@ import com.rifsxd.ksunext.ui.util.reboot
import com.rifsxd.ksunext.ui.util.toggleModule
import com.rifsxd.ksunext.ui.util.uninstallModule
import com.rifsxd.ksunext.ui.util.restoreModule
import com.rifsxd.ksunext.ui.util.zygiskRequired
import com.rifsxd.ksunext.ui.viewmodel.ModuleViewModel
import com.rifsxd.ksunext.ui.webui.WebUIActivity
import com.rifsxd.ksunext.ui.webui.WebUIXActivity
@@ -411,11 +412,13 @@ private fun ModuleList(
val rebootToApply = stringResource(R.string.reboot_to_apply)
val moduleStr = stringResource(R.string.module)
val uninstall = stringResource(R.string.uninstall)
val uninstalled = stringResource(R.string.uninstalled)
val restore = stringResource(R.string.restore)
val cancel = stringResource(android.R.string.cancel)
val moduleUninstallConfirm = stringResource(R.string.module_uninstall_confirm)
val moduleRestoreConfirm = stringResource(R.string.module_restore_confirm)
val updateText = stringResource(R.string.module_update)
val updateLable = stringResource(R.string.module_update_available)
val changelogText = stringResource(R.string.module_changelog)
val downloadingText = stringResource(R.string.module_downloading)
val startDownloadingText = stringResource(R.string.module_start_downloading)
@@ -783,6 +786,8 @@ fun ModuleItem(
)
}
val filterZygiskModules = zygiskAvailable() || !module.zygiskRequired
LaunchedEffect(Unit) {
developerOptionsEnabled = prefs.getBoolean("enable_developer_options", false)
}
@@ -818,7 +823,16 @@ fun ModuleItem(
)
if (module.remove) {
LabelItem(
text = stringResource(R.string.uninstall),
text = stringResource(R.string.uninstalled),
style = com.dergoogler.mmrl.ui.component.LabelItemDefaults.style.copy(
containerColor = MaterialTheme.colorScheme.errorContainer,
contentColor = MaterialTheme.colorScheme.onErrorContainer
)
)
}
if (!zygiskAvailable() && module.zygiskRequired && !module.remove) {
LabelItem(
text = stringResource(R.string.zygisk_required),
style = com.dergoogler.mmrl.ui.component.LabelItemDefaults.style.copy(
containerColor = MaterialTheme.colorScheme.errorContainer,
contentColor = MaterialTheme.colorScheme.onErrorContainer
@@ -827,9 +841,9 @@ fun ModuleItem(
}
if (updateUrl.isNotEmpty() && !module.remove && !module.update) {
LabelItem(
text = stringResource(R.string.module_update),
text = stringResource(R.string.module_update_available),
style = com.dergoogler.mmrl.ui.component.LabelItemDefaults.style.copy(
containerColor = MaterialTheme.colorScheme.onTertiary,
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
contentColor = MaterialTheme.colorScheme.onTertiaryContainer
)
)
@@ -846,7 +860,7 @@ fun ModuleItem(
}
}
if (module.enabled && !module.remove) {
if (module.hasWebUi) {
if (module.hasWebUi && filterZygiskModules) {
LabelItem(
text = stringResource(R.string.webui),
style = com.dergoogler.mmrl.ui.component.LabelItemDefaults.style.copy(
@@ -855,7 +869,7 @@ fun ModuleItem(
)
)
}
if (module.hasActionScript) {
if (module.hasActionScript && filterZygiskModules) {
LabelItem(
text = stringResource(R.string.action),
style = com.dergoogler.mmrl.ui.component.LabelItemDefaults.style.copy(
@@ -961,7 +975,7 @@ fun ModuleItem(
if (module.hasActionScript) {
FilledTonalButton(
modifier = Modifier.defaultMinSize(52.dp, 32.dp),
enabled = !module.remove && module.enabled,
enabled = !module.remove && module.enabled && filterZygiskModules,
onClick = {
navigator.navigate(ExecuteModuleActionScreenDestination(module.dirId))
viewModel.markNeedRefresh()
@@ -989,7 +1003,7 @@ fun ModuleItem(
if (module.hasWebUi) {
FilledTonalButton(
modifier = Modifier.defaultMinSize(52.dp, 32.dp),
enabled = !module.remove && module.enabled,
enabled = !module.remove && module.enabled && filterZygiskModules,
onClick = { onClick(module) },
interactionSource = interactionSource,
contentPadding = ButtonDefaults.TextButtonContentPadding
@@ -1012,7 +1026,7 @@ fun ModuleItem(
Spacer(modifier = Modifier.weight(1f, true))
if (updateUrl.isNotEmpty()) {
if (updateUrl.isNotEmpty() && !module.remove && !module.update) {
Button(
modifier = Modifier.defaultMinSize(52.dp, 32.dp),
enabled = !module.remove,
@@ -1089,6 +1103,7 @@ fun ModuleItem(
}
fun formatSize(size: Long): String {
if (size == 0L) return "null"
val kb = 1024
val mb = kb * 1024
val gb = mb * 1024
@@ -1118,7 +1133,8 @@ fun ModuleItemPreview() {
hasActionScript = false,
dirId = "dirId",
size = 12345678L,
banner = ""
banner = "",
zygiskRequired = false
)
ModuleItem(EmptyDestinationsNavigator, module, "", {}, {}, {}, {}, {}, false, {})
}

View File

@@ -349,18 +349,20 @@ fun SettingScreen(navigator: DestinationsNavigator) {
}
val developer = stringResource(id = R.string.developer)
ListItem(
leadingContent = {
Icon(
Icons.Filled.DeveloperBoard,
developer
)
},
headlineContent = { Text(developer) },
modifier = Modifier.clickable {
navigator.navigate(DeveloperScreenDestination)
}
)
if (ksuVersion != null) {
ListItem(
leadingContent = {
Icon(
Icons.Filled.DeveloperBoard,
developer
)
},
headlineContent = { Text(developer) },
modifier = Modifier.clickable {
navigator.navigate(DeveloperScreenDestination)
}
)
}
val lkmMode = Natives.version >= Natives.MINIMAL_SUPPORTED_KERNEL_LKM && Natives.isLkmMode
if (lkmMode) {

View File

@@ -92,7 +92,7 @@ fun SuperUserScreen(navigator: DestinationsNavigator) {
}
)
}, onClick = {
viewModel.showSystemApps = !viewModel.showSystemApps
viewModel.updateShowSystemApps(!viewModel.showSystemApps)
showDropdown = false
})
}
@@ -163,10 +163,18 @@ private fun AppItem(
LabelItem(
text = "CUSTOM",
style = LabelItemDefaults.style.copy(
containerColor = MaterialTheme.colorScheme.onTertiary,
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
contentColor = MaterialTheme.colorScheme.onTertiaryContainer,
)
)
} else if (!app.allowSu && !Natives.uidShouldUmount(app.uid)) {
LabelItem(
text = "DEFAULT",
style = LabelItemDefaults.style.copy(
containerColor = MaterialTheme.colorScheme.primaryContainer,
contentColor = MaterialTheme.colorScheme.onPrimaryContainer,
)
)
}
}
}

View File

@@ -28,6 +28,7 @@ import java.io.File
* @date 2023/1/1.
*/
private const val TAG = "KsuCli"
private const val BUSYBOX = "/data/adb/ksu/bin/busybox"
private fun ksuDaemonMagicPath(): String {
return ksuApp.applicationInfo.nativeLibraryDir + File.separator + "libksud_magic.so"
@@ -490,7 +491,7 @@ fun moduleBackup(): Boolean {
val internalBackupDir = "/sdcard/.ksunext/modules"
val internalBackupPath = "$internalBackupDir/$tarName"
val tarCmd = "tar -cpf $tarPath -C /data/adb/modules $(ls /data/adb/modules)"
val tarCmd = "$BUSYBOX tar -cpf $tarPath -C /data/adb/modules $(ls /data/adb/modules)"
val tarResult = ShellUtils.fastCmd(shell, tarCmd).trim()
if (tarResult.isNotEmpty()) return false
@@ -511,7 +512,7 @@ fun moduleRestore(): Boolean {
val tarPath = ShellUtils.fastCmd(shell, findTarCmd).trim()
if (tarPath.isEmpty()) return false
val extractCmd = "tar -xpf $tarPath -C /data/adb/modules_update"
val extractCmd = "$BUSYBOX tar -xpf $tarPath -C /data/adb/modules_update"
val extractResult = ShellUtils.fastCmd(shell, extractCmd).trim()
return extractResult.isEmpty()
}
@@ -533,7 +534,7 @@ fun allowlistBackup(): Boolean {
val internalBackupDir = "/sdcard/.ksunext/allowlist"
val internalBackupPath = "$internalBackupDir/$tarName"
val tarCmd = "tar -cpf $tarPath -C /data/adb/ksu .allowlist"
val tarCmd = "$BUSYBOX tar -cpf $tarPath -C /data/adb/ksu .allowlist"
val tarResult = ShellUtils.fastCmd(shell, tarCmd).trim()
if (tarResult.isNotEmpty()) return false
@@ -556,7 +557,7 @@ fun allowlistRestore(): Boolean {
if (tarPath.isEmpty()) return false
// Extract the tar to /data/adb/ksu (restores .allowlist folder with permissions)
val extractCmd = "tar -xpf $tarPath -C /data/adb/ksu"
val extractCmd = "$BUSYBOX tar -xpf $tarPath -C /data/adb/ksu"
val extractResult = ShellUtils.fastCmd(shell, extractCmd).trim()
return extractResult.isEmpty()
}
@@ -623,7 +624,7 @@ fun currentMountSystem(): String {
fun getModuleSize(dir: File): Long {
val shell = getRootShell()
val cmd = "du -sb '${dir.absolutePath}' | awk '{print \$1}'"
val cmd = "$BUSYBOX du -sb '${dir.absolutePath}' | awk '{print \$1}'"
val result = ShellUtils.fastCmd(shell, cmd).trim()
return result.toLongOrNull() ?: 0L
}
@@ -632,6 +633,39 @@ fun isSuCompatDisabled(): Boolean {
return Natives.version >= Natives.MINIMAL_SUPPORTED_SU_COMPAT && !Natives.isSuEnabled()
}
fun zygiskRequired(dir: File): Boolean {
val shell = getRootShell()
val zygiskLib = "${dir.absolutePath}/zygisk"
val cmd = "ls \"$zygiskLib\""
val result = ShellUtils.fastCmdResult(shell, cmd)
return result
}
fun zygiskAvailable(): Boolean {
val shell = getRootShell()
val zygiskLib = "libzygisk.so"
val rezygisk64 = "/data/adb/modules/rezygisk/lib64/$zygiskLib"
val rezygisk = "/data/adb/modules/rezygisk/lib/$zygiskLib"
val zygiskNext64 = "/data/adb/modules/zygisksu/lib64/$zygiskLib"
val zygiskNext = "/data/adb/modules/zygisksu/lib/$zygiskLib"
val cmdRezygisk64 = "[ -f \"$rezygisk64\" ]"
if (ShellUtils.fastCmdResult(shell, cmdRezygisk64)) {
return true
}
val cmdZygiskNext64 = "[ -f \"$zygiskNext64\" ]"
if (ShellUtils.fastCmdResult(shell, cmdZygiskNext64)) {
return true
}
val cmdRezygisk = "[ -f \"$rezygisk\" ]"
if (ShellUtils.fastCmdResult(shell, cmdRezygisk)) {
return true
}
val cmdZygiskNext = "[ -f \"$zygiskNext\" ]"
return ShellUtils.fastCmdResult(shell, cmdZygiskNext)
}
fun setAppProfileTemplate(id: String, template: String): Boolean {
val shell = getRootShell()
val escapedTemplate = template.replace("\"", "\\\"")

View File

@@ -0,0 +1,149 @@
package com.rifsxd.ksunext.ui.util
import android.annotation.TargetApi
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.net.Uri
import android.os.Build
import android.provider.Settings
import java.util.Locale
object LocaleHelper {
/**
* Check if should use system language settings (Android 13+)
*/
val useSystemLanguageSettings: Boolean
get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU
/**
* Launch system app locale settings (Android 13+)
*/
fun launchSystemLanguageSettings(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
try {
val intent = Intent(Settings.ACTION_APP_LOCALE_SETTINGS).apply {
data = Uri.fromParts("package", context.packageName, null)
}
context.startActivity(intent)
} catch (e: Exception) {
// Fallback to app language settings if system settings not available
}
}
}
/**
* Apply saved language setting to context (for Android < 13)
*/
fun applyLanguage(context: Context): Context {
// On Android 13+, language is handled by system
if (useSystemLanguageSettings) {
return context
}
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
val localeTag = prefs.getString("app_locale", "system") ?: "system"
return if (localeTag == "system") {
context
} else {
val locale = parseLocaleTag(localeTag)
setLocale(context, locale)
}
}
/**
* Set locale for context (Android < 13)
*/
private fun setLocale(context: Context, locale: Locale): Context {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
updateResources(context, locale)
} else {
updateResourcesLegacy(context, locale)
}
}
@TargetApi(Build.VERSION_CODES.N)
private fun updateResources(context: Context, locale: Locale): Context {
val configuration = Configuration()
configuration.setLocale(locale)
configuration.setLayoutDirection(locale)
return context.createConfigurationContext(configuration)
}
@SuppressWarnings("deprecation")
private fun updateResourcesLegacy(context: Context, locale: Locale): Context {
Locale.setDefault(locale)
val resources = context.resources
val configuration = resources.configuration
configuration.locale = locale
configuration.setLayoutDirection(locale)
resources.updateConfiguration(configuration, resources.displayMetrics)
return context
}
/**
* Parse locale tag to Locale object
*/
private fun parseLocaleTag(tag: String): Locale {
return try {
if (tag.contains("_")) {
val parts = tag.split("_")
Locale.Builder()
.setLanguage(parts[0])
.setRegion(parts.getOrNull(1) ?: "")
.build()
} else {
Locale.Builder()
.setLanguage(tag)
.build()
}
} catch (e: Exception) {
Locale.getDefault()
}
}
/**
* Restart activity to apply language change (Android < 13)
*/
fun restartActivity(context: Context) {
if (context is Activity && !useSystemLanguageSettings) {
context.recreate()
}
}
/**
* Get current app locale
*/
fun getCurrentAppLocale(context: Context): Locale? {
return if (useSystemLanguageSettings) {
// Android 13+ - get from system app locale settings
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
try {
val localeManager = context.getSystemService(Context.LOCALE_SERVICE) as? android.app.LocaleManager
val locales = localeManager?.applicationLocales
if (locales != null && !locales.isEmpty) {
locales.get(0)
} else {
null // System default
}
} catch (e: Exception) {
null // System default
}
} else {
null // System default
}
} else {
// Android < 13 - get from SharedPreferences
val prefs = context.getSharedPreferences("settings", Context.MODE_PRIVATE)
val localeTag = prefs.getString("app_locale", "system") ?: "system"
if (localeTag == "system") {
null // System default
} else {
parseLocaleTag(localeTag)
}
}
}
}

View File

@@ -23,6 +23,8 @@ import com.rifsxd.ksunext.ksuApp
import com.rifsxd.ksunext.ui.util.HanziToPinyin
import com.rifsxd.ksunext.ui.util.listModules
import com.rifsxd.ksunext.ui.util.getModuleSize
import com.rifsxd.ksunext.ui.util.zygiskRequired
import com.rifsxd.ksunext.ui.util.zygiskAvailable
import org.json.JSONArray
import org.json.JSONObject
@@ -48,7 +50,8 @@ class ModuleViewModel : ViewModel() {
val hasActionScript: Boolean,
val dirId: String,
val size: Long,
val banner: String
val banner: String,
val zygiskRequired: Boolean
)
data class ModuleUpdateInfo(
@@ -139,6 +142,7 @@ class ModuleViewModel : ViewModel() {
val dirId = obj.getString("dir_id")
val moduleDir = File("/data/adb/modules/$dirId")
val size = getModuleSize(moduleDir)
val zygiskRequired = zygiskRequired(moduleDir)
ModuleInfo(
id,
@@ -155,7 +159,8 @@ class ModuleViewModel : ViewModel() {
obj.optBoolean("action"),
dirId,
size,
obj.optString("banner")
obj.optString("banner"),
zygiskRequired
)
}.toList()
isNeedRefresh = false

View File

@@ -1,5 +1,9 @@
package com.rifsxd.ksunext.ui.viewmodel
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.os.Parcelable
@@ -24,6 +28,9 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.withTimeoutOrNull
import java.text.Collator
import java.util.*
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
import androidx.core.content.edit
class SuperUserViewModel : ViewModel() {
val isPlatformAlive get() = Platform.isAlive
@@ -61,11 +68,19 @@ class SuperUserViewModel : ViewModel() {
}
}
private val prefs = ksuApp.getSharedPreferences("settings", Context.MODE_PRIVATE)!!
var search by mutableStateOf("")
var showSystemApps by mutableStateOf(false)
var showSystemApps by mutableStateOf(prefs.getBoolean("show_system_apps", false))
private set
var isRefreshing by mutableStateOf(false)
private set
fun updateShowSystemApps(newValue: Boolean) {
showSystemApps = newValue
prefs.edit { putBoolean("show_system_apps", newValue) }
}
private val sortedList by derivedStateOf {
val comparator = compareBy<AppInfo> {
when {

View File

@@ -9,7 +9,7 @@
<string name="issue_report_telegram_link">https://t.me/ksunext</string>
<string name="confirm">Potwierdź</string>
<string name="app_name" translatable="false">KernelSU Next</string>
<string name="home">Strona główna</string>
<string name="home">Główna</string>
<string name="home_not_installed">Niezainstalowany</string>
<string name="home_click_to_install">Kliknij, aby zainstalować</string>
<string name="lkm_mode_deprecated">Tryb LKM jest już przestarzały!</string>
@@ -22,7 +22,7 @@
<string name="home_failure">Nie znaleziono sygnatury KernelSU Next v2 w jądrze! [ !KSU_NEXT || != size/hash ]</string>
<string name="home_failure_tip">Poproś swojego programistę jądra o integrację KernelSU Next!</string>
<string name="home_kernel">Wersja jądra</string>
<string name="hook_mode">Tryb hookowania</string>
<string name="hook_mode">Rodzaj hooków</string>
<string name="enable">Aktywuj</string>
<string name="disable">Dezaktywuj</string>
<string name="enabled">Aktywny</string>
@@ -61,7 +61,7 @@
<string name="reboot_bootloader">Reboot do trybu Bootloader</string>
<string name="reboot_download">Reboot do trybu Download</string>
<string name="reboot_edl">Reboot do trybu EDL</string>
<string name="about">O autorze</string>
<string name="about">O aplikacji</string>
<string name="module_uninstall_confirm">Czy na pewno chcesz odinstalować moduł %s?</string>
<string name="module_uninstall_success">%s został odinstalowany</string>
<string name="module_uninstall_failed">Nie można odinstalować: %s</string>
@@ -94,7 +94,7 @@
<string name="reboot_message">Zmiany zaczną obowiązywać po restarcie systemu. Czy chcesz teraz uruchomić ponownie system?</string>
<string name="module_restore">Przywróć moduły z kopii zapasowej</string>
<string name="module_restore_message">Przywróć moduły z ostatniej kopii zapasowej.</string>
<string name="backup_restore">Tworzenie / przywracanie kopii zapasowej</string>
<string name="backup_restore">Tworzenie i przywracanie kopii zapasowej</string>
<string name="module_backup">Utwórz kopię zapasową modułów</string>
<string name="module_backup_message">Utwórz kopię zapasową obecnie zainstalowanych modułów.</string>
<string name="allowlist_restore">Przywróć listę zgód z kopii zapasowej</string>

View File

@@ -19,6 +19,7 @@
<string name="home_superuser_count">SuperUsuários: %d</string>
<string name="home_module_count">Módulos: %d</string>
<string name="home_module_update_count">Atualizações: %d</string>
<string name="module_update_available">Atualizar</string>
<string name="home_failure">Assinatura KernelSU Next v2 não encontrada no kernel! [ !KSU_NEXT || != size/hash ]</string>
<string name="home_failure_tip">Peça ao seu desenvolvedor de kernel para integrar o KernelSU Next!</string>
<string name="home_kernel">Versão do kernel</string>
@@ -211,6 +212,7 @@
<string name="settings_disable_su">Desativar compatibilidade su</string>
<string name="settings_disable_su_summary">Desative temporariamente a capacidade de qualquer app obter privilégios root por meio do comando su (processos root existentes não serão afetados).</string>
<string name="settings_language">Idioma</string>
<string name="system_default">Padrão do sistema</string>
<string name="settings_legacyui">Usar IU antiga</string>
<string name="settings_legacyui_summary">Mude para o estilo de interface do usuário anterior.</string>
<string name="settings_banner">Ativar banners</string>
@@ -220,4 +222,7 @@
<string name="use_webuix_eruda">Injetar Eruda no WebUI X</string>
<string name="use_webuix_eruda_summary">Injeta um console de depuração no WebUI X para facilitar a depuração. Requer que a depuração web esteja ativada.</string>
<string name="customization">Personalização</string>
<string name="developer">Desenvolvedor</string>
<string name="sucompat_disabled">SUCOMPAT DESATIVADO</string>
<string name="zygisk_required">Zygisk necessário</string>
</resources>

View File

@@ -15,10 +15,10 @@
<string name="lkm_mode_deprecated">Режим LKM теперь устарел!</string>
<string name="lkm_alternative_suggestion">Установите GKI ядро или интегрируйте ядра рядом с вашим устройством.</string>
<string name="home_working">Работает</string>
<string name="home_working_version">Версия: %d</string>
<string name="home_superuser_count">Superusers: %d</string>
<string name="home_module_count">Модули: %d</string>
<string name="home_module_update_count">Обновления: %d</string>
<string name="home_working_version">Версия драйвера: %d</string>
<string name="home_superuser_count">Выдано прав: %d</string>
<string name="home_module_count">Модулей: %d</string>
<string name="home_module_update_count">Обновлений: %d</string>
<string name="home_failure">Подпись KernelSU Next v2 не найдена в ядре! [!KSU_NEXT || != size/hash]</string>
<string name="home_failure_tip">Попросите вашего разработчика ядра интегрировать KernelSU Next!</string>
<string name="home_kernel">Версия ядра</string>
@@ -44,11 +44,11 @@
<string name="module_failed_to_disable">Не удалось отключить модуль: %s</string>
<string name="module_empty">Нет установленных модулей</string>
<string name="module">Модули</string>
<string name="module_install_prompt_with_name">Следующие модуль(и) будут установлены: %1$s</string>
<string name="module_install_prompt_with_name">Следующие модули будут установлены: %1$s</string>
<string name="module_sort_a_to_z">Сортировать (А → Я)</string>
<string name="module_sort_z_to_a">Сортировать (Я → А)</string>
<string name="module_size_low_to_high">Сортировать (Низкий → Высокий)</string>
<string name="module_size_high_to_low">Сортировать (Высокий → Низкий)</string>
<string name="module_size_low_to_high">Сортировать (Меньше → Больше)</string>
<string name="module_size_high_to_low">Сортировать (Больше → Меньше)</string>
<string name="uninstall">Удалить</string>
<string name="restore">Восстановить</string>
<string name="module_install">Установить</string>
@@ -180,9 +180,9 @@
<string name="settings_check_update">Проверять обновления</string>
<string name="settings_check_update_summary">Автоматически проверять обновления при открытии приложения.</string>
<string name="grant_root_failed">Не удалось предоставить root-доступ!</string>
<string name="action">Запустить</string>
<string name="action">Скрипт</string>
<string name="webui">WebUI</string>
<string name="open">Открыть</string>
<string name="open">WebUI</string>
<string name="enable_web_debugging">Отладка WebView</string>
<string name="enable_web_debugging_summary">Можно использовать для отладки WebUI. Включайте только при необходимости.</string>
<string name="direct_install">Прямая установка (Рекомендуется)</string>
@@ -205,17 +205,21 @@
<string name="flash_success">Прошивка выполнена успешно</string>
<string name="flash_failed">Ошибка прошивки</string>
<string name="selected_lkm">Выбран LKM: %s</string>
<string name="save_log">Сохранить логи</string>
<string name="save_log">Сохранить\nлоги</string>
<string name="log_saved">Логи сохранены</string>
<string name="send_log">Поделиться логами</string>
<string name="send_log">Поделиться\nлогами</string>
<string name="settings_disable_su">Откл. совместимость с su</string>
<string name="settings_disable_su_summary">Временно отключить возможность приложениям получать права root через команду su (существующие процессы с правами root не будут затронуты).</string>
<string name="settings_language">Язык</string>
<string name="settings_legacyui">Использовать старый UI</string>
<string name="settings_legacyui_summary">Переключиться на предыдущий стиль интерфейса.</string>
<string name="settings_banner">Включить баннеры</string>
<string name="settings_banner_summary">Показывать фоновые баннеры для модулей.</string>
<string name="use_webuix">Использовать WebUI X</string>
<string name="use_webuix_summary">Использовать WebUI X вместо WebUI, который поддерживает больше API.</string>
<string name="use_webuix_eruda">Инжект Eruda в WebUI X</string>
<string name="use_webuix_eruda_summary">Инжектить консоль отладки в WebUI X, чтобы упростить отладку. Требуется включить отладку WebView.</string>
<string name="customization">Кастомизация</string>
<string name="developer">Для разработчиков</string>
<string name="sucompat_disabled">SUCOMPAT ОТКЛЮЧЕН</string>
</resources>

View File

@@ -15,14 +15,16 @@
<string name="lkm_mode_deprecated">Режим LKM тепер застарілий!</string>
<string name="lkm_alternative_suggestion">Встановіть ядро GKI або інтегруйте ядра поруч із вашим пристроєм.</string>
<string name="home_working">Працює</string>
<string name="home_working_version">Версія: %d</string>
<string name="home_superuser_count">SuperUsers: %d</string>
<string name="home_module_count">Модулі: %d</string>
<string name="home_working_version">Версія драйвера: %d</string>
<string name="home_superuser_count">Видано прав: %d</string>
<string name="home_module_count">Модулів: %d</string>
<string name="home_module_update_count">Оновлення: %d</string>
<string name="home_failure">Підпис KernelSU Next v2 не знайдено в ядрі! [!KSU_NEXT || != size/hash]</string>
<string name="home_failure_tip">Попросіть вашого розробника ядра інтегрувати KernelSU Next!</string>
<string name="home_kernel">Версія ядра</string>
<string name="hook_mode">Режим хуків</string>
<string name="enable">Увімкнути</string>
<string name="disable">Вимкнути</string>
<string name="disabled">Вимкнено</string>
<string name="enabled">Увімкнено</string>
<string name="susfs_supported">Доступно</string>
@@ -42,18 +44,18 @@
<string name="module_failed_to_disable">Не вдалося вимкнути модуль: %s</string>
<string name="module_empty">Нема встановлених модулів</string>
<string name="module">Модулі</string>
<string name="module_install_prompt_with_name">Наступні модуль(і) будуть встановлені: %1$s</string>
<string name="module_install_prompt_with_name">Наступні модулі будуть встановлені: %1$s</string>
<string name="module_sort_a_to_z">Сортувати (А-Я)</string>
<string name="module_sort_z_to_a">Сортувати (Я-А)</string>
<string name="module_size_low_to_high">Сортувати (Низький → Великий)</string>
<string name="module_size_high_to_low">Сортувати (Великий → Низький)</string>
<string name="module_size_low_to_high">Сортувати (Менше → Більше)</string>
<string name="module_size_high_to_low">Сортувати (Більше → Менше)</string>
<string name="uninstall">Видалити</string>
<string name="restore">Відновити</string>
<string name="module_install">Встановити</string>
<string name="install">Встановити</string>
<string name="reboot">Перезавантаження</string>
<string name="uninstalled">Видалено</string>
<string name="settings">Налаштування</string>
<string name="settings">Параметри</string>
<string name="reboot_userspace">М’яка перезавантаження</string>
<string name="reboot_recovery">Перезавантаження в Recovery</string>
<string name="reboot_bootloader">Перезавантаження в Bootloader</string>
@@ -116,7 +118,7 @@
<string name="home_experimental_kernelsu_body_point_3"> • Лише для тестування: призначено для користувачів, які розуміють ризики і готові вирішувати виникаючі проблеми.</string>
<string name="about_source_code"><![CDATA[Вихідний код на %1$s]]></string>
<string name="profile" translatable="false">Профіль додатка</string>
<string name="profile_default">За замовчуванням</string>
<string name="profile_default">Дефолт</string>
<string name="profile_template">Шаблон</string>
<string name="profile_custom">Налаштувати</string>
<string name="profile_name">Ім’я профілю</string>
@@ -178,9 +180,9 @@
<string name="settings_check_update">Перевіряти оновлення</string>
<string name="settings_check_update_summary">Автоматично перевіряти оновлення при відкритті додатка.</string>
<string name="grant_root_failed">Не вдалося надати root-доступ!</string>
<string name="action">Запустити</string>
<string name="action">Скрипт</string>
<string name="webui">WebUI</string>
<string name="open">Відкрити</string>
<string name="open">WebUI</string>
<string name="enable_web_debugging">Відладка WebView</string>
<string name="enable_web_debugging_summary">Можна використовувати для відладки WebUI. Увімкніть лише при необхідності.</string>
<string name="direct_install">Пряма установка (Рекомендовано)</string>
@@ -203,9 +205,9 @@
<string name="flash_success">Прошивка виконана успішно</string>
<string name="flash_failed">Помилка прошивки</string>
<string name="selected_lkm">Вибрано LKM: %s</string>
<string name="save_log">Зберегти логи</string>
<string name="save_log">Зберегти\nлоги</string>
<string name="log_saved">Логи збережено</string>
<string name="send_log">Поділитися логами</string>
<string name="send_log">Поділитися\nлогами</string>
<string name="settings_disable_su">Вимкн. сумісність із su</string>
<string name="settings_disable_su_summary">Тимчасово вимкнути можливість додаткам отримувати права root через команду su (існуючі процеси з правами root не будуть зачеплені).</string>
<string name="settings_language">Мова</string>
@@ -218,5 +220,6 @@
<string name="use_webuix_eruda">Інжект Eruda у WebUI X</string>
<string name="use_webuix_eruda_summary">Інжектити консоль відладки у WebUI X, щоб спростити відладку. Потрібно увімкнути відладку WebView.</string>
<string name="customization">Кастомізація</string>
<string name="developer">Розробник</string>
<string name="developer">Для Розробників</string>
<string name="sucompat_disabled">SUCOMPAT ВИМКНЕНО</string>
</resources>

View File

@@ -14,21 +14,26 @@
<string name="home_click_to_install">Bấm để cài đặt</string>
<string name="lkm_mode_deprecated">Chế độ LKM không còn được hỗ trợ!</string>
<string name="lkm_alternative_suggestion">Cài đặt kernel GKI hoặc tự nhúng KernelSU Next vào kernel của bạn.</string>
<string name="home_working">Đã cài đặt và hoạt động</string>
<string name="home_working">Đã cài đặt</string>
<string name="home_working_version">Phiên bản: %d</string>
<string name="home_superuser_count">Ứng dụng đã cấp su: %d</string>
<string name="home_module_count">Module: %d</string>
<string name="home_module_update_count">Module có cập nhật: %d</string>
<string name="home_failure">Không tìm thấy chữ kí KernelSU Next v2 (chưa cài đặt, nhúng sai cách hoặc nhầm trình quản lý?) [ !KSU_NEXT || != size/hash ]</string>
<string name="home_failure_tip">Hỏi hoặc tự nhúng KernelSU Next vào kernel của bạn!</string>
<string name="home_failure_tip">Hỏi hoặc tự nhúng nhân KernelSU Next vào kernel của bạn!</string>
<string name="home_kernel">Phiên bản kernel</string>
<string name="enabled">Kích hoạt</string>
<string name="disabled">Vô hiệu hoá</string>
<string name="hook_mode">Chế độ hook</string>
<string name="enable">Kích hoạt</string>
<string name="disable">Vô hiệu hóa</string>
<string name="enabled">Đã kích hoạt</string>
<string name="disabled">Đã vô hiệu hoá</string>
<string name="susfs_supported">Hỗ trợ</string>
<string name="home_susfs">SuSFS: %s</string>
<string name="home_susfs_version">Phiên bản SuSFS</string>
<string name="home_susfs_sus_su">SuS SU</string>
<string name="home_android">Phiên bản Android</string>
<string name="home_manager_version">Phiên bản trình quản lý</string>
<string name="home_abi">ABI</string>
<string name="home_selinux_status">Trạng thái SELinux</string>
<string name="selinux_status_disabled">Vô hiệu hoá</string>
<string name="selinux_status_enforcing">Enforcing</string>
@@ -42,13 +47,16 @@
<string name="module_install_prompt_with_name">Bạn có THẬT SỰ muốn cài module này không (kiểm tra trước khi cài ASAF) %1$s?</string>
<string name="module_sort_a_to_z">Sắp xếp (A-Z)</string>
<string name="module_sort_z_to_a">Sắp xếp (Z-A)</string>
<string name="module_size_low_to_high">Sắp xếp (Thấp → Cao)</string>
<string name="module_size_high_to_low">Sắp xếp (Cao → Thấp)</string>
<string name="uninstall">Gỡ cài đặt</string>
<string name="restore">Khôi phục</string>
<string name="module_install">Cài đặt</string>
<string name="install">Cài đặt</string>
<string name="reboot">Khởi động lại</string>
<string name="uninstalled">Đã gỡ cài đặt</string>
<string name="settings">Cài đặt</string>
<string name="reboot_userspace">Khởi động mềm</string>
<string name="reboot_userspace">Khởi động TẠM</string>
<string name="reboot_recovery">Vào Recovery</string>
<string name="reboot_bootloader">Vào Bootloader</string>
<string name="reboot_download">Vào Download Mode</string>
@@ -66,7 +74,7 @@
<string name="module_version_code">Code</string>
<string name="module_update_json">UpdateJson</string>
<string name="module_update_json_empty">Trống</string>
<string name="enable_developer_options">Kích hoạt tính năng dành cho nhà phát triển</string>
<string name="enable_developer_options">Cài đặt dành cho nhà phát triển</string>
<string name="enable_developer_options_summary">Hiện thị những cài đặt ẩn và nhũng log DÀNH RIÊNG CHO NHÀ PHÁT TRIỂN.</string>
<string name="module_overlay_fs_not_available">Không thể sử dụng module vì flag Overlayfs không được kích hoạt trong kernel, vui lòng kích hoạt</string>
<string name="refresh">Làm mới</string>
@@ -98,6 +106,7 @@
<string name="proceed">Tiếp tục</string>
<string name="cancel">Huỷ</string>
<string name="later">Để sau</string>
<string name="lkm_warning_message">Bản vá LKM này có những thành phần mã nguồn đóng, bạn có muốn tiếp tục không</string>
<string name="home_next_kernelsu">🔥 Bản dựng tiếp theo</string>
<string name="home_next_kernelsu_repo">https://github.com/KernelSU-Next/KernelSU-Next</string>
<string name="home_next_kernelsu_body">Nhánh thử nghiệm tiếp theo. Xem trong GitHub!</string>
@@ -108,7 +117,7 @@
<string name="home_experimental_kernelsu_body_point_2"> • Không bảo hành: Các nhà phát triển KernelSU Next và bên thứ 3 sẽ không chịu trách nghiệm cho hardbrick, bootloop, mất dữ liệu,...</string>
<string name="home_experimental_kernelsu_body_point_3"> • Đùng để thử nghiệm là chính: Chỉ dành cho những người có kĩ năng, kinh nghiệm, trải nghiệm .</string>
<string name="about_source_code"><![CDATA[Xem mã nguồn tại %1$s]]></string>
<string name="profile" translatable="false">Hồ sơ ứng dụng</string>
<string name="profile" translatable="false">App Profile</string>
<string name="profile_default">Mặc định</string>
<string name="profile_template">Mẫu</string>
<string name="profile_custom">Tuỳ biến</string>
@@ -131,6 +140,7 @@
<string name="profile_selinux_domain">Tên miền</string>
<string name="profile_selinux_rules">Quyền</string>
<string name="module_update">Có cập nhật!</string>
<string name="module_updated">Đã cập nhật</string>
<string name="module_downloading">Tải xuống module: %s</string>
<string name="module_start_downloading">Đang tải xuống module: %s</string>
<string name="new_version_available">Phiên bản %s đã ra mắt, bấm để cập nhật.</string>
@@ -138,6 +148,10 @@
<string name="close">Đóng</string>
<string name="force_stop_app">Buộc tắt</string>
<string name="restart_app">Khởi động lại</string>
<string name="settings_amoled_mode">Chế độ AMOLED</string>
<string name="settings_amoled_mode_summary">Kích hoạt nền đen tuyền, tốt cho mắt và pin trên những thiết bị sử dụng màn AMOLED/OLED.</string>
<string name="restart_required">Yêu cầu khởi động lại</string>
<string name="restart_app_message">Ứng dụng yêu cầu khởi động lại để có hiệu lực.</string>
<string name="failed_to_update_sepolicy">Cập nhật phân quyền SELinux thất bại cho: %s</string>
<string name="su_not_allowed">Cấp quyền SU không được phép cho: %s</string>
<string name="module_changelog">Nhật kí thay đổi</string>
@@ -162,13 +176,14 @@
<string name="app_profile_template_sync">Đồng bộ với hồ sơ ứng dụng trực tuyến</string>
<string name="app_profile_template_save_failed">Lưu hồ sơ ứng dụng thất bại</string>
<string name="app_profile_template_import_empty">Bảng nhớ tạm đang trống hoặc sai thông tin!</string>
<string name="module_changelog_failed">Đọc nhất kí thay đôi thất bại: %s</string>
<string name="module_changelog_failed">Lấy nhất kí thay đổi module thất bại: %s</string>
<string name="settings_check_update">Kiểm tra cập nhật</string>
<string name="settings_check_update_summary">Tự động kiểm tra cập nhật khi mở ứng dụng.</string>
<string name="grant_root_failed">Cấp quyền root thất bại!</string>
<string name="action">Chạy</string>
<string name="webui">WebUI</string>
<string name="open">Mở</string>
<string name="enable_web_debugging">Kích hoạt WebView debugging</string>
<string name="enable_web_debugging">Kích hoạt gỡ lỗi WebView</string>
<string name="enable_web_debugging_summary">Sử dụng để debug WebUI. Sử dụng khi bạn có kinh nghiệm, kĩ năng.</string>
<string name="direct_install">Cài đặt trực tiếp (cho GKI 2.0)</string>
<string name="select_file">Chọn file</string>
@@ -193,11 +208,18 @@
<string name="save_log">Lưu logs</string>
<string name="log_saved">Logs đã được lưu</string>
<string name="send_log">Chia sẻ logs</string>
<string name="settings_disable_su">Vô hiệu hoá khả năng của lệnh SU</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="settings_language">Ngôn ngữ</string>
<string name="settings_legacyui">Sử dụng giao diện cũ</string>
<string name="settings_legacyui_summary">Chuyển về giao diện cũ.</string>
<string name="settings_banner">Hiện thị ảnh bìa</string>
<string name="settings_banner_summary">Hiện thị ảnh bìa trong module.</string>
<string name="use_webuix">Dùng WebUI X</string>
<string name="use_webuix_summary">Dùng WebUI X thay vì WebUI (hỗ trợ nhiều API hơn).</string>
<string name="use_webuix_eruda">Nhúng nhân Eruda vào WebUI X</string>
<string name="use_webuix_eruda_summary">Nhúng trình gỡ lỗi vào WebUI X để việc sửa lỗi thuận tiện hơn. Yêu cầu gỡ lỗi webview được bật.</string>
<string name="customization">Tùy biến</string>
<string name="developer">Cài đặt nhà phát triển</string>
<string name="sucompat_disabled">ĐÃ VÔ HIỆU HÓA SUCOMPAT</string>
</resources>

View File

@@ -18,7 +18,7 @@
<string name="home_working_version">版本:%d</string>
<string name="home_superuser_count">超级用户数:%d</string>
<string name="home_module_count">模块数:%d</string>
<string name="home_module_update_count">可更新模块: %d</string>
<string name="home_module_update_count">%d 个模块可更新!</string>
<string name="home_failure">内核中未找到 KernelSU Next V2 签名! [ !KSU_NEXT || != size/hash ]</string>
<string name="home_failure_tip">请让你的内核开发人员集成 KernelSU Next</string>
<string name="home_kernel">内核版本</string>
@@ -47,19 +47,19 @@
<string name="module_install_prompt_with_name">是否要继续安装模块 %1$s </string>
<string name="module_sort_a_to_z">按 A - Z 排序</string>
<string name="module_sort_z_to_a">按 Z - A 排序</string>
<string name="module_size_low_to_high">按模块大小正序排序</string>
<string name="module_size_high_to_low">按模块大小倒序排序</string>
<string name="module_size_low_to_high">正序排序(按模块大小</string>
<string name="module_size_high_to_low">倒序排序(按模块大小</string>
<string name="uninstall">卸载</string>
<string name="restore">恢复</string>
<string name="module_install">安装</string>
<string name="install">安装</string>
<string name="reboot">重启</string>
<string name="uninstalled">卸载</string>
<string name="uninstalled">重启以应用卸载</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_recovery">重启到恢复模式</string>
<string name="reboot_bootloader">重启到 Bootloader 模式</string>
<string name="reboot_download">重启到下载模式</string>
<string name="reboot_edl">重启到 EDL 模式</string>
<string name="about">关于</string>
<string name="module_uninstall_confirm">确定要卸载模块 %s 吗?</string>
@@ -76,14 +76,14 @@
<string name="module_update_json_empty">无更新配置</string>
<string name="enable_developer_options">启用开发者模式</string>
<string name="enable_developer_options_summary">显示隐藏的开发者专用设置和调试信息。</string>
<string name="module_overlay_fs_not_available">OverlayFS 被内核禁用,模块系统不可用</string>
<string name="module_overlay_fs_not_available">OverlayFS 被内核禁用,模块系统不可用</string>
<string name="refresh">刷新</string>
<string name="show_system_apps">显示系统应用</string>
<string name="hide_system_apps">隐藏系统应用</string>
<string name="export_log">导出日志</string>
<string name="safe_mode">安全模式</string>
<string name="reboot_to_apply">重启生效</string>
<string name="module_magisk_conflict">由于与 Magisk 冲突,模块系统不可用</string>
<string name="module_magisk_conflict">由于与 Magisk 冲突,模块系统不可用</string>
<string name="home_mount_system">模块系统</string>
<string name="home_magic_mount">Magic Mount</string>
<string name="home_overlayfs_mount">OverlayFS</string>
@@ -101,19 +101,19 @@
<string name="allowlist_backup_message">备份当前的超级用户列表</string>
<string name="module_backup">备份模块</string>
<string name="module_backup_message">备份当前已安装的模块。</string>
<string name="warning">警告</string>
<string name="warning">免责声明</string>
<string name="warning_message">此为尚在开发阶段的实验功能,继续操作前请确保模块已备份。使用此功能需要先了解其风险,在知晓后果的情况下谨慎操作。</string>
<string name="proceed">继续</string>
<string name="cancel">取消</string>
<string name="later">稍后</string>
<string name="lkm_warning_message">此 LKM 补丁依赖于闭源组件,你确定要继续吗?</string>
<string name="lkm_warning_message">此 LKM 补丁依赖于闭源组件,确认操作代表你知晓因继续使用该功能所导致的一切后果与开发团队无关。</string>
<string name="home_next_kernelsu">🔥 Next 构建</string>
<string name="home_next_kernelsu_repo">https://github.com/KernelSU-Next/KernelSU-Next</string>
<string name="home_next_kernelsu_body">Next 实验性分支。在 GitHub 上查看!</string>
<string name="home_experimental_kernelsu">⚠️ 实验性开发警告!</string>
<string name="home_experimental_kernelsu_repo">127.0.0.1</string>
<string name="home_experimental_kernelsu_body">KernelSU Next 属于第三方版本,保持着积极的实验开发。该版本所得即所用,不保证稳定性、性能和可靠性。</string>
<string name="home_experimental_kernelsu_body_point_1"> • 风险自担:可能会发生崩溃、意外行为或导致系统故障。</string>
<string name="home_experimental_kernelsu_body_point_1"> • 风险自担:可能会出现崩溃、意外行为或导致系统故障。</string>
<string name="home_experimental_kernelsu_body_point_2"> • 不做保证:开发者不对数据丢失、系统损坏等问题负责。</string>
<string name="home_experimental_kernelsu_body_point_3"> • 仅供测试:此版适合了解风险并能轻松解决问题的用户。</string>
<string name="about_source_code"><![CDATA[在 %1$s 查看源代码]]></string>
@@ -140,7 +140,8 @@
<string name="profile_selinux_domain"></string>
<string name="profile_selinux_rules">规则</string>
<string name="module_update">更新</string>
<string name="module_updated">更新</string>
<string name="module_update_available">更新可用</string>
<string name="module_updated">重启以应用更新</string>
<string name="module_downloading">正在下载模块: %s</string>
<string name="module_start_downloading">开始下载: %s</string>
<string name="new_version_available">发现新版本:%s点击升级。</string>
@@ -178,9 +179,9 @@
<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="action">执行</string>
<string name="webui">WebUI</string>
<string name="open">打开</string>
<string name="enable_web_debugging">启用 WebView 调试</string>
@@ -211,13 +212,16 @@
<string name="settings_disable_su">禁用超级用户指令</string>
<string name="settings_disable_su_summary">临时禁止任何应用通过 su 命令获取 root 权限(正在运行的 root 进程不受影响)。</string>
<string name="settings_language">语言</string>
<string name="system_default">系统默认</string>
<string name="settings_legacyui">切换到传统 UI</string>
<string name="settings_legacyui_summary">使用早期的用户界面风格。</string>
<string name="settings_banner">启用横幅</string>
<string name="settings_banner_summary">针对模块启用背景横幅。</string>
<string name="use_webuix">使用 WebUI X</string>
<string name="use_webuix_summary">使用更完善的 WebUI X 取代旧有 WebUI请注意模块开发者可在模块信息中单独覆盖此项设置。</string>
<string name="use_webuix_summary">使用更完善的 WebUI X 取代旧有 WebUI请注意模块开发者可在模块信息中单独覆盖此项设置。</string>
<string name="use_webuix_eruda">对 WebUI X 注入 Eruda</string>
<string name="use_webuix_eruda_summary">使用 WebUI X 时注入控制台以便于调试,需要启用 WebView 调试功能。</string>
<string name="customization">自定义</string>
<string name="customization">外观</string>
<string name="developer">开发者选项</string>
<string name="sucompat_disabled">SUCOMPAT 已禁用</string>
</resources>

View File

@@ -47,14 +47,14 @@
<string name="module_install_prompt_with_name">是否要繼續安裝模組 %1$s </string>
<string name="module_sort_a_to_z">按 A - Z 排序</string>
<string name="module_sort_z_to_a">按 Z - A 排序</string>
<string name="module_size_low_to_high">按模組大小正序排序</string>
<string name="module_size_high_to_low">按模組大小倒序排序</string>
<string name="module_size_low_to_high">正序排序(按模組大小</string>
<string name="module_size_high_to_low">倒序排序(按模組大小</string>
<string name="uninstall">卸載</string>
<string name="restore">恢復</string>
<string name="module_install">安裝</string>
<string name="install">安裝</string>
<string name="reboot">重啟</string>
<string name="uninstalled">已經卸載</string>
<string name="uninstalled">重啓以完成卸載</string>
<string name="settings">設定</string>
<string name="reboot_userspace">軟重啟</string>
<string name="reboot_recovery">重啟到 Recovery</string>
@@ -140,7 +140,8 @@
<string name="profile_selinux_domain"></string>
<string name="profile_selinux_rules">規則</string>
<string name="module_update">更新</string>
<string name="module_updated">已經更新</string>
<string name="module_update_available">更新可用</string>
<string name="module_updated">重啓以完成更新</string>
<string name="module_downloading">正在下載模組: %s</string>
<string name="module_start_downloading">開始下載: %s</string>
<string name="new_version_available">發現新版本:%s點擊升級。</string>
@@ -178,9 +179,9 @@
<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">Action</string>
<string name="action">執行</string>
<string name="webui">WebUI</string>
<string name="open">打開</string>
<string name="enable_web_debugging">啟用 WebView 偵錯</string>
@@ -211,6 +212,7 @@
<string name="settings_disable_su">關閉 SU 相容</string>
<string name="settings_disable_su_summary">暫時禁止任何應用通過 su 命令獲取 root 權限(已運行的 root 進程不受影響)。</string>
<string name="settings_language">語言</string>
<string name="system_default">跟隨系統</string>
<string name="settings_legacyui">換回傳統 UI</string>
<string name="settings_legacyui_summary">使用早期版本 UI 風格。</string>
<string name="settings_banner">啓用橫幅</string>
@@ -219,5 +221,7 @@
<string name="use_webuix_summary">使用更爲完善的 WebUI X 而不是 WebUI請注意模組開發人員能在模組信息中覆寫這個設置。</string>
<string name="use_webuix_eruda">將 Eruda 注入 WebUI X</string>
<string name="use_webuix_eruda_summary">在使用 WebUI X 時注入控制台以便於偵錯,需要啟用 WebView 偵錯功能。</string>
<string name="customization">客制化</string>
<string name="customization">自訂外觀</string>
<string name="developer">開發人員設定</string>
<string name="sucompat_disabled">SUCOMPAT 已經禁用</string>
</resources>

View File

@@ -56,7 +56,7 @@
<string name="reboot">Reboot</string>
<string name="uninstalled">Uninstalled</string>
<string name="settings">Settings</string>
<string name="reboot_userspace">Soft Reboot</string>
<string name="reboot_userspace">Soft reboot</string>
<string name="reboot_recovery">Reboot to Recovery</string>
<string name="reboot_bootloader">Reboot to Bootloader</string>
<string name="reboot_download">Reboot to Download</string>
@@ -112,7 +112,7 @@
<string name="home_next_kernelsu_body">Next experimental branch. Check it out on GitHub!</string>
<string name="home_experimental_kernelsu">⚠️ Experimental development warning!</string>
<string name="home_experimental_kernelsu_repo">127.0.0.1</string>
<string name="home_experimental_kernelsu_body">KernelSU Next is a non-official version that is always under active experimental development. It is provided as-is, with no guarantees of stability, performance, or reliability.</string>
<string name="home_experimental_kernelsu_body">KernelSU Next is a non-official version that is always under active experimental development. It\'s provided as-is, with no guarantees of stability, performance, or reliability.</string>
<string name="home_experimental_kernelsu_body_point_1"> • Use at your own risk: crashes, unexpected behavior, or system issues may occur.</string>
<string name="home_experimental_kernelsu_body_point_2"> • No warranty: the developers aren\'t responsible for any data loss, system damage, or other consequences resulting from its use.</string>
<string name="home_experimental_kernelsu_body_point_3"> • For testing purposes only: intended for users who understand the risks and are comfortable troubleshooting issues.</string>
@@ -140,6 +140,7 @@
<string name="profile_selinux_domain">Domain</string>
<string name="profile_selinux_rules">Rules</string>
<string name="module_update">Update</string>
<string name="module_update_available">Update</string>
<string name="module_updated">Updated</string>
<string name="module_downloading">Downloading module: %s</string>
<string name="module_start_downloading">Start downloading: %s</string>
@@ -211,7 +212,8 @@
<string name="settings_disable_su">Disable su compatibility</string>
<string name="settings_disable_su_summary">Temporarily disable the ability of any app to gain root privileges via the su command (existing root processes won\'t be affected).</string>
<string name="settings_language">Language</string>
<string name="settings_legacyui">Use Legacy UI</string>
<string name="system_default">System default</string>
<string name="settings_legacyui">Use legacy UI</string>
<string name="settings_legacyui_summary">Switch to the previous user interface style.</string>
<string name="settings_banner">Enable banners</string>
<string name="settings_banner_summary">Show background banners for modules.</string>
@@ -222,4 +224,5 @@
<string name="customization">Customization</string>
<string name="developer">Developer</string>
<string name="sucompat_disabled">SUCOMPAT DISABLED</string>
<string name="zygisk_required">Zygisk required</string>
</resources>

View File

@@ -167,6 +167,36 @@ fn do_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> {
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");
let status = Command::new(magiskboot)
.current_dir(workdir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg("cpio")
.arg(vendor_init_boot_cpio)
.arg(cmd)
.status()?;
ensure!(status.success(), "magiskboot cpio {} failed", cmd);
Ok(())
}
fn do_vendor_ramdisk_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> {
let vendor_ramdisk_cpio = workdir.join("vendor_ramdisk").join("ramdisk.cpio");
let status = Command::new(magiskboot)
.current_dir(workdir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg("cpio")
.arg(vendor_ramdisk_cpio)
.arg(cmd)
.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)
@@ -179,6 +209,32 @@ fn is_magisk_patched(magiskboot: &Path, workdir: &Path) -> Result<bool> {
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");
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_magisk_patched_vendor_ramdisk(magiskboot: &Path, workdir: &Path) -> Result<bool> {
let vendor_ramdisk_cpio = workdir.join("vendor_ramdisk").join("ramdisk.cpio");
let status = Command::new(magiskboot)
.current_dir(workdir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.args(["cpio", vendor_ramdisk_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)
@@ -190,6 +246,38 @@ fn is_kernelsu_patched(magiskboot: &Path, workdir: &Path) -> Result<bool> {
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",
])
.status()?;
Ok(status.success())
}
fn is_kernelsu_patched_vendor_ramdisk(magiskboot: &Path, workdir: &Path) -> Result<bool> {
let vendor_ramdisk_cpio = workdir.join("vendor_ramdisk").join("ramdisk.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",
])
.status()?;
Ok(status.success())
}
fn dd<P: AsRef<Path>, Q: AsRef<Path>>(ifile: P, ofile: Q) -> Result<()> {
let status = Command::new("dd")
.stdout(Stdio::null())
@@ -234,9 +322,22 @@ pub fn restore(
.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();
let no_vendor_ramdisk = !workdir
.join("vendor_ramdisk")
.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)?;
let is_kernelsu_patched_vendor_ramdisk =
is_kernelsu_patched_vendor_ramdisk(&magiskboot, workdir)?;
ensure!(
is_kernelsu_patched,
is_kernelsu_patched || is_kernelsu_patched_vendor_init_boot || is_kernelsu_patched_vendor_ramdisk,
"boot image is not patched by KernelSU Next"
);
@@ -270,16 +371,44 @@ pub fn restore(
}
if new_boot.is_none() {
// remove kernelsu.ko
do_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?;
if no_ramdisk {
if !no_vendor_init_boot {
// vendor init_boot restore
do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?;
// if init.real exists, restore it
let status = do_cpio_cmd(&magiskboot, workdir, "exists init.real").is_ok();
if status {
do_cpio_cmd(&magiskboot, workdir, "mv init.real init")?;
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 if !no_vendor_ramdisk {
// vendor ramdisk restore
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?;
let status =
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "exists init.real").is_ok();
if status {
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "mv init.real init")?;
} else {
let vendor_ramdisk = workdir.join("vendor_ramdisk").join("ramdisk.cpio");
std::fs::remove_file(vendor_ramdisk)?;
}
}
} else {
let ramdisk = workdir.join("ramdisk.cpio");
std::fs::remove_file(ramdisk)?;
// remove kernelsu.ko
do_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?;
// if init.real exists, restore it
let status = do_cpio_cmd(&magiskboot, workdir, "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)?;
}
}
println!("- Repacking boot image");
@@ -441,11 +570,6 @@ 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)
@@ -457,28 +581,73 @@ fn do_patch(
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();
let no_vendor_ramdisk = !workdir
.join("vendor_ramdisk")
.join("ramdisk.cpio")
.exists();
if no_ramdisk && no_vendor_init_boot && no_vendor_ramdisk {
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)?;
let is_magisk_patched_vendor_ramdisk =
is_magisk_patched_vendor_ramdisk(&magiskboot, workdir)?;
ensure!(
no_ramdisk || !is_magisk_patched,
!is_magisk_patched || !is_magisk_patched_vendor_init_boot || !is_magisk_patched_vendor_ramdisk,
"Cannot work with Magisk patched image"
);
println!("- Adding KernelSU Next 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_vendor_ramdisk =
is_kernelsu_patched_vendor_ramdisk(&magiskboot, workdir)?;
let mut need_backup = false;
if !is_kernelsu_patched {
// kernelsu.ko is not exist, backup init if necessary
let status = do_cpio_cmd(&magiskboot, workdir, "exists init");
if status.is_ok() {
do_cpio_cmd(&magiskboot, workdir, "mv init init.real")?;
if !is_kernelsu_patched || (no_ramdisk && !is_kernelsu_patched_vendor_init_boot) || (no_ramdisk && no_vendor_init_boot && !is_kernelsu_patched_vendor_ramdisk)
{
if no_ramdisk {
if !no_vendor_init_boot {
// 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 !no_vendor_ramdisk {
// vendor ramdisk patching
let status = do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "exists init");
if status.is_ok() {
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "mv init init.real")?;
}
}
} else {
// kernelsu.ko is not exist, backup init if necessary
let status = do_cpio_cmd(&magiskboot, workdir, "exists init");
if status.is_ok() {
do_cpio_cmd(&magiskboot, workdir, "mv init init.real")?;
}
need_backup = flash;
}
need_backup = flash;
}
do_cpio_cmd(&magiskboot, workdir, "add 0755 init init")?;
do_cpio_cmd(&magiskboot, workdir, "add 0755 kernelsu.ko kernelsu.ko")?;
if no_ramdisk {
if !no_vendor_init_boot {
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 if !no_vendor_ramdisk {
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "add 0750 init init")?;
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "add 0750 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")?;
}
#[cfg(target_os = "android")]
if need_backup {
@@ -658,8 +827,12 @@ fn find_boot_image(
let init_boot_exist =
Path::new(&format!("/dev/block/by-name/init_boot{slot_suffix}")).exists();
let vendor_boot_exist =
Path::new(&format!("/dev/block/by-name/vendor_boot{slot_suffix}")).exists();
let boot_partition = if !is_replace_kernel && init_boot_exist && !skip_init {
format!("/dev/block/by-name/init_boot{slot_suffix}")
} else if !is_replace_kernel && vendor_boot_exist && !skip_init {
format!("/dev/block/by-name/vendor_boot{slot_suffix}")
} else {
format!("/dev/block/by-name/boot{slot_suffix}")
};

View File

@@ -167,6 +167,36 @@ fn do_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> {
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");
let status = Command::new(magiskboot)
.current_dir(workdir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg("cpio")
.arg(vendor_init_boot_cpio)
.arg(cmd)
.status()?;
ensure!(status.success(), "magiskboot cpio {} failed", cmd);
Ok(())
}
fn do_vendor_ramdisk_cpio_cmd(magiskboot: &Path, workdir: &Path, cmd: &str) -> Result<()> {
let vendor_ramdisk_cpio = workdir.join("vendor_ramdisk").join("ramdisk.cpio");
let status = Command::new(magiskboot)
.current_dir(workdir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg("cpio")
.arg(vendor_ramdisk_cpio)
.arg(cmd)
.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)
@@ -179,6 +209,32 @@ fn is_magisk_patched(magiskboot: &Path, workdir: &Path) -> Result<bool> {
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");
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_magisk_patched_vendor_ramdisk(magiskboot: &Path, workdir: &Path) -> Result<bool> {
let vendor_ramdisk_cpio = workdir.join("vendor_ramdisk").join("ramdisk.cpio");
let status = Command::new(magiskboot)
.current_dir(workdir)
.stdout(Stdio::null())
.stderr(Stdio::null())
.args(["cpio", vendor_ramdisk_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)
@@ -190,6 +246,38 @@ fn is_kernelsu_patched(magiskboot: &Path, workdir: &Path) -> Result<bool> {
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",
])
.status()?;
Ok(status.success())
}
fn is_kernelsu_patched_vendor_ramdisk(magiskboot: &Path, workdir: &Path) -> Result<bool> {
let vendor_ramdisk_cpio = workdir.join("vendor_ramdisk").join("ramdisk.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",
])
.status()?;
Ok(status.success())
}
fn dd<P: AsRef<Path>, Q: AsRef<Path>>(ifile: P, ofile: Q) -> Result<()> {
let status = Command::new("dd")
.stdout(Stdio::null())
@@ -234,9 +322,22 @@ pub fn restore(
.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();
let no_vendor_ramdisk = !workdir
.join("vendor_ramdisk")
.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)?;
let is_kernelsu_patched_vendor_ramdisk =
is_kernelsu_patched_vendor_ramdisk(&magiskboot, workdir)?;
ensure!(
is_kernelsu_patched,
is_kernelsu_patched || is_kernelsu_patched_vendor_init_boot || is_kernelsu_patched_vendor_ramdisk,
"boot image is not patched by KernelSU Next"
);
@@ -270,16 +371,44 @@ pub fn restore(
}
if new_boot.is_none() {
// remove kernelsu.ko
do_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?;
if no_ramdisk {
if !no_vendor_init_boot {
// vendor init_boot restore
do_vendor_init_boot_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?;
// if init.real exists, restore it
let status = do_cpio_cmd(&magiskboot, workdir, "exists init.real").is_ok();
if status {
do_cpio_cmd(&magiskboot, workdir, "mv init.real init")?;
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 if !no_vendor_ramdisk {
// vendor ramdisk restore
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?;
let status =
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "exists init.real").is_ok();
if status {
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "mv init.real init")?;
} else {
let vendor_ramdisk = workdir.join("vendor_ramdisk").join("ramdisk.cpio");
std::fs::remove_file(vendor_ramdisk)?;
}
}
} else {
let ramdisk = workdir.join("ramdisk.cpio");
std::fs::remove_file(ramdisk)?;
// remove kernelsu.ko
do_cpio_cmd(&magiskboot, workdir, "rm kernelsu.ko")?;
// if init.real exists, restore it
let status = do_cpio_cmd(&magiskboot, workdir, "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)?;
}
}
println!("- Repacking boot image");
@@ -301,7 +430,7 @@ pub fn restore(
let output_dir = std::env::current_dir()?;
let now = chrono::Utc::now();
let output_image = output_dir.join(format!(
"kernelsu_restore_{}.img",
"kernelsu_next_restore_{}.img",
now.format("%Y%m%d_%H%M%S")
));
@@ -441,11 +570,6 @@ 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)
@@ -457,28 +581,73 @@ fn do_patch(
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();
let no_vendor_ramdisk = !workdir
.join("vendor_ramdisk")
.join("ramdisk.cpio")
.exists();
if no_ramdisk && no_vendor_init_boot && no_vendor_ramdisk {
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)?;
let is_magisk_patched_vendor_ramdisk =
is_magisk_patched_vendor_ramdisk(&magiskboot, workdir)?;
ensure!(
no_ramdisk || !is_magisk_patched,
!is_magisk_patched || !is_magisk_patched_vendor_init_boot || !is_magisk_patched_vendor_ramdisk,
"Cannot work with Magisk patched image"
);
println!("- Adding KernelSU Next 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_vendor_ramdisk =
is_kernelsu_patched_vendor_ramdisk(&magiskboot, workdir)?;
let mut need_backup = false;
if !is_kernelsu_patched {
// kernelsu.ko is not exist, backup init if necessary
let status = do_cpio_cmd(&magiskboot, workdir, "exists init");
if status.is_ok() {
do_cpio_cmd(&magiskboot, workdir, "mv init init.real")?;
if !is_kernelsu_patched || (no_ramdisk && !is_kernelsu_patched_vendor_init_boot) || (no_ramdisk && no_vendor_init_boot && !is_kernelsu_patched_vendor_ramdisk)
{
if no_ramdisk {
if !no_vendor_init_boot {
// 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 !no_vendor_ramdisk {
// vendor ramdisk patching
let status = do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "exists init");
if status.is_ok() {
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "mv init init.real")?;
}
}
} else {
// kernelsu.ko is not exist, backup init if necessary
let status = do_cpio_cmd(&magiskboot, workdir, "exists init");
if status.is_ok() {
do_cpio_cmd(&magiskboot, workdir, "mv init init.real")?;
}
need_backup = flash;
}
need_backup = flash;
}
do_cpio_cmd(&magiskboot, workdir, "add 0755 init init")?;
do_cpio_cmd(&magiskboot, workdir, "add 0755 kernelsu.ko kernelsu.ko")?;
if no_ramdisk {
if !no_vendor_init_boot {
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 if !no_vendor_ramdisk {
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "add 0750 init init")?;
do_vendor_ramdisk_cpio_cmd(&magiskboot, workdir, "add 0750 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")?;
}
#[cfg(target_os = "android")]
if need_backup {
@@ -658,8 +827,12 @@ fn find_boot_image(
let init_boot_exist =
Path::new(&format!("/dev/block/by-name/init_boot{slot_suffix}")).exists();
let vendor_boot_exist =
Path::new(&format!("/dev/block/by-name/vendor_boot{slot_suffix}")).exists();
let boot_partition = if !is_replace_kernel && init_boot_exist && !skip_init {
format!("/dev/block/by-name/init_boot{slot_suffix}")
} else if !is_replace_kernel && vendor_boot_exist && !skip_init {
format!("/dev/block/by-name/vendor_boot{slot_suffix}")
} else {
format!("/dev/block/by-name/boot{slot_suffix}")
};

View File

@@ -119,50 +119,50 @@ int main(int argc, char *argv[]) {
ptr_buf += str_len;
}
if (enabled_features & (1 << 5)) {
str_len = strlen("CONFIG_KSU_SUSFS_SUS_OVERLAYFS\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_SUS_OVERLAYFS\n", str_len);
ptr_buf += str_len;
}
if (enabled_features & (1 << 6)) {
str_len = strlen("CONFIG_KSU_SUSFS_TRY_UMOUNT\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_TRY_UMOUNT\n", str_len);
ptr_buf += str_len;
}
if (enabled_features & (1 << 7)) {
if (enabled_features & (1 << 6)) {
str_len = strlen("CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT\n", str_len);
ptr_buf += str_len;
}
if (enabled_features & (1 << 8)) {
if (enabled_features & (1 << 7)) {
str_len = strlen("CONFIG_KSU_SUSFS_SPOOF_UNAME\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_SPOOF_UNAME\n", str_len);
ptr_buf += str_len;
}
if (enabled_features & (1 << 9)) {
if (enabled_features & (1 << 8)) {
str_len = strlen("CONFIG_KSU_SUSFS_ENABLE_LOG\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_ENABLE_LOG\n", str_len);
ptr_buf += str_len;
}
if (enabled_features & (1 << 10)) {
if (enabled_features & (1 << 9)) {
str_len = strlen("CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS\n", str_len);
ptr_buf += str_len;
}
if (enabled_features & (1 << 11)) {
if (enabled_features & (1 << 10)) {
str_len = strlen("CONFIG_KSU_SUSFS_SPOOF_BOOTCONFIG\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_SPOOF_BOOTCONFIG\n", str_len);
ptr_buf += str_len;
}
if (enabled_features & (1 << 12)) {
if (enabled_features & (1 << 11)) {
str_len = strlen("CONFIG_KSU_SUSFS_OPEN_REDIRECT\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_OPEN_REDIRECT\n", str_len);
ptr_buf += str_len;
}
if (enabled_features & (1 << 13)) {
if (enabled_features & (1 << 12)) {
str_len = strlen("CONFIG_KSU_SUSFS_SUS_SU\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_SUS_SU\n", str_len);
ptr_buf += str_len;
}
if (enabled_features & (1 << 13)) {
str_len = strlen("CONFIG_KSU_SUSFS_HAS_MAGIC_MOUNT\n");
strncpy(ptr_buf, "CONFIG_KSU_SUSFS_HAS_MAGIC_MOUNT\n", str_len);
ptr_buf += str_len;
}
printf("%s", enabled_features_buf);
free(enabled_features_buf);
} else {