6 Commits

Author SHA1 Message Date
ShirkNeko
b8b75349e0 kernel: Bump KSU_VERSION_API to 3.1.9 2025-08-24 11:45:08 +08:00
dabao1955
3f3e520641 kernel: Remove unnecessary strip in CONFIG_KSU_TRACEPOINT_HOOK check (#353)
The 'strip' function is redundant when checking Kconfig variables, as
values from CONFIG options (like CONFIG_KSU_TRACEPOINT_HOOK) are already
trimmed and do not contain leading/trailing whitespace.

Simplify the condition for better readability and maintainability:
  - Remove unnecessary $(strip ...)
  - Add consistent spacing around the comma

This change aligns with kernel Makefile conventions and improves code clarity
without altering behavior.

Signed-off-by: dabao1955 <dabao1955@163.com>
2025-08-24 11:41:18 +08:00
OukaroMF
21aa58f1ee docs: EN: add simple translation for tracepoint-hook docs (#352)
* add EN translation for tracepoint-hook

* add EN translation for tracepoint-hook
2025-08-24 11:38:04 +08:00
ShirkNeko
da164ebeed kernel: Modified dynamic signature All files have been renamed to the correct names: Dynamic Manager 2025-08-24 11:00:17 +08:00
YC酱luyancib
35ae324df5 docs: zh: Introduce tracepoint hook documents (#350)
* docs: Introduce tracepoint hook documents

Still working in progress...
Only zh-cn.

* fix broken link

* format

* 更新 tracepoint-hook.md

* format

* 更新 tracepoint-hook.md

* docs: zh: fmt

Signed-off-by: WenHao2130 <wenhao2130@outlook.com>

---------

Signed-off-by: WenHao2130 <wenhao2130@outlook.com>
Co-authored-by: WenHao2130 <wenhao2130@outlook.com>
2025-08-24 00:06:13 +08:00
rsuntk
fdda1f4ec0 ksud: strip resetprop
* Upstream resetprop is intentionally not stripped.

Signed-off-by: rsuntk <rsuntk@yukiprjkt.my.id>
2025-08-23 17:03:31 +08:00
42 changed files with 1297 additions and 831 deletions

View File

@@ -24,6 +24,12 @@ Prerequisites: open source bootable kernel.
- Requires [`guide/how-to-integrate.md`](guide/how-to-integrate.md)
- Requires [https://github.com/~](https://github.com/tiann/KernelSU/blob/main/website/docs/guide/how-to-integrate-for-non-gki.md#manually-modify-the-kernel-source)
3. **Tracepoint Hook:**
- Hook method introduced since SukiSU commit [49b01aad](https://github.com/SukiSU-Ultra/SukiSU-Ultra/commit/49b01aad74bcca6dba5a8a2e053bb54b648eb124)
- Requires `CONFIG_KSU_TRACEPOINT_HOOK=y`
- Requires [`guide/tracepoint-hook.md`](tracepoint-hook.md)
<!-- This part refer to [rsuntk/KernelSU](https://github.com/rsuntk/KernelSU). -->
If you're able to build a bootable kernel, there are two ways to integrate KernelSU into the kernel source code:

View File

@@ -0,0 +1,270 @@
# Tracepoint Hook Integration
## Introduction
Since commit [49b01aad](https://github.com/SukiSU-Ultra/SukiSU-Ultra/commit/49b01aad74bcca6dba5a8a2e053bb54b648eb124), SukiSU has introduced Tracepoint Hook
This Hook theoretically has lower performance overhead compared to Kprobes Hook, but is inferior to Manual Hook / Syscall Hook
> [!NOTE]
> This tutorial references the syscall hook v1.4 version from [backslashxx/KernelSU#5](https://github.com/backslashxx/KernelSU/issues/5), as well as the original KernelSU's [Manual Hook](https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source)
## Guide
### execve Hook (`exec.c`)
Generally need to modify the `do_execve` and `compat_do_execve` methods in `fs/exec.c`
```patch
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -78,6 +78,10 @@
#include <trace/hooks/sched.h>
#endif
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+#include <../drivers/kernelsu/ksu_trace.h>
+#endif
+
EXPORT_TRACEPOINT_SYMBOL_GPL(task_rename);
static int bprm_creds_from_file(struct linux_binprm *bprm);
@@ -2037,6 +2041,9 @@ static int do_execve(struct filename *filename,
{
struct user_arg_ptr argv = { .ptr.native = __argv };
struct user_arg_ptr envp = { .ptr.native = __envp };
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_execveat_hook((int *)AT_FDCWD, &filename, &argv, &envp, 0);
+#endif
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}
@@ -2064,6 +2071,9 @@ static int compat_do_execve(struct filename *filename,
.is_compat = true,
.ptr.compat = __envp,
};
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_execveat_sucompat_hook((int *)AT_FDCWD, &filename, NULL, NULL, NULL); /* 32-bit su */
+#endif
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}
```
### faccessat Hook (`open.c`)
Generally need to modify the `do_faccessat` method in `/fs/open.c`
```patch
--- a/fs/open.c
+++ b/fs/open.c
@@ -37,6 +37,10 @@
#include "internal.h"
#include <trace/hooks/syscall_check.h>
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+#include <../drivers/kernelsu/ksu_trace.h>
+#endif
+
int do_truncate(struct user_namespace *mnt_userns, struct dentry *dentry,
loff_t length, unsigned int time_attrs, struct file *filp)
{
@@ -468,6 +472,9 @@ static long do_faccessat(int dfd, const char __user *filename, int mode, int fla
SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
{
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_faccessat_hook(&dfd, &filename, &mode, NULL);
+#endif
return do_faccessat(dfd, filename, mode, 0);
}
```
If there's no `do_faccessat` method, you can find the `faccessat` SYSCALL definition (for kernels earlier than 4.17)
```patch
--- a/fs/open.c
+++ b/fs/open.c
@@ -31,6 +31,9 @@
#include <linux/ima.h>
#include <linux/dnotify.h>
#include <linux/compat.h>
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+#include <../drivers/kernelsu/ksu_trace.h>
+#endif
#include "internal.h"
@@ -369,6 +372,9 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
int res;
unsigned int lookup_flags = LOOKUP_FOLLOW;
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_faccessat_hook(&dfd, &filename, &mode, NULL);
+#endif
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
return -EINVAL;
```
### sys_read Hook (`read_write.c`)
Need to modify the `sys_read` method in `fs/read_write.c` (4.19 and above)
```patch
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -25,6 +25,10 @@
#include <linux/uaccess.h>
#include <asm/unistd.h>
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+#include <../drivers/kernelsu/ksu_trace.h>
+#endif
+
const struct file_operations generic_ro_fops = {
.llseek = generic_file_llseek,
.read_iter = generic_file_read_iter,
@@ -630,6 +634,9 @@ ssize_t ksys_read(unsigned int fd, char __user *buf, size_t count)
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_sys_read_hook(fd, &buf, &count);
+#endif
return ksys_read(fd, buf, count);
}
```
Or the `read` SYSCALL definition (4.14 and below)
```patch
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -25,6 +25,11 @@
#include <linux/uaccess.h>
#include <asm/unistd.h>
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+#include <../drivers/kernelsu/ksu_trace.h>
+#endif
+
+
const struct file_operations generic_ro_fops = {
.llseek = generic_file_llseek,
.read_iter = generic_file_read_iter,
@@ -575,6 +580,9 @@ SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
if (f.file) {
loff_t pos = file_pos_read(f.file);
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_sys_read_hook(fd, &buf, &count);
+#endif
ret = vfs_read(f.file, buf, count, &pos);
if (ret >= 0)
file_pos_write(f.file, pos);
```
### fstatat Hook (`stat.c`)
Need to modify the `newfstatat` SYSCALL definition in `stat.c`
If 32-bit support is needed, also need to modify the `statat64` SYSCALL definition
```patch
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -24,6 +24,10 @@
#include "internal.h"
#include "mount.h"
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+#include <../drivers/kernelsu/ksu_trace.h>
+#endif
+
/**
* generic_fillattr - Fill in the basic attributes from the inode struct
* @mnt_userns: user namespace of the mount the inode was found from
@@ -408,6 +412,10 @@ SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename,
struct kstat stat;
int error;
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_stat_hook(&dfd, &filename, &flag);
+#endif
+
error = vfs_fstatat(dfd, filename, &stat, flag);
if (error)
return error;
@@ -559,6 +567,10 @@ SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,
struct kstat stat;
int error;
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_stat_hook(&dfd, &filename, &flag); /* 32-bit su support */
+#endif
+
error = vfs_fstatat(dfd, filename, &stat, flag);
if (error)
return error;
```
### input Hook (`input.c`, for entering KSU built-in security mode)
Need to modify the `input_event` method in `drivers/input/input.c`, not `input_handle_event`
```patch
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -26,6 +26,10 @@
#include "input-compat.h"
#include "input-poller.h"
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+#include <../../drivers/kernelsu/ksu_trace.h>
+#endif
+
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("Input core");
MODULE_LICENSE("GPL");
@@ -451,6 +455,10 @@ void input_event(struct input_dev *dev,
{
unsigned long flags;
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_input_hook(&type, &code, &value);
+#endif
+
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
```
### devpts Hook (`pty.c`)
Need to modify the `pts_unix98_lookup` method in `drivers/tty/pty.c`
```patch
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -31,6 +31,10 @@
#include <linux/compat.h>
#include "tty.h"
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+#include <../../drivers/kernelsu/ksu_trace.h>
+#endif
+
#undef TTY_DEBUG_HANGUP
#ifdef TTY_DEBUG_HANGUP
# define tty_debug_hangup(tty, f, args...) tty_debug(tty, f, ##args)
@@ -707,6 +711,10 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
{
struct tty_struct *tty;
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_devpts_hook((struct inode *)file->f_path.dentry->d_inode);
+#endif
+
mutex_lock(&devpts_mutex);
tty = devpts_get_priv(file->f_path.dentry);
mutex_unlock(&devpts_mutex);
```

View File

@@ -21,9 +21,15 @@ SukiSU 可以集成到 GKI 和 non-GKI 内核中,并且已反向移植到 4.14
<!-- - backslashxx's syscall manual hook: https://github.com/backslashxx/KernelSU/issues/5 (v1.5 version is not available at the moment, if you want to use it, please use v1.4 version, or standard KernelSU hooks)-->
- 需要 `CONFIG_KSU_MANUAL_HOOK=y`
- 需要 [`guide/how-to-integrate.md`](guide/how-to-integrate.md)
- 需要 [`guide/how-to-integrate.md`](how-to-integrate.md)
- 需要 [https://github.com/~](https://github.com/tiann/KernelSU/blob/main/website/docs/guide/how-to-integrate-for-non-gki.md#manually-modify-the-kernel-source)
3. **Tracepoint Hook:**
- 自 SukiSU commit [49b01aad](https://github.com/SukiSU-Ultra/SukiSU-Ultra/commit/49b01aad74bcca6dba5a8a2e053bb54b648eb124) 引入的 hook 方法
- 需要 `CONFIG_KSU_TRACEPOINT_HOOK=y`
- 需要 [`guide/tracepoint-hook.md`](tracepoint-hook.md)
<!-- This part refer to [rsuntk/KernelSU](https://github.com/rsuntk/KernelSU). -->
如果您能够构建可启动内核,有两种方法可以将 KernelSU 集成到内核源代码中:

View File

@@ -0,0 +1,270 @@
# Tracepoint Hook 集成
## 介绍
自 commit [49b01aad](https://github.com/SukiSU-Ultra/SukiSU-Ultra/commit/49b01aad74bcca6dba5a8a2e053bb54b648eb124) 起SukiSU 引入了 Tracepoint Hook
该 Hook 理论上相比于 Kprobes Hook性能开销更小但次于 Manual Hook / Syscall Hook
> [!NOTE]
> 本教程参考了 [backslashxx/KernelSU#5](https://github.com/backslashxx/KernelSU/issues/5) 的 syscall hook v1.4 版本钩子,以及原版 KernelSU 的 [Manual Hook](https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source)
## Guide
### execve 钩子(`exec.c`
一般需要修改 `fs/exec.c``do_execve``compat_do_execve` 方法
```patch
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -78,6 +78,10 @@
#include <trace/hooks/sched.h>
#endif
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+#include <../drivers/kernelsu/ksu_trace.h>
+#endif
+
EXPORT_TRACEPOINT_SYMBOL_GPL(task_rename);
static int bprm_creds_from_file(struct linux_binprm *bprm);
@@ -2037,6 +2041,9 @@ static int do_execve(struct filename *filename,
{
struct user_arg_ptr argv = { .ptr.native = __argv };
struct user_arg_ptr envp = { .ptr.native = __envp };
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_execveat_hook((int *)AT_FDCWD, &filename, &argv, &envp, 0);
+#endif
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}
@@ -2064,6 +2071,9 @@ static int compat_do_execve(struct filename *filename,
.is_compat = true,
.ptr.compat = __envp,
};
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_execveat_sucompat_hook((int *)AT_FDCWD, &filename, NULL, NULL, NULL); /* 32-bit su */
+#endif
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}
```
### faccessat 钩子 (`open.c`)
一般需要修改 `/fs/open.c``do_faccessat` 方法
```patch
--- a/fs/open.c
+++ b/fs/open.c
@@ -37,6 +37,10 @@
#include "internal.h"
#include <trace/hooks/syscall_check.h>
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+#include <../drivers/kernelsu/ksu_trace.h>
+#endif
+
int do_truncate(struct user_namespace *mnt_userns, struct dentry *dentry,
loff_t length, unsigned int time_attrs, struct file *filp)
{
@@ -468,6 +472,9 @@ static long do_faccessat(int dfd, const char __user *filename, int mode, int fla
SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
{
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_faccessat_hook(&dfd, &filename, &mode, NULL);
+#endif
return do_faccessat(dfd, filename, mode, 0);
}
```
如果没有 `do_faccessat` 方法,可以找 `faccessat` 的 SYSCALL 定义(对于早于 4.17 的内核)
```patch
--- a/fs/open.c
+++ b/fs/open.c
@@ -31,6 +31,9 @@
#include <linux/ima.h>
#include <linux/dnotify.h>
#include <linux/compat.h>
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+#include <../drivers/kernelsu/ksu_trace.h>
+#endif
#include "internal.h"
@@ -369,6 +372,9 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
int res;
unsigned int lookup_flags = LOOKUP_FOLLOW;
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_faccessat_hook(&dfd, &filename, &mode, NULL);
+#endif
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
return -EINVAL;
```
### sys_read 钩子 ( `read_write.c` )
需要修改 `fs/read_write.c``sys_read` 方法4.19 及以上)
```patch
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -25,6 +25,10 @@
#include <linux/uaccess.h>
#include <asm/unistd.h>
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+#include <../drivers/kernelsu/ksu_trace.h>
+#endif
+
const struct file_operations generic_ro_fops = {
.llseek = generic_file_llseek,
.read_iter = generic_file_read_iter,
@@ -630,6 +634,9 @@ ssize_t ksys_read(unsigned int fd, char __user *buf, size_t count)
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_sys_read_hook(fd, &buf, &count);
+#endif
return ksys_read(fd, buf, count);
}
```
或者是 `read` 的 SYSCALL 定义4.14 及以下)
```patch
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -25,6 +25,11 @@
#include <linux/uaccess.h>
#include <asm/unistd.h>
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+#include <../drivers/kernelsu/ksu_trace.h>
+#endif
+
+
const struct file_operations generic_ro_fops = {
.llseek = generic_file_llseek,
.read_iter = generic_file_read_iter,
@@ -575,6 +580,9 @@ SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
if (f.file) {
loff_t pos = file_pos_read(f.file);
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_sys_read_hook(fd, &buf, &count);
+#endif
ret = vfs_read(f.file, buf, count, &pos);
if (ret >= 0)
file_pos_write(f.file, pos);
```
### fstatat 钩子 ( `stat.c` )
需要修改 `stat.c``newfstatat` SYSCALL 定义
如果需要 32 位支持,还需要修改 `statat64` SYSCALL 定义
```patch
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -24,6 +24,10 @@
#include "internal.h"
#include "mount.h"
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+#include <../drivers/kernelsu/ksu_trace.h>
+#endif
+
/**
* generic_fillattr - Fill in the basic attributes from the inode struct
* @mnt_userns: user namespace of the mount the inode was found from
@@ -408,6 +412,10 @@ SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename,
struct kstat stat;
int error;
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_stat_hook(&dfd, &filename, &flag);
+#endif
+
error = vfs_fstatat(dfd, filename, &stat, flag);
if (error)
return error;
@@ -559,6 +567,10 @@ SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,
struct kstat stat;
int error;
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_stat_hook(&dfd, &filename, &flag); /* 32-bit su support */
+#endif
+
error = vfs_fstatat(dfd, filename, &stat, flag);
if (error)
return error;
```
### input 钩子 (`input.c` ,用于进入KSU系的内置安全模式)
需要修改 `drivers/input/input.c``input_event` 方法,而不是 `input_handle_event`
```patch
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -26,6 +26,10 @@
#include "input-compat.h"
#include "input-poller.h"
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+#include <../../drivers/kernelsu/ksu_trace.h>
+#endif
+
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("Input core");
MODULE_LICENSE("GPL");
@@ -451,6 +455,10 @@ void input_event(struct input_dev *dev,
{
unsigned long flags;
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_input_hook(&type, &code, &value);
+#endif
+
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);
```
### devpts 钩子 (`pty.c`)
需要修改 `drivers/tty/pty.c``pts_unix98_lookup` 方法
```patch
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -31,6 +31,10 @@
#include <linux/compat.h>
#include "tty.h"
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+#include <../../drivers/kernelsu/ksu_trace.h>
+#endif
+
#undef TTY_DEBUG_HANGUP
#ifdef TTY_DEBUG_HANGUP
# define tty_debug_hangup(tty, f, args...) tty_debug(tty, f, ##args)
@@ -707,6 +711,10 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
{
struct tty_struct *tty;
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
+ trace_ksu_trace_devpts_hook((struct inode *)file->f_path.dentry->d_inode);
+#endif
+
mutex_lock(&devpts_mutex);
tty = devpts_get_priv(file->f_path.dentry);
mutex_unlock(&devpts_mutex);
```

View File

@@ -1,6 +1,6 @@
kernelsu-objs := ksu.o
kernelsu-objs += allowlist.o
kernelsu-objs += dynamic_sign.o
kernelsu-objs += dynamic_manager.o
kernelsu-objs += apk_sign.o
kernelsu-objs += sucompat.o
kernelsu-objs += throne_tracker.o
@@ -9,7 +9,7 @@ kernelsu-objs += ksud.o
kernelsu-objs += embed_ksud.o
kernelsu-objs += kernel_compat.o
ifeq ($(strip $(CONFIG_KSU_TRACEPOINT_HOOK)),y)
ifeq ($(CONFIG_KSU_TRACEPOINT_HOOK), y)
kernelsu-objs += ksu_trace.o
endif
@@ -28,7 +28,7 @@ obj-$(CONFIG_KPM) += kpm/
REPO_OWNER := SukiSU-Ultra
REPO_NAME := SukiSU-Ultra
REPO_BRANCH := main
KSU_VERSION_API := 3.1.8
KSU_VERSION_API := 3.1.9
GIT_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin git
CURL_BIN := /usr/bin/env PATH="$$PATH":/usr/bin:/usr/local/bin curl

View File

@@ -15,7 +15,7 @@
#endif
#include "apk_sign.h"
#include "dynamic_sign.h"
#include "dynamic_manager.h"
#include "klog.h" // IWYU pragma: keep
#include "kernel_compat.h"
#include "manager_sign.h"
@@ -109,7 +109,7 @@ static bool check_block(struct file *fp, u32 *size4, loff_t *pos, u32 *offset, i
if (i == 1) { // Dynamic Sign indexing
unsigned int size;
const char *hash;
if (ksu_get_dynamic_sign_config(&size, &hash)) {
if (ksu_get_dynamic_manager_config(&size, &hash)) {
sign_key.size = size;
sign_key.sha256 = hash;
}
@@ -219,8 +219,8 @@ static __always_inline bool check_v2_signature(char *path, bool check_multi_mana
return false;
}
// If you want to check for multi-manager APK signing, but dynamic signing is not enabled, skip
if (check_multi_manager && !ksu_is_dynamic_sign_enabled()) {
// If you want to check for multi-manager APK signing, but dynamic managering is not enabled, skip
if (check_multi_manager && !ksu_is_dynamic_manager_enabled()) {
filp_close(fp, 0);
return 0;
}
@@ -328,7 +328,7 @@ clean:
if (check_multi_manager) {
// 0: ShirkNeko/SukiSU, 1: Dynamic Sign
if (matched_index == 0 || matched_index == 1) {
pr_info("Multi-manager APK detected (dynamic_sign enabled): signature_index=%d\n", matched_index);
pr_info("Multi-manager APK detected (dynamic_manager enabled): signature_index=%d\n", matched_index);
return true;
}
return false;
@@ -369,7 +369,7 @@ bool is_manager_apk(char *path)
return check_v2_signature(path, false, NULL);
}
bool ksu_is_multi_manager_apk(char *path, int *signature_index)
bool ksu_is_dynamic_manager_apk(char *path, int *signature_index)
{
return check_v2_signature(path, true, signature_index);
}

View File

@@ -45,7 +45,7 @@
#include "kernel_compat.h"
#include "kpm/kpm.h"
#include "dynamic_sign.h"
#include "dynamic_manager.h"
static bool ksu_module_mounted = false;
@@ -345,31 +345,31 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
return 0;
}
// Allow the root manager to configure dynamic signatures
if (arg2 == CMD_DYNAMIC_SIGN) {
// Allow the root manager to configure dynamic manageratures
if (arg2 == CMD_DYNAMIC_MANAGER) {
if (!from_root && !from_manager) {
return 0;
}
struct dynamic_sign_user_config config;
struct dynamic_manager_user_config config;
if (copy_from_user(&config, (void __user *)arg3, sizeof(config))) {
pr_err("copy dynamic sign config failed\n");
pr_err("copy dynamic manager config failed\n");
return 0;
}
int ret = ksu_handle_dynamic_sign(&config);
int ret = ksu_handle_dynamic_manager(&config);
if (ret == 0 && config.operation == DYNAMIC_SIGN_OP_GET) {
if (ret == 0 && config.operation == DYNAMIC_MANAGER_OP_GET) {
if (copy_to_user((void __user *)arg3, &config, sizeof(config))) {
pr_err("copy dynamic sign config back failed\n");
pr_err("copy dynamic manager config back failed\n");
return 0;
}
}
if (ret == 0) {
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("dynamic_sign: prctl reply error\n");
pr_err("dynamic_manager: prctl reply error\n");
}
}
return 0;
@@ -408,7 +408,7 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
pr_info("post-fs-data triggered\n");
on_post_fs_data();
// Initializing Dynamic Signatures
ksu_dynamic_sign_init();
ksu_dynamic_manager_init();
pr_info("Dynamic sign config loaded during post-fs-data\n");
}
break;

View File

@@ -15,7 +15,7 @@
#include <crypto/sha.h>
#endif
#include "dynamic_sign.h"
#include "dynamic_manager.h"
#include "klog.h" // IWYU pragma: keep
#include "kernel_compat.h"
#include "manager.h"
@@ -23,7 +23,7 @@
#define MAX_MANAGERS 2
// Dynamic sign configuration
static struct dynamic_sign_config dynamic_sign = {
static struct dynamic_manager_config dynamic_manager = {
.size = 0x300,
.hash = "0000000000000000000000000000000000000000000000000000000000000000",
.is_set = 0
@@ -32,21 +32,21 @@ static struct dynamic_sign_config dynamic_sign = {
// Multi-manager state
static struct manager_info active_managers[MAX_MANAGERS];
static DEFINE_SPINLOCK(managers_lock);
static DEFINE_SPINLOCK(dynamic_sign_lock);
static DEFINE_SPINLOCK(dynamic_manager_lock);
// Work queues for persistent storage
static struct work_struct ksu_save_dynamic_sign_work;
static struct work_struct ksu_load_dynamic_sign_work;
static struct work_struct ksu_clear_dynamic_sign_work;
static struct work_struct ksu_save_dynamic_manager_work;
static struct work_struct ksu_load_dynamic_manager_work;
static struct work_struct ksu_clear_dynamic_manager_work;
bool ksu_is_dynamic_sign_enabled(void)
bool ksu_is_dynamic_manager_enabled(void)
{
unsigned long flags;
bool enabled;
spin_lock_irqsave(&dynamic_sign_lock, flags);
enabled = dynamic_sign.is_set;
spin_unlock_irqrestore(&dynamic_sign_lock, flags);
spin_lock_irqsave(&dynamic_manager_lock, flags);
enabled = dynamic_manager.is_set;
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
return enabled;
}
@@ -56,7 +56,7 @@ void ksu_add_manager(uid_t uid, int signature_index)
unsigned long flags;
int i;
if (!ksu_is_dynamic_sign_enabled()) {
if (!ksu_is_dynamic_manager_enabled()) {
pr_info("Dynamic sign not enabled, skipping multi-manager add\n");
return;
}
@@ -94,7 +94,7 @@ void ksu_remove_manager(uid_t uid)
unsigned long flags;
int i;
if (!ksu_is_dynamic_sign_enabled()) {
if (!ksu_is_dynamic_manager_enabled()) {
return;
}
@@ -117,7 +117,7 @@ bool ksu_is_any_manager(uid_t uid)
bool is_manager = false;
int i;
if (!ksu_is_dynamic_sign_enabled()) {
if (!ksu_is_dynamic_manager_enabled()) {
return false;
}
@@ -145,7 +145,7 @@ int ksu_get_manager_signature_index(uid_t uid)
return 1;
}
if (!ksu_is_dynamic_sign_enabled()) {
if (!ksu_is_dynamic_manager_enabled()) {
return -1;
}
@@ -197,7 +197,7 @@ int ksu_get_active_managers(struct manager_list_info *info)
}
// Add dynamic managers
if (ksu_is_dynamic_sign_enabled()) {
if (ksu_is_dynamic_manager_enabled()) {
spin_lock_irqsave(&managers_lock, flags);
for (i = 0; i < MAX_MANAGERS && count < 2; i++) {
@@ -215,42 +215,42 @@ int ksu_get_active_managers(struct manager_list_info *info)
return 0;
}
static void do_save_dynamic_sign(struct work_struct *work)
static void do_save_dynamic_manager(struct work_struct *work)
{
u32 magic = DYNAMIC_SIGN_FILE_MAGIC;
u32 version = DYNAMIC_SIGN_FILE_VERSION;
struct dynamic_sign_config config_to_save;
u32 magic = DYNAMIC_MANAGER_FILE_MAGIC;
u32 version = DYNAMIC_MANAGER_FILE_VERSION;
struct dynamic_manager_config config_to_save;
loff_t off = 0;
unsigned long flags;
struct file *fp;
spin_lock_irqsave(&dynamic_sign_lock, flags);
config_to_save = dynamic_sign;
spin_unlock_irqrestore(&dynamic_sign_lock, flags);
spin_lock_irqsave(&dynamic_manager_lock, flags);
config_to_save = dynamic_manager;
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
if (!config_to_save.is_set) {
pr_info("Dynamic sign config not set, skipping save\n");
return;
}
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_SIGN, O_WRONLY | O_CREAT | O_TRUNC, 0644);
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (IS_ERR(fp)) {
pr_err("save_dynamic_sign create file failed: %ld\n", PTR_ERR(fp));
pr_err("save_dynamic_manager create file failed: %ld\n", PTR_ERR(fp));
return;
}
if (ksu_kernel_write_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic)) {
pr_err("save_dynamic_sign write magic failed.\n");
pr_err("save_dynamic_manager write magic failed.\n");
goto exit;
}
if (ksu_kernel_write_compat(fp, &version, sizeof(version), &off) != sizeof(version)) {
pr_err("save_dynamic_sign write version failed.\n");
pr_err("save_dynamic_manager write version failed.\n");
goto exit;
}
if (ksu_kernel_write_compat(fp, &config_to_save, sizeof(config_to_save), &off) != sizeof(config_to_save)) {
pr_err("save_dynamic_sign write config failed.\n");
pr_err("save_dynamic_manager write config failed.\n");
goto exit;
}
@@ -260,48 +260,48 @@ exit:
filp_close(fp, 0);
}
static void do_load_dynamic_sign(struct work_struct *work)
static void do_load_dynamic_manager(struct work_struct *work)
{
loff_t off = 0;
ssize_t ret = 0;
struct file *fp = NULL;
u32 magic;
u32 version;
struct dynamic_sign_config loaded_config;
struct dynamic_manager_config loaded_config;
unsigned long flags;
int i;
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_SIGN, O_RDONLY, 0);
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_RDONLY, 0);
if (IS_ERR(fp)) {
if (PTR_ERR(fp) == -ENOENT) {
pr_info("No saved dynamic sign config found\n");
pr_info("No saved dynamic manager config found\n");
} else {
pr_err("load_dynamic_sign open file failed: %ld\n", PTR_ERR(fp));
pr_err("load_dynamic_manager open file failed: %ld\n", PTR_ERR(fp));
}
return;
}
if (ksu_kernel_read_compat(fp, &magic, sizeof(magic), &off) != sizeof(magic) ||
magic != DYNAMIC_SIGN_FILE_MAGIC) {
pr_err("dynamic sign file invalid magic: %x!\n", magic);
magic != DYNAMIC_MANAGER_FILE_MAGIC) {
pr_err("dynamic manager file invalid magic: %x!\n", magic);
goto exit;
}
if (ksu_kernel_read_compat(fp, &version, sizeof(version), &off) != sizeof(version)) {
pr_err("dynamic sign read version failed\n");
pr_err("dynamic manager read version failed\n");
goto exit;
}
pr_info("dynamic sign file version: %d\n", version);
pr_info("dynamic manager file version: %d\n", version);
ret = ksu_kernel_read_compat(fp, &loaded_config, sizeof(loaded_config), &off);
if (ret <= 0) {
pr_info("load_dynamic_sign read err: %zd\n", ret);
pr_info("load_dynamic_manager read err: %zd\n", ret);
goto exit;
}
if (ret != sizeof(loaded_config)) {
pr_err("load_dynamic_sign read incomplete config: %zd/%zu\n", ret, sizeof(loaded_config));
pr_err("load_dynamic_manager read incomplete config: %zd/%zu\n", ret, sizeof(loaded_config));
goto exit;
}
@@ -324,9 +324,9 @@ static void do_load_dynamic_sign(struct work_struct *work)
}
}
spin_lock_irqsave(&dynamic_sign_lock, flags);
dynamic_sign = loaded_config;
spin_unlock_irqrestore(&dynamic_sign_lock, flags);
spin_lock_irqsave(&dynamic_manager_lock, flags);
dynamic_manager = loaded_config;
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
pr_info("Dynamic sign config loaded: size=0x%x, hash=%.16s...\n",
loaded_config.size, loaded_config.hash);
@@ -335,12 +335,12 @@ exit:
filp_close(fp, 0);
}
static bool persistent_dynamic_sign(void)
static bool persistent_dynamic_manager(void)
{
return ksu_queue_work(&ksu_save_dynamic_sign_work);
return ksu_queue_work(&ksu_save_dynamic_manager_work);
}
static void do_clear_dynamic_sign(struct work_struct *work)
static void do_clear_dynamic_manager(struct work_struct *work)
{
loff_t off = 0;
struct file *fp;
@@ -348,15 +348,15 @@ static void do_clear_dynamic_sign(struct work_struct *work)
memset(zero_buffer, 0, sizeof(zero_buffer));
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_SIGN, O_WRONLY | O_CREAT | O_TRUNC, 0644);
fp = ksu_filp_open_compat(KERNEL_SU_DYNAMIC_MANAGER, O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (IS_ERR(fp)) {
pr_err("clear_dynamic_sign create file failed: %ld\n", PTR_ERR(fp));
pr_err("clear_dynamic_manager create file failed: %ld\n", PTR_ERR(fp));
return;
}
// Write null bytes to overwrite the file content
if (ksu_kernel_write_compat(fp, zero_buffer, sizeof(zero_buffer), &off) != sizeof(zero_buffer)) {
pr_err("clear_dynamic_sign write null bytes failed.\n");
pr_err("clear_dynamic_manager write null bytes failed.\n");
} else {
pr_info("Dynamic sign config file cleared successfully\n");
}
@@ -364,12 +364,12 @@ static void do_clear_dynamic_sign(struct work_struct *work)
filp_close(fp, 0);
}
static bool clear_dynamic_sign_file(void)
static bool clear_dynamic_manager_file(void)
{
return ksu_queue_work(&ksu_clear_dynamic_sign_work);
return ksu_queue_work(&ksu_clear_dynamic_manager_work);
}
int ksu_handle_dynamic_sign(struct dynamic_sign_user_config *config)
int ksu_handle_dynamic_manager(struct dynamic_manager_user_config *config)
{
unsigned long flags;
int ret = 0;
@@ -380,7 +380,7 @@ int ksu_handle_dynamic_sign(struct dynamic_sign_user_config *config)
}
switch (config->operation) {
case DYNAMIC_SIGN_OP_SET:
case DYNAMIC_MANAGER_OP_SET:
if (config->size < 0x100 || config->size > 0x1000) {
pr_err("invalid size: 0x%x\n", config->size);
return -EINVAL;
@@ -400,106 +400,106 @@ int ksu_handle_dynamic_sign(struct dynamic_sign_user_config *config)
}
}
spin_lock_irqsave(&dynamic_sign_lock, flags);
dynamic_sign.size = config->size;
spin_lock_irqsave(&dynamic_manager_lock, flags);
dynamic_manager.size = config->size;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
strscpy(dynamic_sign.hash, config->hash, sizeof(dynamic_sign.hash));
strscpy(dynamic_manager.hash, config->hash, sizeof(dynamic_manager.hash));
#else
strlcpy(dynamic_sign.hash, config->hash, sizeof(dynamic_sign.hash));
strlcpy(dynamic_manager.hash, config->hash, sizeof(dynamic_manager.hash));
#endif
dynamic_sign.is_set = 1;
spin_unlock_irqrestore(&dynamic_sign_lock, flags);
dynamic_manager.is_set = 1;
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
persistent_dynamic_sign();
pr_info("dynamic sign updated: size=0x%x, hash=%.16s... (multi-manager enabled)\n",
persistent_dynamic_manager();
pr_info("dynamic manager updated: size=0x%x, hash=%.16s... (multi-manager enabled)\n",
config->size, config->hash);
break;
case DYNAMIC_SIGN_OP_GET:
spin_lock_irqsave(&dynamic_sign_lock, flags);
if (dynamic_sign.is_set) {
config->size = dynamic_sign.size;
case DYNAMIC_MANAGER_OP_GET:
spin_lock_irqsave(&dynamic_manager_lock, flags);
if (dynamic_manager.is_set) {
config->size = dynamic_manager.size;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 13, 0)
strscpy(config->hash, dynamic_sign.hash, sizeof(config->hash));
strscpy(config->hash, dynamic_manager.hash, sizeof(config->hash));
#else
strlcpy(config->hash, dynamic_sign.hash, sizeof(config->hash));
strlcpy(config->hash, dynamic_manager.hash, sizeof(config->hash));
#endif
ret = 0;
} else {
ret = -ENODATA;
}
spin_unlock_irqrestore(&dynamic_sign_lock, flags);
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
break;
case DYNAMIC_SIGN_OP_CLEAR:
spin_lock_irqsave(&dynamic_sign_lock, flags);
dynamic_sign.size = 0x300;
strcpy(dynamic_sign.hash, "0000000000000000000000000000000000000000000000000000000000000000");
dynamic_sign.is_set = 0;
spin_unlock_irqrestore(&dynamic_sign_lock, flags);
case DYNAMIC_MANAGER_OP_CLEAR:
spin_lock_irqsave(&dynamic_manager_lock, flags);
dynamic_manager.size = 0x300;
strcpy(dynamic_manager.hash, "0000000000000000000000000000000000000000000000000000000000000000");
dynamic_manager.is_set = 0;
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
// Clear only dynamic managers, preserve default manager
clear_dynamic_manager();
// Clear file using the same method as save
clear_dynamic_sign_file();
clear_dynamic_manager_file();
pr_info("Dynamic sign config cleared (multi-manager disabled)\n");
break;
default:
pr_err("Invalid dynamic sign operation: %d\n", config->operation);
pr_err("Invalid dynamic manager operation: %d\n", config->operation);
return -EINVAL;
}
return ret;
}
bool ksu_load_dynamic_sign(void)
bool ksu_load_dynamic_manager(void)
{
return ksu_queue_work(&ksu_load_dynamic_sign_work);
return ksu_queue_work(&ksu_load_dynamic_manager_work);
}
void ksu_dynamic_sign_init(void)
void ksu_dynamic_manager_init(void)
{
int i;
INIT_WORK(&ksu_save_dynamic_sign_work, do_save_dynamic_sign);
INIT_WORK(&ksu_load_dynamic_sign_work, do_load_dynamic_sign);
INIT_WORK(&ksu_clear_dynamic_sign_work, do_clear_dynamic_sign);
INIT_WORK(&ksu_save_dynamic_manager_work, do_save_dynamic_manager);
INIT_WORK(&ksu_load_dynamic_manager_work, do_load_dynamic_manager);
INIT_WORK(&ksu_clear_dynamic_manager_work, do_clear_dynamic_manager);
// Initialize manager slots
for (i = 0; i < MAX_MANAGERS; i++) {
active_managers[i].is_active = false;
}
ksu_load_dynamic_sign();
ksu_load_dynamic_manager();
pr_info("Dynamic sign initialized with conditional multi-manager support\n");
}
void ksu_dynamic_sign_exit(void)
void ksu_dynamic_manager_exit(void)
{
clear_dynamic_manager();
// Save current config before exit
do_save_dynamic_sign(NULL);
do_save_dynamic_manager(NULL);
pr_info("Dynamic sign exited with persistent storage\n");
}
// Get dynamic sign configuration for signature verification
bool ksu_get_dynamic_sign_config(unsigned int *size, const char **hash)
// Get dynamic manager configuration for signature verification
bool ksu_get_dynamic_manager_config(unsigned int *size, const char **hash)
{
unsigned long flags;
bool valid = false;
spin_lock_irqsave(&dynamic_sign_lock, flags);
if (dynamic_sign.is_set) {
if (size) *size = dynamic_sign.size;
if (hash) *hash = dynamic_sign.hash;
spin_lock_irqsave(&dynamic_manager_lock, flags);
if (dynamic_manager.is_set) {
if (size) *size = dynamic_manager.size;
if (hash) *hash = dynamic_manager.hash;
valid = true;
}
spin_unlock_irqrestore(&dynamic_sign_lock, flags);
spin_unlock_irqrestore(&dynamic_manager_lock, flags);
return valid;
}

43
kernel/dynamic_manager.h Normal file
View File

@@ -0,0 +1,43 @@
#ifndef __KSU_H_DYNAMIC_MANAGER
#define __KSU_H_DYNAMIC_MANAGER
#include <linux/types.h>
#include "ksu.h"
#define DYNAMIC_MANAGER_FILE_MAGIC 0x7f445347 // 'DSG', u32
#define DYNAMIC_MANAGER_FILE_VERSION 1 // u32
#define KERNEL_SU_DYNAMIC_MANAGER "/data/adb/ksu/.dynamic_manager"
struct dynamic_manager_config {
unsigned int size;
char hash[65];
int is_set;
};
struct manager_info {
uid_t uid;
int signature_index;
bool is_active;
};
// Dynamic sign operations
void ksu_dynamic_manager_init(void);
void ksu_dynamic_manager_exit(void);
int ksu_handle_dynamic_manager(struct dynamic_manager_user_config *config);
bool ksu_load_dynamic_manager(void);
bool ksu_is_dynamic_manager_enabled(void);
// Multi-manager operations
void ksu_add_manager(uid_t uid, int signature_index);
void ksu_remove_manager(uid_t uid);
bool ksu_is_any_manager(uid_t uid);
int ksu_get_manager_signature_index(uid_t uid);
int ksu_get_active_managers(struct manager_list_info *info);
// Multi-manager APK verification
bool ksu_is_dynamic_manager_apk(char *path, int *signature_index);
// Configuration access for signature verification
bool ksu_get_dynamic_manager_config(unsigned int *size, const char **hash);
#endif

View File

@@ -1,43 +0,0 @@
#ifndef __KSU_H_DYNAMIC_SIGN
#define __KSU_H_DYNAMIC_SIGN
#include <linux/types.h>
#include "ksu.h"
#define DYNAMIC_SIGN_FILE_MAGIC 0x7f445347 // 'DSG', u32
#define DYNAMIC_SIGN_FILE_VERSION 1 // u32
#define KERNEL_SU_DYNAMIC_SIGN "/data/adb/ksu/.dynamic_sign"
struct dynamic_sign_config {
unsigned int size;
char hash[65];
int is_set;
};
struct manager_info {
uid_t uid;
int signature_index;
bool is_active;
};
// Dynamic sign operations
int ksu_handle_dynamic_sign(struct dynamic_sign_user_config *config);
void ksu_dynamic_sign_init(void);
void ksu_dynamic_sign_exit(void);
bool ksu_load_dynamic_sign(void);
bool ksu_is_dynamic_sign_enabled(void);
// Multi-manager operations
void ksu_add_manager(uid_t uid, int signature_index);
void ksu_remove_manager(uid_t uid);
bool ksu_is_any_manager(uid_t uid);
int ksu_get_manager_signature_index(uid_t uid);
int ksu_get_active_managers(struct manager_list_info *info);
// Multi-manager APK verification
bool ksu_is_multi_manager_apk(char *path, int *signature_index);
// Configuration access for signature verification
bool ksu_get_dynamic_sign_config(unsigned int *size, const char **hash);
#endif

View File

@@ -28,7 +28,7 @@
#define CMD_ENABLE_KPM 100
#define CMD_HOOK_TYPE 101
#define CMD_DYNAMIC_SIGN 103
#define CMD_DYNAMIC_MANAGER 103
#define CMD_GET_MANAGERS 104
#define EVENT_POST_FS_DATA 1
@@ -47,11 +47,11 @@
#endif
#define KSU_FULL_VERSION_STRING 255
#define DYNAMIC_SIGN_OP_SET 0
#define DYNAMIC_SIGN_OP_GET 1
#define DYNAMIC_SIGN_OP_CLEAR 2
#define DYNAMIC_MANAGER_OP_SET 0
#define DYNAMIC_MANAGER_OP_GET 1
#define DYNAMIC_MANAGER_OP_CLEAR 2
struct dynamic_sign_user_config {
struct dynamic_manager_user_config {
unsigned int operation;
unsigned int size;
char hash[65];

View File

@@ -12,7 +12,7 @@
#include "manager.h"
#include "throne_tracker.h"
#include "kernel_compat.h"
#include "dynamic_sign.h"
#include "dynamic_manager.h"
uid_t ksu_manager_uid = KSU_INVALID_UID;
@@ -196,7 +196,7 @@ FILLDIR_RETURN_TYPE my_actor(struct dir_context *ctx, const char *name,
}
int signature_index = -1;
bool is_multi_manager = ksu_is_multi_manager_apk(dirpath, &signature_index);
bool is_multi_manager = ksu_is_dynamic_manager_apk(dirpath, &signature_index);
pr_info("Found new base.apk at path: %s, is_multi_manager: %d, signature_index: %d\n",
dirpath, is_multi_manager, signature_index);
@@ -401,7 +401,7 @@ void track_throne()
}
// Check for dynamic managers
if (!dynamic_manager_exist && ksu_is_dynamic_sign_enabled()) {
if (!dynamic_manager_exist && ksu_is_dynamic_manager_enabled()) {
list_for_each_entry (np, &uid_list, list) {
if (ksu_is_any_manager(np->uid)) {
dynamic_manager_exist = true;
@@ -419,8 +419,8 @@ void track_throne()
pr_info("Searching manager...\n");
search_manager("/data/app", 2, &uid_list);
pr_info("Search manager finished\n");
} else if (!dynamic_manager_exist && ksu_is_dynamic_sign_enabled()) {
// Always perform search when called from dynamic sign rescan
} else if (!dynamic_manager_exist && ksu_is_dynamic_manager_enabled()) {
// Always perform search when called from dynamic manager rescan
pr_info("Dynamic sign enabled, Searching manager...\n");
search_manager("/data/app", 2, &uid_list);
pr_info("Search Dynamic sign manager finished\n");

View File

@@ -28,12 +28,12 @@ apksign {
android {
/**signingConfigs {
create("Debug") {
storeFile = file("D:\\other\\AndroidTool\\android_key\\keystore\\release-key.keystore")
storePassword = ""
keyAlias = ""
keyPassword = ""
}
create("Debug") {
storeFile = file("D:\\other\\AndroidTool\\android_key\\keystore\\release-key.keystore")
storePassword = ""
keyAlias = ""
keyPassword = ""
}
}**/
namespace = "com.sukisu.ultra"
@@ -41,13 +41,10 @@ android {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
}
/**debug {
signingConfig = signingConfigs.named("Debug").get() as ApkSigningConfig
signingConfig = signingConfigs.named("Debug").get() as ApkSigningConfig
}**/
}
@@ -134,8 +131,6 @@ dependencies {
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.swiperefreshlayout)
implementation(libs.compose.destinations.core)
ksp(libs.compose.destinations.ksp)
@@ -164,16 +159,7 @@ dependencies {
implementation(libs.mmrl.platform)
compileOnly(libs.mmrl.hidden.api)
/**
* Compile only `Java-Native-Access` since plugins are disabled in both WebUI X and KSU WebUI
* Avoid using:
* - fun WXInterface.registerLibrary(clazz: Class<*>, name: String): Unit
* - fun WXInterface.unregisterLibrary(clazz: Class<*>): Unit
* - fun WXInterface.isLibraryRegistered(libName: String): Boolean
*/
compileOnly(libs.mmrl.webui.jna)
implementation(libs.mmrl.webui.portable)
implementation(libs.mmrl.webui)
implementation(libs.mmrl.ui)
implementation(libs.accompanist.drawablepainter)

View File

@@ -4,14 +4,43 @@
-dontwarn org.conscrypt.**
-dontwarn kotlinx.serialization.**
# Please add these rules to your existing keep rules in order to suppress warnings.
# This is generated automatically by the Android Gradle plugin.
-dontwarn com.google.auto.service.AutoService
-dontwarn com.google.j2objc.annotations.RetainedWith
-dontwarn javax.lang.model.SourceVersion
-dontwarn javax.lang.model.element.AnnotationMirror
-dontwarn javax.lang.model.element.AnnotationValue
-dontwarn javax.lang.model.element.Element
-dontwarn javax.lang.model.element.ElementKind
-dontwarn javax.lang.model.element.ElementVisitor
-dontwarn javax.lang.model.element.ExecutableElement
-dontwarn javax.lang.model.element.Modifier
-dontwarn javax.lang.model.element.Name
-dontwarn javax.lang.model.element.PackageElement
-dontwarn javax.lang.model.element.TypeElement
-dontwarn javax.lang.model.element.TypeParameterElement
-dontwarn javax.lang.model.element.VariableElement
-dontwarn javax.lang.model.type.ArrayType
-dontwarn javax.lang.model.type.DeclaredType
-dontwarn javax.lang.model.type.ExecutableType
-dontwarn javax.lang.model.type.TypeKind
-dontwarn javax.lang.model.type.TypeMirror
-dontwarn javax.lang.model.type.TypeVariable
-dontwarn javax.lang.model.type.TypeVisitor
-dontwarn javax.lang.model.util.AbstractAnnotationValueVisitor8
-dontwarn javax.lang.model.util.AbstractTypeVisitor8
-dontwarn javax.lang.model.util.ElementFilter
-dontwarn javax.lang.model.util.Elements
-dontwarn javax.lang.model.util.SimpleElementVisitor8
-dontwarn javax.lang.model.util.SimpleTypeVisitor7
-dontwarn javax.lang.model.util.SimpleTypeVisitor8
-dontwarn javax.lang.model.util.Types
-dontwarn javax.tools.Diagnostic$Kind
# MMRL:webui reflection
-keep class androidx.compose.ui.graphics.Color { *; }
-keep class androidx.compose.material3.ButtonColors { *; }
-keep class androidx.compose.material3.CardColors { *; }
-keep class androidx.compose.material3.ColorScheme { *; }
-keep class com.dergoogler.mmrl.platform.model.ModId { *; }
-keep class com.dergoogler.mmrl.webui.interfaces.WXOptions { *; }
-keep class com.dergoogler.mmrl.webui.interfaces.WXInterface { *; }
-keep class com.dergoogler.mmrl.webui.model.ModId { *; }
-keep class com.dergoogler.mmrl.webui.interfaces.** { *; }
-keep class com.sukisu.ultra.ui.webui.WebViewInterface { *; }

View File

@@ -341,43 +341,43 @@ NativeBridgeNP(getSusfsFeatureStatus, jobject) {
return obj;
}
// dynamic sign
NativeBridge(setDynamicSign, jboolean, jint size, jstring hash) {
// dynamic manager
NativeBridge(setDynamicManager, jboolean, jint size, jstring hash) {
if (!hash) {
LogDebug("setDynamicSign: hash is null");
LogDebug("setDynamicManager: hash is null");
return false;
}
const char* chash = GetEnvironment()->GetStringUTFChars(env, hash, nullptr);
bool result = set_dynamic_sign((unsigned int)size, chash);
bool result = set_dynamic_manager((unsigned int)size, chash);
GetEnvironment()->ReleaseStringUTFChars(env, hash, chash);
LogDebug("setDynamicSign: size=0x%x, result=%d", size, result);
LogDebug("setDynamicManager: size=0x%x, result=%d", size, result);
return result;
}
NativeBridgeNP(getDynamicSign, jobject) {
struct dynamic_sign_user_config config;
bool result = get_dynamic_sign(&config);
NativeBridgeNP(getDynamicManager, jobject) {
struct dynamic_manager_user_config config;
bool result = get_dynamic_manager(&config);
if (!result) {
LogDebug("getDynamicSign: failed to get dynamic sign config");
LogDebug("getDynamicManager: failed to get dynamic manager config");
return NULL;
}
jobject obj = CREATE_JAVA_OBJECT("com/sukisu/ultra/Natives$DynamicSignConfig");
jclass cls = GetEnvironment()->FindClass(env, "com/sukisu/ultra/Natives$DynamicSignConfig");
jobject obj = CREATE_JAVA_OBJECT("com/sukisu/ultra/Natives$DynamicManagerConfig");
jclass cls = GetEnvironment()->FindClass(env, "com/sukisu/ultra/Natives$DynamicManagerConfig");
SET_INT_FIELD(obj, cls, size, (jint)config.size);
SET_STRING_FIELD(obj, cls, hash, config.hash);
LogDebug("getDynamicSign: size=0x%x, hash=%.16s...", config.size, config.hash);
LogDebug("getDynamicManager: size=0x%x, hash=%.16s...", config.size, config.hash);
return obj;
}
NativeBridgeNP(clearDynamicSign, jboolean) {
bool result = clear_dynamic_sign();
LogDebug("clearDynamicSign: result=%d", result);
NativeBridgeNP(clearDynamicManager, jboolean) {
bool result = clear_dynamic_manager();
LogDebug("clearDynamicManager: result=%d", result);
return result;
}

View File

@@ -46,12 +46,12 @@ extern const char* zako_file_verrcidx2str(uint8_t index);
#define CMD_ENABLE_KPM 100
#define CMD_HOOK_TYPE 101
#define CMD_GET_SUSFS_FEATURE_STATUS 102
#define CMD_DYNAMIC_SIGN 103
#define CMD_DYNAMIC_MANAGER 103
#define CMD_GET_MANAGERS 104
#define DYNAMIC_SIGN_OP_SET 0
#define DYNAMIC_SIGN_OP_GET 1
#define DYNAMIC_SIGN_OP_CLEAR 2
#define DYNAMIC_MANAGER_OP_SET 0
#define DYNAMIC_MANAGER_OP_GET 1
#define DYNAMIC_MANAGER_OP_CLEAR 2
static bool ksuctl(int cmd, void* arg1, void* arg2) {
int32_t result = 0;
@@ -157,33 +157,33 @@ bool get_susfs_feature_status(struct susfs_feature_status* status) {
return ksuctl(CMD_GET_SUSFS_FEATURE_STATUS, status, NULL);
}
bool set_dynamic_sign(unsigned int size, const char* hash) {
bool set_dynamic_manager(unsigned int size, const char* hash) {
if (hash == NULL) {
return false;
}
struct dynamic_sign_user_config config;
config.operation = DYNAMIC_SIGN_OP_SET;
struct dynamic_manager_user_config config;
config.operation = DYNAMIC_MANAGER_OP_SET;
config.size = size;
strncpy(config.hash, hash, sizeof(config.hash) - 1);
config.hash[sizeof(config.hash) - 1] = '\0';
return ksuctl(CMD_DYNAMIC_SIGN, &config, NULL);
return ksuctl(CMD_DYNAMIC_MANAGER, &config, NULL);
}
bool get_dynamic_sign(struct dynamic_sign_user_config* config) {
bool get_dynamic_manager(struct dynamic_manager_user_config* config) {
if (config == NULL) {
return false;
}
config->operation = DYNAMIC_SIGN_OP_GET;
return ksuctl(CMD_DYNAMIC_SIGN, config, NULL);
config->operation = DYNAMIC_MANAGER_OP_GET;
return ksuctl(CMD_DYNAMIC_MANAGER, config, NULL);
}
bool clear_dynamic_sign() {
struct dynamic_sign_user_config config;
config.operation = DYNAMIC_SIGN_OP_CLEAR;
return ksuctl(CMD_DYNAMIC_SIGN, &config, NULL);
bool clear_dynamic_manager() {
struct dynamic_manager_user_config config;
config.operation = DYNAMIC_MANAGER_OP_CLEAR;
return ksuctl(CMD_DYNAMIC_MANAGER, &config, NULL);
}
bool get_managers_list(struct manager_list_info* info) {

View File

@@ -29,11 +29,11 @@ bool is_lkm_mode();
#define KSU_MAX_GROUPS 32
#define KSU_SELINUX_DOMAIN 64
#define DYNAMIC_SIGN_OP_SET 0
#define DYNAMIC_SIGN_OP_GET 1
#define DYNAMIC_SIGN_OP_CLEAR 2
#define DYNAMIC_MANAGER_OP_SET 0
#define DYNAMIC_MANAGER_OP_GET 1
#define DYNAMIC_MANAGER_OP_CLEAR 2
struct dynamic_sign_user_config {
struct dynamic_manager_user_config {
unsigned int operation;
unsigned int size;
char hash[65];
@@ -128,11 +128,11 @@ bool get_hook_type(char* hook_type, size_t size);
bool get_susfs_feature_status(struct susfs_feature_status* status);
bool set_dynamic_sign(unsigned int size, const char* hash);
bool set_dynamic_manager(unsigned int size, const char* hash);
bool get_dynamic_sign(struct dynamic_sign_user_config* config);
bool get_dynamic_manager(struct dynamic_manager_user_config* config);
bool clear_dynamic_sign();
bool clear_dynamic_manager();
bool get_managers_list(struct manager_list_info* info);

View File

@@ -11,7 +11,7 @@ import android.os.Build
import android.os.Bundle
import coil.Coil
import coil.ImageLoader
import com.dergoogler.mmrl.platform.PlatformManager
import com.dergoogler.mmrl.platform.Platform
import me.zhanghai.android.appiconloader.coil.AppIconFetcher
import me.zhanghai.android.appiconloader.coil.AppIconKeyer
import java.io.File
@@ -90,7 +90,7 @@ class KernelSUApplication : Application() {
// 注册Activity生命周期回调
registerActivityLifecycleCallbacks(activityLifecycleCallbacks)
PlatformManager.setHiddenApiExemptions()
Platform.setHiddenApiExemptions()
val context = this
val iconSize = resources.getDimensionPixelSize(android.R.dimen.app_icon_size)

View File

@@ -29,7 +29,7 @@ object Natives {
const val MINIMAL_SUPPORTED_KPM = 12800
const val MINIMAL_SUPPORTED_DYNAMIC_SIGN = 13215
const val MINIMAL_SUPPORTED_DYNAMIC_MANAGER = 13215
const val ROOT_UID = 0
const val ROOT_GID = 0
@@ -107,28 +107,28 @@ object Natives {
external fun getSusfsFeatureStatus(): SusfsFeatureStatus?
/**
* Set dynamic signature configuration
* Set dynamic managerature configuration
* @param size APK signature size
* @param hash APK signature hash (64 character hex string)
* @return true if successful, false otherwise
*/
external fun setDynamicSign(size: Int, hash: String): Boolean
external fun setDynamicManager(size: Int, hash: String): Boolean
/**
* Get current dynamic signature configuration
* @return DynamicSignConfig object containing current configuration, or null if not set
* Get current dynamic managerature configuration
* @return DynamicManagerConfig object containing current configuration, or null if not set
*/
external fun getDynamicSign(): DynamicSignConfig?
external fun getDynamicManager(): DynamicManagerConfig?
/**
* Clear dynamic signature configuration
* Clear dynamic managerature configuration
* @return true if successful, false otherwise
*/
external fun clearDynamicSign(): Boolean
external fun clearDynamicManager(): Boolean
/**
* Get active managers list when dynamic sign is enabled
* Get active managers list when dynamic manager is enabled
* @return ManagersList object containing active managers, or null if failed or not enabled
*/
external fun getManagersList(): ManagersList?
@@ -185,7 +185,7 @@ object Natives {
@Immutable
@Parcelize
@Keep
data class DynamicSignConfig(
data class DynamicManagerConfig(
val size: Int = 0,
val hash: String = ""
) : Parcelable {

View File

@@ -747,7 +747,7 @@ private fun InfoCard(
append(
when (signatureIndex) {
0 -> "(${stringResource(R.string.default_signature)})"
1 -> "(${stringResource(R.string.dynamic_signature)})"
1 -> "(${stringResource(R.string.dynamic_managerature)})"
else -> if (signatureIndex >= 2) "(${
stringResource(
R.string.signature_index,

View File

@@ -84,8 +84,8 @@ import java.util.concurrent.TimeUnit
import androidx.core.content.edit
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.webui.WebUIXActivity
import com.dergoogler.mmrl.platform.Platform
import androidx.core.net.toUri
import com.dergoogler.mmrl.platform.PlatformManager
import com.dergoogler.mmrl.platform.model.ModuleConfig
import com.dergoogler.mmrl.platform.model.ModuleConfig.Companion.asModuleConfig
import com.sukisu.ultra.ui.component.AnimatedFab
@@ -460,7 +460,7 @@ fun ModuleScreen(navigator: DestinationsNavigator) {
"wx" -> wxEngine
"ksu" -> ksuEngine
else -> {
if (PlatformManager.isAlive) {
if (Platform.isAlive) {
wxEngine
} else {
ksuEngine

View File

@@ -2,7 +2,6 @@ package com.sukisu.ultra.ui.theme
import android.content.ContentResolver
import android.content.Context
import android.content.res.Configuration
import android.net.Uri
import android.os.Build
import android.util.Log
@@ -49,10 +48,8 @@ import androidx.activity.SystemBarStyle
import androidx.activity.ComponentActivity
import androidx.activity.enableEdgeToEdge
import androidx.compose.material3.ColorScheme
import androidx.compose.runtime.ReadOnlyComposable
import androidx.compose.runtime.SideEffect
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalConfiguration
/**
* 主题配置对象,管理应用的主题相关状态
@@ -87,13 +84,13 @@ object ThemeConfig {
*/
@Composable
fun KernelSUTheme(
darkTheme: Boolean = when (ThemeConfig.forceDarkMode) {
darkTheme: Boolean = when(ThemeConfig.forceDarkMode) {
true -> true
false -> false
null -> isSystemInDarkTheme()
},
dynamicColor: Boolean = ThemeConfig.useDynamicColor,
content: @Composable () -> Unit,
content: @Composable () -> Unit
) {
val context = LocalContext.current
val systemIsDark = isSystemInDarkTheme()
@@ -138,17 +135,18 @@ fun KernelSUTheme(
ThemeConfig.backgroundImageLoaded = false
}
ThemeConfig.preventBackgroundRefresh =
context.getSharedPreferences("theme_prefs", Context.MODE_PRIVATE)
.getBoolean("prevent_background_refresh", true)
ThemeConfig.preventBackgroundRefresh = context.getSharedPreferences("theme_prefs", Context.MODE_PRIVATE)
.getBoolean("prevent_background_refresh", true)
}
// 创建颜色方案
val colorScheme = createColorScheme(
context = context,
darkTheme = darkTheme,
dynamicColor = dynamicColor
)
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
if (darkTheme) createDynamicDarkColorScheme(context) else createDynamicLightColorScheme(context)
}
darkTheme -> createDarkColorScheme()
else -> createLightColorScheme()
}
// 根据暗色模式和自定义背景调整卡片配置
val isDarkModeWithCustomBackground = darkTheme && ThemeConfig.customBackgroundUri != null
@@ -219,18 +217,8 @@ fun KernelSUTheme(
modifier = Modifier
.fillMaxSize()
.zIndex(-2f)
.background(
if (darkTheme) if (CardConfig.isCustomBackgroundEnabled) {
colorScheme.surfaceContainerLow
} else {
colorScheme.background
}
else if (CardConfig.isCustomBackgroundEnabled) {
colorScheme.surfaceContainerLow
} else {
colorScheme.background
}
)
.background(if (darkTheme) if (CardConfig.isCustomBackgroundEnabled) { colorScheme.surfaceContainerLow } else { colorScheme.background }
else if (CardConfig.isCustomBackgroundEnabled) { colorScheme.surfaceContainerLow } else { colorScheme.background })
)
// 自定义背景层
@@ -251,9 +239,7 @@ fun KernelSUTheme(
contentScale = ContentScale.Crop
)
.graphicsLayer {
alpha =
(painter.state as? AsyncImagePainter.State.Success)?.let { 1f }
?: 0f
alpha = (painter.state as? AsyncImagePainter.State.Success)?.let { 1f } ?: 0f
}
)
}
@@ -302,6 +288,7 @@ fun KernelSUTheme(
* 创建动态深色颜色方案
*/
@RequiresApi(Build.VERSION_CODES.S)
@Composable
private fun createDynamicDarkColorScheme(context: Context): ColorScheme {
val scheme = dynamicDarkColorScheme(context)
return scheme.copy(
@@ -316,6 +303,7 @@ private fun createDynamicDarkColorScheme(context: Context): ColorScheme {
* 创建动态浅色颜色方案
*/
@RequiresApi(Build.VERSION_CODES.S)
@Composable
private fun createDynamicLightColorScheme(context: Context): ColorScheme {
val scheme = dynamicLightColorScheme(context)
return scheme.copy(
@@ -326,35 +314,12 @@ private fun createDynamicLightColorScheme(context: Context): ColorScheme {
)
}
internal fun createColorScheme(
context: Context,
darkTheme: Boolean = when (ThemeConfig.forceDarkMode) {
true -> true
false -> false
null -> _isSystemInDarkTheme(context)
},
dynamicColor: Boolean = ThemeConfig.useDynamicColor,
) = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
if (darkTheme) createDynamicDarkColorScheme(context) else createDynamicLightColorScheme(
context
)
}
darkTheme -> createDarkColorScheme()
else -> createLightColorScheme()
}
@Suppress("FunctionName")
internal fun _isSystemInDarkTheme(context: Context): Boolean {
val configuration = context.resources.configuration
val uiMode = configuration.uiMode
return (uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
}
/**
* 创建深色颜色方案
*/
@Composable
private fun createDarkColorScheme() = darkColorScheme(
primary = ThemeConfig.currentTheme.primaryDark,
onPrimary = ThemeConfig.currentTheme.onPrimaryDark,
@@ -396,6 +361,7 @@ private fun createDarkColorScheme() = darkColorScheme(
/**
* 创建浅色颜色方案
*/
@Composable
private fun createLightColorScheme() = lightColorScheme(
primary = ThemeConfig.currentTheme.primaryLight,
onPrimary = ThemeConfig.currentTheme.onPrimaryLight,
@@ -474,10 +440,7 @@ private fun Context.copyImageToInternalStorage(uri: Uri): Uri? {
/**
* 保存并应用自定义背景
*/
fun Context.saveAndApplyCustomBackground(
uri: Uri,
transformation: BackgroundTransformation? = null,
) {
fun Context.saveAndApplyCustomBackground(uri: Uri, transformation: BackgroundTransformation? = null) {
val finalUri = if (transformation != null) {
saveTransformedBackground(uri, transformation)
} else {
@@ -573,7 +536,7 @@ fun Context.loadThemeMode() {
val mode = getSharedPreferences("theme_prefs", Context.MODE_PRIVATE)
.getString("theme_mode", "system")
ThemeConfig.forceDarkMode = when (mode) {
ThemeConfig.forceDarkMode = when(mode) {
"dark" -> true
"light" -> false
else -> null

View File

@@ -7,7 +7,7 @@ import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.util.Log
import android.widget.Toast
import com.dergoogler.mmrl.platform.PlatformManager.context
import com.dergoogler.mmrl.platform.Platform.Companion.context
import com.sukisu.ultra.Natives
import com.sukisu.ultra.R
import com.topjohnwu.superuser.Shell

View File

@@ -10,7 +10,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.dergoogler.mmrl.platform.PlatformManager.context
import com.dergoogler.mmrl.platform.Platform.Companion.context
import com.google.gson.Gson
import com.google.gson.JsonSyntaxException
import com.sukisu.ultra.KernelVersion
@@ -431,18 +431,18 @@ class HomeViewModel : ViewModel() {
}
}
// 获取动态签名状态和管理器列表
// 获取动态管理器状态和管理器列表
val dynamicSignConfig = try {
Natives.getDynamicSign()
Natives.getDynamicManager()
} catch (e: Exception) {
Log.w(TAG, "Failed to get dynamic sign config", e)
Log.w(TAG, "Failed to get dynamic manager config", e)
null
}
val isDynamicSignEnabled = try {
dynamicSignConfig?.isValid() == true
} catch (e: Exception) {
Log.w(TAG, "Failed to check dynamic sign validity", e)
Log.w(TAG, "Failed to check dynamic manager validity", e)
false
}

View File

@@ -1,57 +1,60 @@
package com.sukisu.ultra.ui.webui
import android.content.Context
import android.content.ServiceConnection
import android.util.Log
import android.content.pm.PackageInfo
import com.dergoogler.mmrl.platform.Platform
import com.dergoogler.mmrl.platform.Platform.Companion.createPlatformIntent
import com.dergoogler.mmrl.platform.PlatformManager
import com.dergoogler.mmrl.platform.PlatformManager.packageManager
import com.dergoogler.mmrl.platform.PlatformManager.userManager
import com.dergoogler.mmrl.platform.model.IProvider
import com.dergoogler.mmrl.platform.model.PlatformIntent
import com.sukisu.ultra.ksuApp
import com.sukisu.ultra.Natives
import com.topjohnwu.superuser.ipc.RootService
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Deferred
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
class KsuLibSuProvider(
private val context: Context,
) : IProvider {
override val name = "SukiLibSu"
class KsuLibSuProvider : IProvider {
override val name = "KsuLibSu"
override fun isAvailable() = true
override suspend fun isAuthorized() = Natives.becomeManager(context.packageName)
override suspend fun isAuthorized() = Natives.becomeManager(ksuApp.packageName)
private val intent by lazy {
context.createPlatformIntent<SuService>(Platform.SukiSU)
}
private val serviceIntent
get() = PlatformIntent(
ksuApp,
Platform.KsuNext,
SuService::class.java
)
override fun bind(connection: ServiceConnection) {
RootService.bind(intent, connection)
RootService.bind(serviceIntent.intent, connection)
}
override fun unbind(connection: ServiceConnection) {
RootService.stop(intent)
RootService.stop(serviceIntent.intent)
}
}
// webui x
suspend fun CoroutineScope.initPlatform(context: Context = ksuApp): Deferred<Boolean> =
suspend fun initPlatform() = withContext(Dispatchers.IO) {
try {
val active = PlatformManager.init(this) {
from(KsuLibSuProvider(context))
val active = Platform.init {
this.context = ksuApp
this.platform = Platform.KsuNext
this.provider = from(KsuLibSuProvider())
}
active
while (!active) {
delay(1000)
}
return@withContext active
} catch (e: Exception) {
Log.e("KsuLibSu", "Failed to initialize platform", e)
CompletableDeferred(false)
return@withContext false
}
}
fun Platform.Companion.getInstalledPackagesAll(catch: (Exception) -> Unit = {}): List<PackageInfo> =
try {

View File

@@ -2,13 +2,13 @@ package com.sukisu.ultra.ui.webui
import android.content.Intent
import android.os.IBinder
import com.dergoogler.mmrl.platform.Platform.Companion.getPlatform
import com.dergoogler.mmrl.platform.model.PlatformIntent.Companion.getPlatform
import com.dergoogler.mmrl.platform.service.ServiceManager
import com.topjohnwu.superuser.ipc.RootService
class SuService : RootService() {
override fun onBind(intent: Intent): IBinder {
val mode = intent.getPlatform() ?: throw Exception("Platform not found")
val mode = intent.getPlatform()
return ServiceManager(mode)
}
}

View File

@@ -1,10 +1,10 @@
package com.sukisu.ultra.ui.webui
import android.content.Context
import android.content.SharedPreferences
import android.annotation.SuppressLint
import android.app.ActivityManager
import android.os.Build
import android.os.Bundle
import android.view.ViewGroup
import android.view.ViewGroup.MarginLayoutParams
import android.webkit.WebResourceRequest
import android.webkit.WebResourceResponse
import android.webkit.WebView
@@ -14,30 +14,21 @@ import androidx.activity.enableEdgeToEdge
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updateLayoutParams
import androidx.lifecycle.lifecycleScope
import androidx.webkit.WebViewAssetLoader
import com.dergoogler.mmrl.platform.model.ModId.Companion.getModId
import com.dergoogler.mmrl.platform.model.ModId.Companion.webrootDir
import com.dergoogler.mmrl.ui.component.dialog.ConfirmData
import com.dergoogler.mmrl.ui.component.dialog.confirm
import com.dergoogler.mmrl.webui.activity.WXActivity.Companion.createLoadingRenderer
import com.dergoogler.mmrl.platform.model.ModId
import com.topjohnwu.superuser.Shell
import com.sukisu.ultra.ui.util.createRootShell
import com.dergoogler.mmrl.webui.util.WebUIOptions
import com.dergoogler.mmrl.webui.view.WebUIView
import com.sukisu.ultra.ui.theme.ThemeConfig
import com.sukisu.ultra.ui.theme._isSystemInDarkTheme
import com.sukisu.ultra.ui.theme.createColorScheme
import kotlinx.coroutines.launch
import java.io.File
import com.dergoogler.mmrl.webui.interfaces.WXOptions
@SuppressLint("SetJavaScriptEnabled")
class WebUIActivity : ComponentActivity() {
val modId get() = intent.getModId() ?: throw IllegalArgumentException("Invalid Module ID")
val prefs: SharedPreferences get() = getSharedPreferences("settings", MODE_PRIVATE)
val context: Context get() = this
private lateinit var webviewInterface: WebViewInterface
private var rootShell: Shell? = null
override fun onCreate(savedInstanceState: Bundle?) {
// Enable edge to edge
enableEdgeToEdge()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
@@ -46,76 +37,44 @@ class WebUIActivity : ComponentActivity() {
super.onCreate(savedInstanceState)
val darkTheme = when (ThemeConfig.forceDarkMode) {
true -> true
false -> false
null -> _isSystemInDarkTheme(context)
val moduleId = intent.getStringExtra("id")!!
val name = intent.getStringExtra("name")!!
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
@Suppress("DEPRECATION")
setTaskDescription(ActivityManager.TaskDescription("SukiSU-Ultra - $name"))
} else {
val taskDescription =
ActivityManager.TaskDescription.Builder().setLabel("SukiSU-Ultra - $name").build()
setTaskDescription(taskDescription)
}
val colorScheme = createColorScheme(
context = context,
darkTheme = darkTheme
)
val loading = createLoadingRenderer(colorScheme)
setContentView(loading)
lifecycleScope.launch {
val ready = initPlatform(context)
if (ready.await()) {
init()
return@launch
}
confirm(
ConfirmData(
title = "Failed!",
description = "Failed to initialize platform. Please try again.",
confirmText = "Close",
onConfirm = {
finish()
},
),
colorScheme = colorScheme
)
}
}
private fun init() {
val webDebugging = prefs.getBoolean("enable_web_debugging", false)
val options = WebUIOptions(
modId = modId,
debug = webDebugging,
// keep plugins disabled for security reasons
pluginsEnabled = false,
context = context,
)
val prefs = getSharedPreferences("settings", MODE_PRIVATE)
WebView.setWebContentsDebuggingEnabled(prefs.getBoolean("enable_web_debugging", false))
val moduleDir = "/data/adb/modules/${moduleId}"
val webRoot = File("${moduleDir}/webroot")
val rootShell = createRootShell(true).also { this.rootShell = it }
val webViewAssetLoader = WebViewAssetLoader.Builder()
.setDomain("mui.kernelsu.org")
.addPathHandler(
"/",
SuFilePathHandler(this, modId.webrootDir, rootShell)
SuFilePathHandler(this, webRoot, rootShell)
)
.build()
val webViewClient = object : WebViewClient() {
override fun shouldInterceptRequest(
view: WebView,
request: WebResourceRequest,
request: WebResourceRequest
): WebResourceResponse? {
return webViewAssetLoader.shouldInterceptRequest(request.url)
}
}
val webView = WebUIView(options).apply {
val webView = WebView(this).apply {
ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
val inset = insets.getInsets(WindowInsetsCompat.Type.systemBars())
view.updateLayoutParams<ViewGroup.MarginLayoutParams> {
view.updateLayoutParams<MarginLayoutParams> {
leftMargin = inset.left
rightMargin = inset.right
topMargin = inset.top
@@ -123,17 +82,20 @@ class WebUIActivity : ComponentActivity() {
}
return@setOnApplyWindowInsetsListener insets
}
addJavascriptInterface<WebViewInterface>()
settings.javaScriptEnabled = true
settings.domStorageEnabled = true
settings.allowFileAccess = false
webviewInterface = WebViewInterface(WXOptions(this@WebUIActivity, this, ModId(moduleId)))
addJavascriptInterface(webviewInterface, "ksu")
setWebViewClient(webViewClient)
loadDomain()
loadUrl("https://mui.kernelsu.org/index.html")
}
setContentView(webView)
}
override fun onDestroy() {
runCatching { rootShell?.close() }
super.onDestroy()
runCatching { rootShell?.close() }
}
}

View File

@@ -1,126 +1,110 @@
package com.sukisu.ultra.ui.webui
import android.content.Context
import android.content.SharedPreferences
import android.app.ActivityManager
import android.os.Build
import android.os.Bundle
import android.webkit.WebView
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.lifecycleScope
import com.dergoogler.mmrl.platform.Platform
import com.dergoogler.mmrl.platform.PlatformManager
import com.dergoogler.mmrl.webui.activity.WXActivity
import com.dergoogler.mmrl.webui.util.WebUIOptions
import com.dergoogler.mmrl.webui.view.WebUIXView
import com.dergoogler.mmrl.platform.model.ModId
import com.dergoogler.mmrl.ui.component.Loading
import com.dergoogler.mmrl.webui.screen.WebUIScreen
import com.dergoogler.mmrl.webui.util.rememberWebUIOptions
import com.sukisu.ultra.BuildConfig
import com.sukisu.ultra.ui.theme.KernelSUTheme
import com.sukisu.ultra.ui.theme.ThemeConfig
import com.sukisu.ultra.ui.theme._isSystemInDarkTheme
import kotlinx.coroutines.CoroutineScope
import kotlin.jvm.java
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class WebUIXActivity : ComponentActivity() {
private lateinit var webView: WebView
class WebUIXActivity : WXActivity() {
private val userAgent
get(): String {
val ksuVersion = BuildConfig.VERSION_CODE
val platform = PlatformManager.get(Platform.Unknown) {
platform
val platform = Platform.get("Unknown") {
platform.name
}
val platformVersion = PlatformManager.get(-1) {
val platformVersion = Platform.get(-1) {
moduleManager.versionCode
}
val osVersion = Build.VERSION.RELEASE
val deviceModel = Build.MODEL
return "SukiSU-Ultra/$ksuVersion (Linux; Android $osVersion; $deviceModel; ${platform.name}/$platformVersion)"
return "SukiSU-Ultra /$ksuVersion (Linux; Android $osVersion; $deviceModel; $platform/$platformVersion)"
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
val prefs: SharedPreferences get() = getSharedPreferences("settings", MODE_PRIVATE)
val context: Context get() = this
webView = WebView(this)
override suspend fun onRender(scope: CoroutineScope) {
super.onRender(scope)
lifecycleScope.launch {
initPlatform()
}
val modId =
this.modId
?: throw IllegalArgumentException("modId cannot be null or empty")
val moduleId = intent.getStringExtra("id")!!
val name = intent.getStringExtra("name")!!
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
@Suppress("DEPRECATION")
setTaskDescription(ActivityManager.TaskDescription("SukiSU-Ultra - $name"))
} else {
val taskDescription =
ActivityManager.TaskDescription.Builder().setLabel("SukiSU-Ultra - $name").build()
setTaskDescription(taskDescription)
}
val webDebugging = prefs.getBoolean("enable_web_debugging", false)
val erudaInject = prefs.getBoolean("use_webuix_eruda", false)
val prefs = getSharedPreferences("settings", MODE_PRIVATE)
setContent {
// keep the compose logic so custom background continue to work
KernelSUTheme {
var ready by remember { mutableStateOf(false) }
var isLoading by remember { mutableStateOf(true) }
LaunchedEffect(Unit) {
val init = initPlatform(context)
ready = init.await()
}
if (!ready) {
Box(
modifier = Modifier
.fillMaxSize(),
contentAlignment = Alignment.Center
) {
CircularProgressIndicator()
LaunchedEffect(Platform.isAlive) {
while (!Platform.isAlive) {
delay(1000)
}
isLoading = false
}
if (isLoading) {
Loading()
return@KernelSUTheme
}
val darkTheme = remember(ThemeConfig) {
when (ThemeConfig.forceDarkMode) {
true -> true
false -> false
null -> _isSystemInDarkTheme(context)
}
}
val webDebugging = prefs.getBoolean("enable_web_debugging", false)
val erudaInject = prefs.getBoolean("use_webuix_eruda", false)
val dark = isSystemInDarkTheme()
val options = WebUIOptions(
modId = modId,
context = context,
val options = rememberWebUIOptions(
modId = ModId(moduleId),
debug = webDebugging,
isDarkMode = darkTheme,
// keep plugins disabled for security reasons
pluginsEnabled = false,
appVersionCode = BuildConfig.VERSION_CODE,
isDarkMode = dark,
enableEruda = erudaInject,
cls = WebUIXActivity::class.java,
userAgentString = userAgent,
colorScheme = MaterialTheme.colorScheme
userAgentString = userAgent
)
// Activity Title
config {
if (title != null) {
setActivityTitle("SukiSU-Ultra - $title")
}
}
AndroidView(
factory = { WebUIXView(options) },
update = { view ->
val v = view.apply {
wx.addJavascriptInterface<WebViewInterface>()
}
// pass it for the activity
this.view = v
}
WebUIScreen(
webView = webView,
options = options,
interfaces = listOf(
WebViewInterface.factory()
)
)
}
}

View File

@@ -1,23 +1,17 @@
package com.sukisu.ultra.ui.webui
import android.app.Activity
import android.content.Context
import android.webkit.WebView
import com.dergoogler.mmrl.platform.model.ModId
import android.os.Handler
import android.os.Looper
import android.text.TextUtils
import android.util.Log
import android.view.Window
import android.webkit.JavascriptInterface
import android.widget.Toast
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import com.dergoogler.mmrl.platform.file.ExtFile
import com.dergoogler.mmrl.platform.model.ModId.Companion.moduleDir
import com.dergoogler.mmrl.webui.interfaces.WXInterface
import com.dergoogler.mmrl.webui.interfaces.WXOptions
import com.dergoogler.mmrl.webui.util.WebUIOptions
import com.dergoogler.mmrl.webui.model.JavaScriptInterface
import com.topjohnwu.superuser.CallbackList
import com.topjohnwu.superuser.ShellUtils
import com.topjohnwu.superuser.internal.UiThreadHandler
@@ -27,14 +21,19 @@ import com.sukisu.ultra.ui.util.withNewRootShell
import org.json.JSONArray
import org.json.JSONObject
import com.sukisu.ultra.ui.util.*
import java.io.File
import java.util.concurrent.CompletableFuture
import kotlin.collections.iterator
internal class WebViewInterface(wxOptions: WXOptions) : WXInterface(wxOptions) {
// `ExtFile` to make sure that the platform won't get called when it is used within KSU WebUI
private val modDir: ExtFile get() = modId.moduleDir.toExtFile()
class WebViewInterface(
wxOptions: WXOptions,
) : WXInterface(wxOptions) {
override var name: String = "ksu"
override var name = "ksu"
companion object {
fun factory() = JavaScriptInterface(WebViewInterface::class.java)
}
private val modDir get() = "/data/adb/modules/${modId.id}"
@JavascriptInterface
fun exec(cmd: String): String {
@@ -67,7 +66,7 @@ internal class WebViewInterface(wxOptions: WXOptions) : WXInterface(wxOptions) {
fun exec(
cmd: String,
options: String?,
callbackFunc: String,
callbackFunc: String
) {
val finalCommand = StringBuilder()
processOptions(finalCommand, options)
@@ -173,18 +172,14 @@ internal class WebViewInterface(wxOptions: WXOptions) : WXInterface(wxOptions) {
@JavascriptInterface
fun fullScreen(enable: Boolean) {
if (activity == null) return
try {
if (context is Activity) {
Handler(Looper.getMainLooper()).post {
if (enable) {
hideSystemUI(activity!!.window)
hideSystemUI(activity.window)
} else {
showSystemUI(activity!!.window)
showSystemUI(activity.window)
}
}
} catch (e: Exception) {
Log.e("WebViewInterface", "fullScreen", e)
}
}
@@ -192,8 +187,8 @@ internal class WebViewInterface(wxOptions: WXOptions) : WXInterface(wxOptions) {
fun moduleInfo(): String {
val moduleInfos = JSONArray(listModules())
val currentModuleInfo = JSONObject()
currentModuleInfo.put("moduleDir", modDir.path)
val moduleId = modDir.getName()
currentModuleInfo.put("moduleDir", modDir)
val moduleId = File(modDir).getName()
for (i in 0 until moduleInfos.length()) {
val currentInfo = moduleInfos.getJSONObject(i)
@@ -213,25 +208,21 @@ internal class WebViewInterface(wxOptions: WXOptions) : WXInterface(wxOptions) {
// =================== KPM支持 =============================
@JavascriptInterface
fun listAllKpm(): String {
fun listAllKpm() : String {
return listKpmModules()
}
@JavascriptInterface
fun controlKpm(name: String, args: String): Int {
fun controlKpm(name: String, args: String) : Int {
return controlKpmModule(name, args)
}
}
private fun hideSystemUI(window: Window) =
fun hideSystemUI(window: Window) =
WindowInsetsControllerCompat(window, window.decorView).let { controller ->
controller.hide(WindowInsetsCompat.Type.systemBars())
controller.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
controller.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
private fun showSystemUI(window: Window) =
WindowInsetsControllerCompat(
window,
window.decorView
).show(WindowInsetsCompat.Type.systemBars())
fun showSystemUI(window: Window) =
WindowInsetsControllerCompat(window, window.decorView).show(WindowInsetsCompat.Type.systemBars())

View File

@@ -148,8 +148,8 @@ fun MoreSettingsScreen(
var showDpiConfirmDialog by remember { mutableStateOf(false) }
var showImageEditor by remember { mutableStateOf(false) }
// 动态签名配置状态
var dynamicSignConfig by remember { mutableStateOf<Natives.DynamicSignConfig?>(null) }
// 动态管理器配置状态
var dynamicSignConfig by remember { mutableStateOf<Natives.DynamicManagerConfig?>(null) }
var isDynamicSignEnabled by remember { mutableStateOf(false) }
var dynamicSignSize by remember { mutableStateOf("") }
var dynamicSignHash by remember { mutableStateOf("") }
@@ -674,8 +674,8 @@ fun MoreSettingsScreen(
}
LaunchedEffect(Unit) {
// 初始化动态签名配置
dynamicSignConfig = Natives.getDynamicSign()
// 初始化动态管理器配置
dynamicSignConfig = Natives.getDynamicManager()
dynamicSignConfig?.let { config ->
if (config.isValid()) {
isDynamicSignEnabled = true
@@ -696,11 +696,11 @@ fun MoreSettingsScreen(
}
}
// 动态签名配置对话框
// 动态管理器配置对话框
if (showDynamicSignDialog) {
AlertDialog(
onDismissRequest = { showDynamicSignDialog = false },
title = { Text(stringResource(R.string.dynamic_sign_title)) },
title = { Text(stringResource(R.string.dynamic_manager_title)) },
text = {
Column(
modifier = Modifier.verticalScroll(rememberScrollState())
@@ -718,7 +718,7 @@ fun MoreSettingsScreen(
onCheckedChange = { isDynamicSignEnabled = it }
)
Spacer(modifier = Modifier.width(12.dp))
Text(stringResource(R.string.enable_dynamic_sign))
Text(stringResource(R.string.enable_dynamic_manager))
}
Spacer(modifier = Modifier.height(16.dp))
@@ -773,18 +773,18 @@ fun MoreSettingsScreen(
if (isDynamicSignEnabled) {
val size = parseDynamicSignSize(dynamicSignSize)
if (size != null && size > 0 && dynamicSignHash.length == 64) {
val success = Natives.setDynamicSign(size, dynamicSignHash)
val success = Natives.setDynamicManager(size, dynamicSignHash)
if (success) {
dynamicSignConfig = Natives.DynamicSignConfig(size, dynamicSignHash)
dynamicSignConfig = Natives.DynamicManagerConfig(size, dynamicSignHash)
Toast.makeText(
context,
context.getString(R.string.dynamic_sign_set_success),
context.getString(R.string.dynamic_manager_set_success),
Toast.LENGTH_SHORT
).show()
} else {
Toast.makeText(
context,
context.getString(R.string.dynamic_sign_set_failed),
context.getString(R.string.dynamic_manager_set_failed),
Toast.LENGTH_SHORT
).show()
}
@@ -797,20 +797,20 @@ fun MoreSettingsScreen(
return@Button
}
} else {
val success = Natives.clearDynamicSign()
val success = Natives.clearDynamicManager()
if (success) {
dynamicSignConfig = null
dynamicSignSize = ""
dynamicSignHash = ""
Toast.makeText(
context,
context.getString(R.string.dynamic_sign_disabled_success),
context.getString(R.string.dynamic_manager_disabled_success),
Toast.LENGTH_SHORT
).show()
} else {
Toast.makeText(
context,
context.getString(R.string.dynamic_sign_clear_failed),
context.getString(R.string.dynamic_manager_clear_failed),
Toast.LENGTH_SHORT
).show()
return@Button
@@ -1417,18 +1417,18 @@ fun MoreSettingsScreen(
}
)
}
// 动态签名设置
if (Natives.version >= Natives.MINIMAL_SUPPORTED_DYNAMIC_SIGN) {
// 动态管理器设置
if (Natives.version >= Natives.MINIMAL_SUPPORTED_DYNAMIC_MANAGER) {
SettingItem(
icon = Icons.Filled.Security,
title = stringResource(R.string.dynamic_sign_title),
title = stringResource(R.string.dynamic_manager_title),
subtitle = if (isDynamicSignEnabled) {
stringResource(
R.string.dynamic_sign_enabled_summary,
R.string.dynamic_manager_enabled_summary,
dynamicSignSize
)
} else {
stringResource(R.string.dynamic_sign_disabled)
stringResource(R.string.dynamic_manager_disabled)
},
onClick = { showDynamicSignDialog = true }
)

View File

@@ -570,19 +570,19 @@
<string name="selected_apps_count">%1$d aplikasi dipilih</string>
<string name="already_added_apps_count">%1$d aplikasi sudah ditambahkan</string>
<string name="all_apps_already_added">Semua aplikasi telah ditambahkan</string>
<string name="dynamic_sign_title">Konfigurasi Tanda Tangan Dinamis</string>
<string name="dynamic_sign_enabled_summary">Diaktifkan (Ukuran: %s)</string>
<string name="dynamic_sign_disabled">Dinonaktifkan</string>
<string name="enable_dynamic_sign">Aktifkan Tanda Tangan Dinamis</string>
<string name="dynamic_manager_title">Konfigurasi Tanda Tangan Dinamis</string>
<string name="dynamic_manager_enabled_summary">Diaktifkan (Ukuran: %s)</string>
<string name="dynamic_manager_disabled">Dinonaktifkan</string>
<string name="enable_dynamic_manager">Aktifkan Tanda Tangan Dinamis</string>
<string name="signature_size">Ukuran Tanda Tangan</string>
<string name="signature_hash">Hash Tanda Tangan</string>
<string name="hash_must_be_64_chars">Hash harus 64 karakter heksadesimal</string>
<string name="dynamic_sign_set_success">Konfigurasi tanda tangan dinamis berhasil diatur</string>
<string name="dynamic_sign_set_failed">Gagal mengatur konfigurasi tanda tangan dinamis</string>
<string name="dynamic_manager_set_success">Konfigurasi tanda tangan dinamis berhasil diatur</string>
<string name="dynamic_manager_set_failed">Gagal mengatur konfigurasi tanda tangan dinamis</string>
<string name="invalid_sign_config">Konfigurasi tanda tangan tidak valid</string>
<string name="dynamic_sign_disabled_success">Tanda tangan dinamis dinonaktifkan</string>
<string name="dynamic_sign_clear_failed">Gagal membersihkan tanda tangan dinamis</string>
<string name="dynamic_signature">Dinamis</string>
<string name="dynamic_manager_disabled_success">Tanda tangan dinamis dinonaktifkan</string>
<string name="dynamic_manager_clear_failed">Gagal membersihkan tanda tangan dinamis</string>
<string name="dynamic_managerature">Dinamis</string>
<string name="signature_index">Tanda Tangan %1$d</string>
<string name="unknown_signature">Tidak diketahui</string>
<string name="multi_manager_list">Manajer Aktif</string>

View File

@@ -570,19 +570,19 @@
<string name="selected_apps_count">%1$d 個のアプリを選択済み</string>
<string name="already_added_apps_count">%1$d 個のアプリを追加済み</string>
<string name="all_apps_already_added">すべてのアプリが追加されました</string>
<string name="dynamic_sign_title">動的な署名の構成</string>
<string name="dynamic_sign_enabled_summary">有効 (サイズ: %s)</string>
<string name="dynamic_sign_disabled">無効</string>
<string name="enable_dynamic_sign">動的な署名を有効化</string>
<string name="dynamic_manager_title">動的な署名の構成</string>
<string name="dynamic_manager_enabled_summary">有効 (サイズ: %s)</string>
<string name="dynamic_manager_disabled">無効</string>
<string name="enable_dynamic_manager">動的な署名を有効化</string>
<string name="signature_size">署名のサイズ</string>
<string name="signature_hash">署名のハッシュ</string>
<string name="hash_must_be_64_chars">ハッシュは 64 桁の 16 進数の文字列でなければなりません。</string>
<string name="dynamic_sign_set_success">動的な署名の構成が正常に設定されました</string>
<string name="dynamic_sign_set_failed">動的な署名の構成の設定に失敗しました</string>
<string name="dynamic_manager_set_success">動的な署名の構成が正常に設定されました</string>
<string name="dynamic_manager_set_failed">動的な署名の構成の設定に失敗しました</string>
<string name="invalid_sign_config">無効な署名の構成</string>
<string name="dynamic_sign_disabled_success">動的な署名が無効です</string>
<string name="dynamic_sign_clear_failed">動的な署名の消去に失敗しました</string>
<string name="dynamic_signature">動的</string>
<string name="dynamic_manager_disabled_success">動的な署名が無効です</string>
<string name="dynamic_manager_clear_failed">動的な署名の消去に失敗しました</string>
<string name="dynamic_managerature">動的</string>
<string name="signature_index">署名 %1$d</string>
<string name="unknown_signature">不明</string>
<string name="multi_manager_list">有効なマネージャー</string>

View File

@@ -570,19 +570,19 @@
<string name="selected_apps_count">Выбрано %1$d приложений</string>
<string name="already_added_apps_count">%1$d приложений уже добавлено</string>
<string name="all_apps_already_added">Все приложения были добавлены</string>
<string name="dynamic_sign_title">Конфигурация динамической подписи</string>
<string name="dynamic_sign_enabled_summary">Включено (размер: %s)</string>
<string name="dynamic_sign_disabled">Выключено</string>
<string name="enable_dynamic_sign">Включить динамическую подпись</string>
<string name="dynamic_manager_title">Конфигурация динамической подписи</string>
<string name="dynamic_manager_enabled_summary">Включено (размер: %s)</string>
<string name="dynamic_manager_disabled">Выключено</string>
<string name="enable_dynamic_manager">Включить динамическую подпись</string>
<string name="signature_size">Размер подписи</string>
<string name="signature_hash">Хэш подписи</string>
<string name="hash_must_be_64_chars">Хеш должен содержать 64 шестнадцатеричных символа</string>
<string name="dynamic_sign_set_success">Конфигурация динамической подписи успешно установлена</string>
<string name="dynamic_sign_set_failed">Не удалось установить конфигурацию динамической подписи</string>
<string name="dynamic_manager_set_success">Конфигурация динамической подписи успешно установлена</string>
<string name="dynamic_manager_set_failed">Не удалось установить конфигурацию динамической подписи</string>
<string name="invalid_sign_config">Неверная конфигурация подписи</string>
<string name="dynamic_sign_disabled_success">Динамическая подпись отключена</string>
<string name="dynamic_sign_clear_failed">Не удалось очистить динамическую подпись</string>
<string name="dynamic_signature">Динамическая</string>
<string name="dynamic_manager_disabled_success">Динамическая подпись отключена</string>
<string name="dynamic_manager_clear_failed">Не удалось очистить динамическую подпись</string>
<string name="dynamic_managerature">Динамическая</string>
<string name="signature_index">Подпись %1$d</string>
<string name="unknown_signature">Неизвестно</string>
<string name="multi_manager_list">Активный менеджер</string>

View File

@@ -568,19 +568,19 @@
<string name="selected_apps_count">%1$d uygulama seçildi</string>
<string name="already_added_apps_count">%1$d uygulama zaten eklendi</string>
<string name="all_apps_already_added">Tüm uygulamalar eklendi</string>
<string name="dynamic_sign_title">Dinamik İmza Yapılandırması</string>
<string name="dynamic_sign_enabled_summary">Etkin (Boyut: %s)</string>
<string name="dynamic_sign_disabled">Devre Dışı</string>
<string name="enable_dynamic_sign">Dinamik İmzayı Etkinleştir</string>
<string name="dynamic_manager_title">Dinamik İmza Yapılandırması</string>
<string name="dynamic_manager_enabled_summary">Etkin (Boyut: %s)</string>
<string name="dynamic_manager_disabled">Devre Dışı</string>
<string name="enable_dynamic_manager">Dinamik İmzayı Etkinleştir</string>
<string name="signature_size">İmza Boyutu</string>
<string name="signature_hash">İmza Hash</string>
<string name="hash_must_be_64_chars">Hash, 64 adet onaltılık karakterden oluşmalıdır</string>
<string name="dynamic_sign_set_success">Dinamik imza yapılandırması başarıyla ayarlandı</string>
<string name="dynamic_sign_set_failed">Dinamik imza yapılandırması ayarlanamadı</string>
<string name="dynamic_manager_set_success">Dinamik imza yapılandırması başarıyla ayarlandı</string>
<string name="dynamic_manager_set_failed">Dinamik imza yapılandırması ayarlanamadı</string>
<string name="invalid_sign_config">Geçersiz imza yapılandırması</string>
<string name="dynamic_sign_disabled_success">Dinamik imza devre dışı bırakıldı</string>
<string name="dynamic_sign_clear_failed">Dinamik imza temizlenemedi</string>
<string name="dynamic_signature">Dinamik</string>
<string name="dynamic_manager_disabled_success">Dinamik imza devre dışı bırakıldı</string>
<string name="dynamic_manager_clear_failed">Dinamik imza temizlenemedi</string>
<string name="dynamic_managerature">Dinamik</string>
<string name="signature_index">İmza %1$d</string>
<string name="unknown_signature">Bilinmiyor</string>
<string name="multi_manager_list">Aktif Yönetici</string>

View File

@@ -568,19 +568,19 @@
<string name="selected_apps_count">%1$d ứng dụng đã chọn</string>
<string name="already_added_apps_count">%1$d ứng dụng đã thêm</string>
<string name="all_apps_already_added">Tất cả các ứng dụng đã được thêm vào</string>
<string name="dynamic_sign_title">Cấu hình chữ ký động</string>
<string name="dynamic_sign_enabled_summary">Đã kích hoạt (Size: %s)</string>
<string name="dynamic_sign_disabled">Đã vô hiệu hoá</string>
<string name="enable_dynamic_sign">Kích hoạt chữ ký động</string>
<string name="dynamic_manager_title">Cấu hình chữ ký động</string>
<string name="dynamic_manager_enabled_summary">Đã kích hoạt (Size: %s)</string>
<string name="dynamic_manager_disabled">Đã vô hiệu hoá</string>
<string name="enable_dynamic_manager">Kích hoạt chữ ký động</string>
<string name="signature_size">Size chữ ký</string>
<string name="signature_hash">Hash chữ ký</string>
<string name="hash_must_be_64_chars">Hash phải dài 64 ký tự thập lục phân</string>
<string name="dynamic_sign_set_success">Cấu hình chữ ký động đã được thiết lập thành công</string>
<string name="dynamic_sign_set_failed">Thiết lập cấu hình chữ ký động thất bại</string>
<string name="dynamic_manager_set_success">Cấu hình chữ ký động đã được thiết lập thành công</string>
<string name="dynamic_manager_set_failed">Thiết lập cấu hình chữ ký động thất bại</string>
<string name="invalid_sign_config">Cấu hình chữ ký không hợp lệ</string>
<string name="dynamic_sign_disabled_success">Chữ ký động đã bị vô hiệu hoá</string>
<string name="dynamic_sign_clear_failed">Xoá chữ ký động thất bại</string>
<string name="dynamic_signature">Chữ ký động</string>
<string name="dynamic_manager_disabled_success">Chữ ký động đã bị vô hiệu hoá</string>
<string name="dynamic_manager_clear_failed">Xoá chữ ký động thất bại</string>
<string name="dynamic_managerature">Chữ ký động</string>
<string name="signature_index">Chữ ký %1$d</string>
<string name="unknown_signature">Không xác định</string>
<string name="multi_manager_list">Trình quản lý đang hoạt động</string>

View File

@@ -568,19 +568,19 @@
<string name="selected_apps_count">%1$d 个已选应用</string>
<string name="already_added_apps_count">%1$d 个已添加应用</string>
<string name="all_apps_already_added">所有应用均已添加</string>
<string name="dynamic_sign_title">动态签名配置</string>
<string name="dynamic_sign_enabled_summary">已启用(大小: %s</string>
<string name="dynamic_sign_disabled">未启用</string>
<string name="enable_dynamic_sign">启用动态签名</string>
<string name="signature_size">签名大小</string>
<string name="signature_hash">签名哈希值</string>
<string name="dynamic_manager_title">动态管理器配置</string>
<string name="dynamic_manager_enabled_summary">已启用(大小: %s</string>
<string name="dynamic_manager_disabled">未启用</string>
<string name="enable_dynamic_manager">启用动态管理器</string>
<string name="signature_size">动态管理器签名大小</string>
<string name="signature_hash">动态管理器签名哈希值</string>
<string name="hash_must_be_64_chars">哈希值必须是 64 位十六进制字符</string>
<string name="dynamic_sign_set_success">动态签名配置设置成功</string>
<string name="dynamic_sign_set_failed">动态签名配置设置失败</string>
<string name="dynamic_manager_set_success">动态管理器配置设置成功</string>
<string name="dynamic_manager_set_failed">动态管理器配置设置失败</string>
<string name="invalid_sign_config">无效的签名配置</string>
<string name="dynamic_sign_disabled_success">动态签名已禁用</string>
<string name="dynamic_sign_clear_failed">清除动态签名错误</string>
<string name="dynamic_signature">动态</string>
<string name="dynamic_manager_disabled_success">动态管理器已禁用</string>
<string name="dynamic_manager_clear_failed">清除动态管理器错误</string>
<string name="dynamic_managerature">动态</string>
<string name="signature_index">签名 %1$d</string>
<string name="unknown_signature">未知</string>
<string name="multi_manager_list">活跃管理器</string>

View File

@@ -565,19 +565,19 @@
<string name="selected_apps_count">%1$d 個已選應用程式</string>
<string name="already_added_apps_count">%1$d 個已添加應用程式</string>
<string name="all_apps_already_added">所有应用均已添加</string>
<string name="dynamic_sign_title">動態簽名配置</string>
<string name="dynamic_sign_enabled_summary">已啟用(大小: %s</string>
<string name="dynamic_sign_disabled">未啟用</string>
<string name="enable_dynamic_sign">啟用動態簽名</string>
<string name="dynamic_manager_title">動態簽名配置</string>
<string name="dynamic_manager_enabled_summary">已啟用(大小: %s</string>
<string name="dynamic_manager_disabled">未啟用</string>
<string name="enable_dynamic_manager">啟用動態簽名</string>
<string name="signature_size">簽名大小</string>
<string name="signature_hash">簽名哈希值</string>
<string name="hash_must_be_64_chars">哈希值必須是 64 位十六進制字符</string>
<string name="dynamic_sign_set_success">動態簽名配置設定成功</string>
<string name="dynamic_sign_set_failed">動態簽名配置設定失敗</string>
<string name="dynamic_manager_set_success">動態簽名配置設定成功</string>
<string name="dynamic_manager_set_failed">動態簽名配置設定失敗</string>
<string name="invalid_sign_config">無效嘅簽名配置</string>
<string name="dynamic_sign_disabled_success">動態簽名已禁用</string>
<string name="dynamic_sign_clear_failed">清除動態簽名錯誤</string>
<string name="dynamic_signature">動態</string>
<string name="dynamic_manager_disabled_success">動態簽名已禁用</string>
<string name="dynamic_manager_clear_failed">清除動態簽名錯誤</string>
<string name="dynamic_managerature">動態</string>
<string name="signature_index">簽名 %1$d</string>
<string name="unknown_signature">未知</string>
<string name="multi_manager_list">活躍管理器</string>

View File

@@ -568,19 +568,19 @@
<string name="selected_apps_count">%1$d 個已選應用程式</string>
<string name="already_added_apps_count">%1$d 個已添加應用程式</string>
<string name="all_apps_already_added">所有应用均已添加</string>
<string name="dynamic_sign_title">动态签名配置</string>
<string name="dynamic_sign_enabled_summary">已启用(大小: %s</string>
<string name="dynamic_sign_disabled">未启用</string>
<string name="enable_dynamic_sign">啟用動態簽名</string>
<string name="dynamic_manager_title">动态签名配置</string>
<string name="dynamic_manager_enabled_summary">已启用(大小: %s</string>
<string name="dynamic_manager_disabled">未启用</string>
<string name="enable_dynamic_manager">啟用動態簽名</string>
<string name="signature_size">簽名大小</string>
<string name="signature_hash">簽名哈希值</string>
<string name="hash_must_be_64_chars">哈希值必須是 64 位十六進制字符</string>
<string name="dynamic_sign_set_success">動態簽名配置設定成功</string>
<string name="dynamic_sign_set_failed">動態簽名配置設定失敗</string>
<string name="dynamic_manager_set_success">動態簽名配置設定成功</string>
<string name="dynamic_manager_set_failed">動態簽名配置設定失敗</string>
<string name="invalid_sign_config">無效的簽名配置</string>
<string name="dynamic_sign_disabled_success">動態簽名已禁用</string>
<string name="dynamic_sign_clear_failed">清除動態簽名錯誤</string>
<string name="dynamic_signature">動態</string>
<string name="dynamic_manager_disabled_success">動態簽名已禁用</string>
<string name="dynamic_manager_clear_failed">清除動態簽名錯誤</string>
<string name="dynamic_managerature">動態</string>
<string name="signature_index">簽名 %1$d</string>
<string name="unknown_signature">未知</string>
<string name="multi_manager_list">活躍管理器</string>

View File

@@ -570,19 +570,19 @@
<string name="selected_apps_count">%1$d apps selected</string>
<string name="already_added_apps_count">%1$d apps already added</string>
<string name="all_apps_already_added">All apps have been added</string>
<string name="dynamic_sign_title">Dynamic Signature Configuration</string>
<string name="dynamic_sign_enabled_summary">Enabled (Size: %s)</string>
<string name="dynamic_sign_disabled">Disabled</string>
<string name="enable_dynamic_sign">Enable Dynamic Signature</string>
<string name="signature_size">Signature Size</string>
<string name="signature_hash">Signature Hash</string>
<string name="dynamic_manager_title">Dynamic Manager Configuration</string>
<string name="dynamic_manager_enabled_summary">Enabled (Size: %s)</string>
<string name="dynamic_manager_disabled">Disabled</string>
<string name="enable_dynamic_manager">Enable Dynamic Manager</string>
<string name="signature_size">Dynamic Manager Signature Size</string>
<string name="signature_hash">Dynamic Manager Signature Hash</string>
<string name="hash_must_be_64_chars">Hash must be 64 hexadecimal characters</string>
<string name="dynamic_sign_set_success">Dynamic signature configuration set successfully</string>
<string name="dynamic_sign_set_failed">Failed to set dynamic signature configuration</string>
<string name="invalid_sign_config">Invalid signature configuration</string>
<string name="dynamic_sign_disabled_success">Dynamic signature disabled</string>
<string name="dynamic_sign_clear_failed">Failed to clear dynamic signature</string>
<string name="dynamic_signature">Dynamic</string>
<string name="dynamic_manager_set_success">Dynamic Manager configuration set successfully</string>
<string name="dynamic_manager_set_failed">Failed to set dynamic Manager configuration</string>
<string name="invalid_sign_config">Invalid Manager configuration</string>
<string name="dynamic_manager_disabled_success">Dynamic Manager disabled</string>
<string name="dynamic_manager_clear_failed">Failed to clear dynamic Manager</string>
<string name="dynamic_managerature">Dynamic</string>
<string name="signature_index">Signature %1$d</string>
<string name="unknown_signature">Unknown</string>
<string name="multi_manager_list">Active Manager</string>

View File

@@ -23,9 +23,7 @@ compose-material = "1.8.3"
compose-material3 = "1.3.2"
compose-ui = "1.8.3"
documentfile = "1.1.0"
mmrl = "v33953"
webui-x-portable = "953fad192a"
swiperefreshlayout = "1.1.0"
mmrl = "2bb00b3c2b"
[plugins]
agp-app = { id = "com.android.application", version.ref = "agp" }
@@ -59,8 +57,6 @@ androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecyc
androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle" }
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" }
androidx-swiperefreshlayout = { group = "androidx.swiperefreshlayout", name = "swiperefreshlayout", version.ref = "swiperefreshlayout" }
androidx-webkit = { module = "androidx.webkit:webkit", version.ref = "webkit" }
com-github-topjohnwu-libsu-core = { group = "com.github.topjohnwu.libsu", name = "core", version.ref = "libsu" }
@@ -88,8 +84,8 @@ markdown = { group = "io.noties.markwon", name = "core", version.ref = "markdown
lsposed-cxx = { module = "org.lsposed.libcxx:libcxx", version = "29.0.13599879-beta2" }
androidx-documentfile = { group = "androidx.documentfile", name = "documentfile", version.ref = "documentfile" }
mmrl-webui-portable = { group = "com.github.MMRLApp.WebUI-X-Portable", name = "webui", version.ref = "webui-x-portable" }
mmrl-webui-jna = { group = "com.github.MMRLApp.WebUI-X-Portable", name = "jna", version.ref = "webui-x-portable" }
mmrl-webui = { group = "com.github.MMRLApp.MMRL", name = "webui", version.ref = "mmrl" }
mmrl-platform = { group = "com.github.MMRLApp.MMRL", name = "platform", version.ref = "mmrl" }
mmrl-ui = { group = "com.github.MMRLApp.MMRL", name = "ui", version.ref = "mmrl" }
mmrl-hidden-api = { group = "com.github.MMRLApp.MMRL", name = "hidden-api", version.ref = "mmrl" }

Binary file not shown.

Binary file not shown.