manager: some ui changes
* Steeing: add enhanced security dropdown * Settings: allow change module update check * Settings: allow always enable/disable feat * misc: update all deps --------- Co-authored-by: weishu <twsxtd@gmail.com> Co-authored-by: Ylarod <me@ylarod.cn> Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com> Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
This commit is contained in:
@@ -545,7 +545,7 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old)
|
|||||||
}
|
}
|
||||||
// disallow appuid decrease to any other uid if it is allowed to su
|
// disallow appuid decrease to any other uid if it is allowed to su
|
||||||
if (is_appuid(old_uid)) {
|
if (is_appuid(old_uid)) {
|
||||||
if (new_uid.val < old_uid.val && ksu_is_allow_uid_for_current(old_uid.val)) {
|
if (new_uid.val < old_uid.val && !ksu_is_allow_uid_for_current(old_uid.val)) {
|
||||||
pr_warn("find suspicious EoP: %d %s, from %d to %d\n",
|
pr_warn("find suspicious EoP: %d %s, from %d to %d\n",
|
||||||
current->pid, current->comm, old_uid.val, new_uid.val);
|
current->pid, current->comm, old_uid.val, new_uid.val);
|
||||||
send_sig(SIGKILL, current, 0);
|
send_sig(SIGKILL, current, 0);
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
|
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
|
||||||
import com.android.build.gradle.tasks.PackageAndroidArtifact
|
import com.android.build.gradle.tasks.PackageAndroidArtifact
|
||||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
alias(libs.plugins.agp.app)
|
alias(libs.plugins.agp.app)
|
||||||
@@ -17,6 +16,7 @@ plugins {
|
|||||||
|
|
||||||
val managerVersionCode: Int by rootProject.extra
|
val managerVersionCode: Int by rootProject.extra
|
||||||
val managerVersionName: String by rootProject.extra
|
val managerVersionName: String by rootProject.extra
|
||||||
|
val androidCmakeVersion: String by rootProject.extra
|
||||||
|
|
||||||
apksign {
|
apksign {
|
||||||
storeFileProperty = "KEYSTORE_FILE"
|
storeFileProperty = "KEYSTORE_FILE"
|
||||||
@@ -57,10 +57,6 @@ android {
|
|||||||
prefab = true
|
prefab = true
|
||||||
}
|
}
|
||||||
|
|
||||||
kotlin {
|
|
||||||
jvmToolchain(21)
|
|
||||||
}
|
|
||||||
|
|
||||||
packaging {
|
packaging {
|
||||||
jniLibs {
|
jniLibs {
|
||||||
useLegacyPackaging = true
|
useLegacyPackaging = true
|
||||||
@@ -78,7 +74,8 @@ android {
|
|||||||
|
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
path("src/main/cpp/CMakeLists.txt")
|
path = file("src/main/cpp/CMakeLists.txt")
|
||||||
|
version = androidCmakeVersion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,6 +123,7 @@ dependencies {
|
|||||||
implementation(libs.androidx.compose.ui.tooling.preview)
|
implementation(libs.androidx.compose.ui.tooling.preview)
|
||||||
implementation(libs.androidx.foundation)
|
implementation(libs.androidx.foundation)
|
||||||
implementation(libs.androidx.documentfile)
|
implementation(libs.androidx.documentfile)
|
||||||
|
implementation(libs.androidx.compose.foundation)
|
||||||
|
|
||||||
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
debugImplementation(libs.androidx.compose.ui.test.manifest)
|
||||||
debugImplementation(libs.androidx.compose.ui.tooling)
|
debugImplementation(libs.androidx.compose.ui.tooling)
|
||||||
|
|||||||
@@ -310,6 +310,14 @@ NativeBridge(setKernelUmountEnabled, jboolean, jboolean enabled) {
|
|||||||
return set_kernel_umount_enabled(enabled);
|
return set_kernel_umount_enabled(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NativeBridgeNP(isEnhancedSecurityEnabled, jboolean) {
|
||||||
|
return is_enhanced_security_enabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
NativeBridge(setEnhancedSecurityEnabled, jboolean, jboolean enabled) {
|
||||||
|
return set_enhanced_security_enabled(enabled);
|
||||||
|
}
|
||||||
|
|
||||||
// Check if KPM is enabled
|
// Check if KPM is enabled
|
||||||
NativeBridgeNP(isKPMEnabled, jboolean) {
|
NativeBridgeNP(isKPMEnabled, jboolean) {
|
||||||
return is_KPM_enable();
|
return is_KPM_enable();
|
||||||
|
|||||||
@@ -215,6 +215,22 @@ bool is_kernel_umount_enabled() {
|
|||||||
return value != 0;
|
return value != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool set_enhanced_security_enabled(bool enabled) {
|
||||||
|
return set_feature(KSU_FEATURE_ENHANCED_SECURITY, enabled ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_enhanced_security_enabled() {
|
||||||
|
uint64_t value = 0;
|
||||||
|
bool supported = false;
|
||||||
|
if (!get_feature(KSU_FEATURE_ENHANCED_SECURITY, &value, &supported)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!supported) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return value != 0;
|
||||||
|
}
|
||||||
|
|
||||||
void get_full_version(char* buff) {
|
void get_full_version(char* buff) {
|
||||||
struct ksu_get_full_version_cmd cmd = {0};
|
struct ksu_get_full_version_cmd cmd = {0};
|
||||||
if (ksuctl(KSU_IOCTL_GET_FULL_VERSION, &cmd) == 0) {
|
if (ksuctl(KSU_IOCTL_GET_FULL_VERSION, &cmd) == 0) {
|
||||||
|
|||||||
@@ -131,6 +131,7 @@ bool clear_uid_scanner_environment();
|
|||||||
enum ksu_feature_id {
|
enum ksu_feature_id {
|
||||||
KSU_FEATURE_SU_COMPAT = 0,
|
KSU_FEATURE_SU_COMPAT = 0,
|
||||||
KSU_FEATURE_KERNEL_UMOUNT = 1,
|
KSU_FEATURE_KERNEL_UMOUNT = 1,
|
||||||
|
KSU_FEATURE_ENHANCED_SECURITY = 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generic feature API
|
// Generic feature API
|
||||||
@@ -208,6 +209,11 @@ bool is_su_enabled();
|
|||||||
bool set_kernel_umount_enabled(bool enabled);
|
bool set_kernel_umount_enabled(bool enabled);
|
||||||
bool is_kernel_umount_enabled();
|
bool is_kernel_umount_enabled();
|
||||||
|
|
||||||
|
// Enhanced security
|
||||||
|
bool set_enhanced_security_enabled(bool enabled);
|
||||||
|
|
||||||
|
bool is_enhanced_security_enabled();
|
||||||
|
|
||||||
// Other command structures
|
// Other command structures
|
||||||
struct ksu_get_full_version_cmd {
|
struct ksu_get_full_version_cmd {
|
||||||
char version_full[KSU_FULL_VERSION_STRING]; // Output: full version string
|
char version_full[KSU_FULL_VERSION_STRING]; // Output: full version string
|
||||||
|
|||||||
@@ -109,6 +109,15 @@ object Natives {
|
|||||||
external fun isKernelUmountEnabled(): Boolean
|
external fun isKernelUmountEnabled(): Boolean
|
||||||
external fun setKernelUmountEnabled(enabled: Boolean): Boolean
|
external fun setKernelUmountEnabled(enabled: Boolean): Boolean
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Enhanced security can be enabled/disabled.
|
||||||
|
* 0: disabled
|
||||||
|
* 1: enabled
|
||||||
|
* negative : error
|
||||||
|
*/
|
||||||
|
external fun isEnhancedSecurityEnabled(): Boolean
|
||||||
|
external fun setEnhancedSecurityEnabled(enabled: Boolean): Boolean
|
||||||
|
|
||||||
external fun isKPMEnabled(): Boolean
|
external fun isKPMEnabled(): Boolean
|
||||||
external fun getHookType(): String
|
external fun getHookType(): String
|
||||||
|
|
||||||
|
|||||||
@@ -10,14 +10,20 @@ import androidx.activity.result.contract.ActivityResultContracts
|
|||||||
import androidx.compose.animation.*
|
import androidx.compose.animation.*
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.interaction.collectIsPressedAsState
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.Undo
|
import androidx.compose.material.icons.automirrored.filled.Undo
|
||||||
import androidx.compose.material.icons.filled.*
|
import androidx.compose.material.icons.filled.*
|
||||||
|
import androidx.compose.material.icons.rounded.EnhancedEncryption
|
||||||
import androidx.compose.material.icons.rounded.FolderDelete
|
import androidx.compose.material.icons.rounded.FolderDelete
|
||||||
|
import androidx.compose.material.icons.rounded.RemoveCircle
|
||||||
|
import androidx.compose.material.icons.rounded.RemoveModerator
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
@@ -130,60 +136,147 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// 卸载模块开关
|
val modeItems = listOf(
|
||||||
var umountChecked by rememberSaveable {
|
stringResource(id = R.string.settings_mode_default),
|
||||||
mutableStateOf(Natives.isDefaultUmountModules())
|
stringResource(id = R.string.settings_mode_temp_enable),
|
||||||
}
|
stringResource(id = R.string.settings_mode_always_enable),
|
||||||
|
|
||||||
SwitchItem(
|
|
||||||
icon = Icons.Filled.FolderDelete,
|
|
||||||
title = stringResource(R.string.settings_umount_modules_default),
|
|
||||||
summary = stringResource(R.string.settings_umount_modules_default_summary),
|
|
||||||
checked = umountChecked,
|
|
||||||
onCheckedChange = { enabled ->
|
|
||||||
if (Natives.setDefaultUmountModules(enabled)) {
|
|
||||||
umountChecked = enabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
var enhancedSecurityMode by rememberSaveable {
|
||||||
// SU 禁用开关
|
mutableIntStateOf(
|
||||||
var isSuDisabled by rememberSaveable {
|
prefs.getInt(
|
||||||
mutableStateOf(!Natives.isSuEnabled())
|
"enhanced_security_mode", if (Natives.isEnhancedSecurityEnabled()) 1 else 0
|
||||||
}
|
)
|
||||||
|
|
||||||
SwitchItem(
|
|
||||||
icon = Icons.Filled.RemoveModerator,
|
|
||||||
title = stringResource(R.string.settings_disable_su),
|
|
||||||
summary = stringResource(R.string.settings_disable_su_summary),
|
|
||||||
checked = isSuDisabled,
|
|
||||||
onCheckedChange = { enabled ->
|
|
||||||
val shouldEnable = !enabled
|
|
||||||
if (Natives.setSuEnabled(shouldEnable)) {
|
|
||||||
isSuDisabled = enabled
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
// 禁用内核卸载开关
|
|
||||||
if (Natives.version >= Natives.MINIMAL_NEW_IOCTL_KERNEL) {
|
|
||||||
var isKernelUmountDisabled by rememberSaveable {
|
|
||||||
mutableStateOf(!Natives.isKernelUmountEnabled())
|
|
||||||
}
|
|
||||||
|
|
||||||
SwitchItem(
|
|
||||||
icon = Icons.Rounded.FolderDelete,
|
|
||||||
title = stringResource(id = R.string.settings_disable_kernel_umount),
|
|
||||||
summary = stringResource(id = R.string.settings_disable_kernel_umount_summary),
|
|
||||||
checked = isKernelUmountDisabled,
|
|
||||||
onCheckedChange = { checked: Boolean ->
|
|
||||||
val shouldEnable = !checked
|
|
||||||
if (Natives.setKernelUmountEnabled(shouldEnable)) {
|
|
||||||
isKernelUmountDisabled = !shouldEnable
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
SettingDropdown(
|
||||||
|
icon = Icons.Rounded.EnhancedEncryption,
|
||||||
|
title = stringResource(id = R.string.settings_enable_enhanced_security),
|
||||||
|
summary = stringResource(id = R.string.settings_enable_enhanced_security_summary),
|
||||||
|
items = modeItems,
|
||||||
|
selectedIndex = enhancedSecurityMode,
|
||||||
|
onSelectedIndexChange = { index ->
|
||||||
|
when (index) {
|
||||||
|
// Default: disable and save to persist
|
||||||
|
0 -> if (Natives.setEnhancedSecurityEnabled(false)) {
|
||||||
|
execKsud("feature save", true)
|
||||||
|
prefs.edit { putInt("enhanced_security_mode", 0) }
|
||||||
|
enhancedSecurityMode = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporarily enable: save disabled state first, then enable
|
||||||
|
1 -> if (Natives.setEnhancedSecurityEnabled(false)) {
|
||||||
|
execKsud("feature save", true)
|
||||||
|
if (Natives.setEnhancedSecurityEnabled(true)) {
|
||||||
|
prefs.edit { putInt("enhanced_security_mode", 1) }
|
||||||
|
enhancedSecurityMode = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Permanently enable: enable and save
|
||||||
|
2 -> if (Natives.setEnhancedSecurityEnabled(true)) {
|
||||||
|
execKsud("feature save", true)
|
||||||
|
prefs.edit { putInt("enhanced_security_mode", 2) }
|
||||||
|
enhancedSecurityMode = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var suCompatMode by rememberSaveable {
|
||||||
|
mutableIntStateOf(
|
||||||
|
prefs.getInt(
|
||||||
|
"su_compat_mode", if (!Natives.isSuEnabled()) 1 else 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
SettingDropdown(
|
||||||
|
icon = Icons.Rounded.RemoveModerator,
|
||||||
|
title = stringResource(id = R.string.settings_disable_su),
|
||||||
|
summary = stringResource(id = R.string.settings_disable_su_summary),
|
||||||
|
items = modeItems,
|
||||||
|
selectedIndex = suCompatMode,
|
||||||
|
onSelectedIndexChange = { index ->
|
||||||
|
when (index) {
|
||||||
|
// Default: enable and save to persist
|
||||||
|
0 -> if (Natives.setSuEnabled(true)) {
|
||||||
|
execKsud("feature save", true)
|
||||||
|
prefs.edit { putInt("su_compat_mode", 0) }
|
||||||
|
suCompatMode = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporarily disable: save enabled state first, then disable
|
||||||
|
1 -> if (Natives.setSuEnabled(true)) {
|
||||||
|
execKsud("feature save", true)
|
||||||
|
if (Natives.setSuEnabled(false)) {
|
||||||
|
prefs.edit { putInt("su_compat_mode", 1) }
|
||||||
|
suCompatMode = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Permanently disable: disable and save
|
||||||
|
2 -> if (Natives.setSuEnabled(false)) {
|
||||||
|
execKsud("feature save", true)
|
||||||
|
prefs.edit { putInt("su_compat_mode", 2) }
|
||||||
|
suCompatMode = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var kernelUmountMode by rememberSaveable {
|
||||||
|
mutableIntStateOf(
|
||||||
|
prefs.getInt(
|
||||||
|
"kernel_umount_mode", if (!Natives.isKernelUmountEnabled()) 1 else 0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
SettingDropdown(
|
||||||
|
icon = Icons.Rounded.RemoveCircle,
|
||||||
|
title = stringResource(id = R.string.settings_disable_kernel_umount),
|
||||||
|
summary = stringResource(id = R.string.settings_disable_kernel_umount_summary),
|
||||||
|
items = modeItems,
|
||||||
|
selectedIndex = kernelUmountMode,
|
||||||
|
onSelectedIndexChange = { index ->
|
||||||
|
when (index) {
|
||||||
|
// Default: enable and save to persist
|
||||||
|
0 -> if (Natives.setKernelUmountEnabled(true)) {
|
||||||
|
execKsud("feature save", true)
|
||||||
|
prefs.edit { putInt("kernel_umount_mode", 0) }
|
||||||
|
kernelUmountMode = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporarily disable: save enabled state first, then disable
|
||||||
|
1 -> if (Natives.setKernelUmountEnabled(true)) {
|
||||||
|
execKsud("feature save", true)
|
||||||
|
if (Natives.setKernelUmountEnabled(false)) {
|
||||||
|
prefs.edit { putInt("kernel_umount_mode", 1) }
|
||||||
|
kernelUmountMode = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Permanently disable: disable and save
|
||||||
|
2 -> if (Natives.setKernelUmountEnabled(false)) {
|
||||||
|
execKsud("feature save", true)
|
||||||
|
prefs.edit { putInt("kernel_umount_mode", 2) }
|
||||||
|
kernelUmountMode = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 卸载模块开关
|
||||||
|
var umountChecked by rememberSaveable { mutableStateOf(Natives.isDefaultUmountModules()) }
|
||||||
|
SwitchItem(
|
||||||
|
icon = Icons.Rounded.FolderDelete,
|
||||||
|
title = stringResource(id = R.string.settings_umount_modules_default),
|
||||||
|
summary = stringResource(id = R.string.settings_umount_modules_default_summary),
|
||||||
|
checked = umountChecked,
|
||||||
|
onCheckedChange = {
|
||||||
|
if (Natives.setDefaultUmountModules(it)) {
|
||||||
|
umountChecked = it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
// 强制签名验证开关
|
// 强制签名验证开关
|
||||||
@@ -967,3 +1060,103 @@ private fun UidScannerSection(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
fun SettingDropdown(
|
||||||
|
icon: ImageVector,
|
||||||
|
title: String,
|
||||||
|
summary: String,
|
||||||
|
items: List<String>,
|
||||||
|
selectedIndex: Int,
|
||||||
|
leftAction: (@Composable () -> Unit)? = null,
|
||||||
|
onSelectedIndexChange: (Int) -> Unit
|
||||||
|
) {
|
||||||
|
var showDialog by remember { mutableStateOf(false) }
|
||||||
|
val selectedItemText = items.getOrNull(selectedIndex) ?: ""
|
||||||
|
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable { showDialog = true }
|
||||||
|
.padding(horizontal = SPACING_LARGE, vertical = 12.dp),
|
||||||
|
verticalAlignment = Alignment.Top
|
||||||
|
) {
|
||||||
|
if (leftAction != null) {
|
||||||
|
leftAction()
|
||||||
|
} else {
|
||||||
|
Icon(
|
||||||
|
imageVector = icon,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(end = SPACING_LARGE)
|
||||||
|
.size(24.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
style = MaterialTheme.typography.titleMedium
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(SPACING_SMALL))
|
||||||
|
Text(
|
||||||
|
text = summary,
|
||||||
|
style = MaterialTheme.typography.bodyMedium
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(SPACING_SMALL))
|
||||||
|
Text(
|
||||||
|
text = selectedItemText,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.ArrowForward,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
modifier = Modifier.size(24.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (showDialog) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { showDialog = false },
|
||||||
|
title = { Text(title) },
|
||||||
|
text = {
|
||||||
|
LazyColumn(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
items(items) { item ->
|
||||||
|
val index = items.indexOf(item)
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable {
|
||||||
|
onSelectedIndexChange(index)
|
||||||
|
showDialog = false
|
||||||
|
}
|
||||||
|
.padding(vertical = 12.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
RadioButton(
|
||||||
|
selected = selectedIndex == index,
|
||||||
|
onClick = null
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
|
Text(text = item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(onClick = { showDialog = false }) {
|
||||||
|
Text(stringResource(android.R.string.cancel))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -165,9 +165,14 @@
|
|||||||
<string name="home_device_model">设备</string>
|
<string name="home_device_model">设备</string>
|
||||||
<string name="su_not_allowed">不允许授予 %s 超级用户权限</string>
|
<string name="su_not_allowed">不允许授予 %s 超级用户权限</string>
|
||||||
<string name="settings_disable_su">禁用 su 兼容性</string>
|
<string name="settings_disable_su">禁用 su 兼容性</string>
|
||||||
<string name="settings_disable_su_summary">临时禁止任何应用程序通过 su 命令获取 Root 权限(现有的 Root 进程不受影响)</string>
|
<string name="settings_disable_su_summary">禁止任何应用通过 su 命令获取 root 权限(已运行的 root 进程不受影响)。</string>
|
||||||
<string name="settings_disable_kernel_umount">关闭内核 umount</string>
|
<string name="settings_disable_kernel_umount">关闭内核 umount</string>
|
||||||
<string name="settings_disable_kernel_umount_summary">临时关闭 KernelSU 控制的内核级 umount 行为。</string>
|
<string name="settings_disable_kernel_umount_summary">关闭 KernelSU 控制的内核级 umount 行为。</string>
|
||||||
|
<string name="settings_enable_enhanced_security">启用增强安全</string>
|
||||||
|
<string name="settings_enable_enhanced_security_summary">启用更严格的安全策略。</string>
|
||||||
|
<string name="settings_mode_default">默认</string>
|
||||||
|
<string name="settings_mode_temp_enable">临时启用</string>
|
||||||
|
<string name="settings_mode_always_enable">始终启用</string>
|
||||||
<string name="module_install_multiple_confirm_with_names">确定要安装以下 %1$d 个模块吗?\n\n%2$s</string>
|
<string name="module_install_multiple_confirm_with_names">确定要安装以下 %1$d 个模块吗?\n\n%2$s</string>
|
||||||
<string name="more_settings">更多设置</string>
|
<string name="more_settings">更多设置</string>
|
||||||
<string name="selinux">SELinux</string>
|
<string name="selinux">SELinux</string>
|
||||||
|
|||||||
@@ -167,9 +167,14 @@
|
|||||||
<string name="home_device_model">Device model</string>
|
<string name="home_device_model">Device model</string>
|
||||||
<string name="su_not_allowed">Granting superuser to %s is not allowed</string>
|
<string name="su_not_allowed">Granting superuser to %s is not allowed</string>
|
||||||
<string name="settings_disable_su">Disable su compatibility</string>
|
<string name="settings_disable_su">Disable su compatibility</string>
|
||||||
<string name="settings_disable_su_summary">Temporarily disable any applications from obtaining root privileges via the su command (Existing root processes will not be affected)</string>
|
<string name="settings_disable_su_summary">Disable the ability of any app to gain root privileges via the su command (existing root processes won\'t be affected).</string>
|
||||||
<string name="settings_disable_kernel_umount">Disable kernel umount</string>
|
<string name="settings_disable_kernel_umount">Disable kernel umount</string>
|
||||||
<string name="settings_disable_kernel_umount_summary">Temporarily disable kernel-level umount behavior controlled by KernelSU.</string>
|
<string name="settings_disable_kernel_umount_summary">Disable kernel-level umount behavior controlled by KernelSU.</string>
|
||||||
|
<string name="settings_enable_enhanced_security">Enable enhanced security</string>
|
||||||
|
<string name="settings_enable_enhanced_security_summary">Enable stricter security policies.</string>
|
||||||
|
<string name="settings_mode_default">Default</string>
|
||||||
|
<string name="settings_mode_temp_enable">Temporarily enable</string>
|
||||||
|
<string name="settings_mode_always_enable">Permanently enable</string>
|
||||||
<string name="module_install_multiple_confirm_with_names">Sure you want to install the following %1$d modules? \n\n%2$s</string>
|
<string name="module_install_multiple_confirm_with_names">Sure you want to install the following %1$d modules? \n\n%2$s</string>
|
||||||
<string name="more_settings">More settings</string>
|
<string name="more_settings">More settings</string>
|
||||||
<string name="selinux">SELinux</string>
|
<string name="selinux">SELinux</string>
|
||||||
|
|||||||
@@ -29,7 +29,9 @@ cmaker {
|
|||||||
val androidMinSdkVersion = 26
|
val androidMinSdkVersion = 26
|
||||||
val androidTargetSdkVersion = 36
|
val androidTargetSdkVersion = 36
|
||||||
val androidCompileSdkVersion = 36
|
val androidCompileSdkVersion = 36
|
||||||
val androidCompileNdkVersion = "29.0.14206865"
|
val androidBuildToolsVersion = "36.1.0"
|
||||||
|
val androidCompileNdkVersion by extra(libs.versions.ndk.get())
|
||||||
|
val androidCmakeVersion by extra("3.22.0+")
|
||||||
val androidSourceCompatibility = JavaVersion.VERSION_21
|
val androidSourceCompatibility = JavaVersion.VERSION_21
|
||||||
val androidTargetCompatibility = JavaVersion.VERSION_21
|
val androidTargetCompatibility = JavaVersion.VERSION_21
|
||||||
val managerVersionCode by extra(4 * 10000 + getGitCommitCount() - 2815)
|
val managerVersionCode by extra(4 * 10000 + getGitCommitCount() - 2815)
|
||||||
@@ -52,6 +54,7 @@ subprojects {
|
|||||||
extensions.configure(CommonExtension::class.java) {
|
extensions.configure(CommonExtension::class.java) {
|
||||||
compileSdk = androidCompileSdkVersion
|
compileSdk = androidCompileSdkVersion
|
||||||
ndkVersion = androidCompileNdkVersion
|
ndkVersion = androidCompileNdkVersion
|
||||||
|
buildToolsVersion = androidBuildToolsVersion
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdk = androidMinSdkVersion
|
minSdk = androidMinSdkVersion
|
||||||
|
|||||||
@@ -2,15 +2,15 @@
|
|||||||
accompanist-drawablepainter = "0.37.3"
|
accompanist-drawablepainter = "0.37.3"
|
||||||
agp = "8.13.0"
|
agp = "8.13.0"
|
||||||
gson = "2.13.2"
|
gson = "2.13.2"
|
||||||
kotlin = "2.2.20"
|
kotlin = "2.2.21"
|
||||||
ksp = "2.2.20-2.0.2"
|
ksp = "2.2.21-2.0.4"
|
||||||
compose-bom = "2025.09.01"
|
compose-bom = "2025.11.00"
|
||||||
lifecycle = "2.9.4"
|
lifecycle = "2.9.4"
|
||||||
navigation = "2.9.5"
|
navigation = "2.9.6"
|
||||||
activity-compose = "1.11.0"
|
activity-compose = "1.11.0"
|
||||||
kotlinx-coroutines = "1.10.2"
|
kotlinx-coroutines = "1.10.2"
|
||||||
coil-compose = "2.7.0"
|
coil-compose = "2.7.0"
|
||||||
compose-destination = "2.2.0"
|
compose-destination = "2.3.0"
|
||||||
sheets-compose-dialogs = "1.3.0"
|
sheets-compose-dialogs = "1.3.0"
|
||||||
markdown = "4.6.2"
|
markdown = "4.6.2"
|
||||||
webkit = "1.14.0"
|
webkit = "1.14.0"
|
||||||
@@ -19,11 +19,13 @@ parcelablelist = "2.0.1"
|
|||||||
libsu = "6.0.0"
|
libsu = "6.0.0"
|
||||||
apksign = "1.4"
|
apksign = "1.4"
|
||||||
cmaker = "1.2"
|
cmaker = "1.2"
|
||||||
compose-material = "1.9.2"
|
compose-material = "1.9.4"
|
||||||
compose-material3 = "1.4.0"
|
compose-material3 = "1.4.0"
|
||||||
compose-ui = "1.9.2"
|
compose-ui = "1.9.4"
|
||||||
documentfile = "1.1.0"
|
documentfile = "1.1.0"
|
||||||
mmrl = "2bb00b3c2b"
|
mmrl = "2bb00b3c2b"
|
||||||
|
ndk = "29.0.13599879-beta2"
|
||||||
|
foundation = "1.9.4"
|
||||||
|
|
||||||
[plugins]
|
[plugins]
|
||||||
agp-app = { id = "com.android.application", version.ref = "agp" }
|
agp-app = { id = "com.android.application", version.ref = "agp" }
|
||||||
@@ -81,7 +83,7 @@ sheet-compose-dialogs-input = { group = "com.maxkeppeler.sheets-compose-dialogs"
|
|||||||
|
|
||||||
markdown = { group = "io.noties.markwon", name = "core", version.ref = "markdown" }
|
markdown = { group = "io.noties.markwon", name = "core", version.ref = "markdown" }
|
||||||
|
|
||||||
lsposed-cxx = { module = "org.lsposed.libcxx:libcxx", version = "29.0.13599879-beta2" }
|
lsposed-cxx = { module = "org.lsposed.libcxx:libcxx", version.ref = "ndk" }
|
||||||
androidx-documentfile = { group = "androidx.documentfile", name = "documentfile", version.ref = "documentfile" }
|
androidx-documentfile = { group = "androidx.documentfile", name = "documentfile", version.ref = "documentfile" }
|
||||||
|
|
||||||
|
|
||||||
@@ -89,3 +91,4 @@ mmrl-webui = { group = "com.github.MMRLApp.MMRL", name = "webui", version.ref =
|
|||||||
mmrl-platform = { group = "com.github.MMRLApp.MMRL", name = "platform", 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-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" }
|
mmrl-hidden-api = { group = "com.github.MMRLApp.MMRL", name = "hidden-api", version.ref = "mmrl" }
|
||||||
|
androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "foundation" }
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.0.0-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
4
manager/gradlew
vendored
4
manager/gradlew
vendored
@@ -114,8 +114,6 @@ case "$( uname )" in #(
|
|||||||
NONSTOP* ) nonstop=true ;;
|
NONSTOP* ) nonstop=true ;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
CLASSPATH="\\\"\\\""
|
|
||||||
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
# Determine the Java command to use to start the JVM.
|
||||||
if [ -n "$JAVA_HOME" ] ; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
@@ -172,7 +170,6 @@ fi
|
|||||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||||
if "$cygwin" || "$msys" ; then
|
if "$cygwin" || "$msys" ; then
|
||||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
|
||||||
|
|
||||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||||
|
|
||||||
@@ -212,7 +209,6 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
|||||||
|
|
||||||
set -- \
|
set -- \
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
-classpath "$CLASSPATH" \
|
|
||||||
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
|
||||||
"$@"
|
"$@"
|
||||||
|
|
||||||
|
|||||||
4
manager/gradlew.bat
vendored
4
manager/gradlew.bat
vendored
@@ -70,11 +70,9 @@ goto fail
|
|||||||
:execute
|
:execute
|
||||||
@rem Setup the command line
|
@rem Setup the command line
|
||||||
|
|
||||||
set CLASSPATH=
|
|
||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute Gradle
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
|
||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ const FEATURE_VERSION: u32 = 1;
|
|||||||
pub enum FeatureId {
|
pub enum FeatureId {
|
||||||
SuCompat = 0,
|
SuCompat = 0,
|
||||||
KernelUmount = 1,
|
KernelUmount = 1,
|
||||||
|
EnhancedSecurity = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FeatureId {
|
impl FeatureId {
|
||||||
@@ -23,6 +24,7 @@ impl FeatureId {
|
|||||||
match id {
|
match id {
|
||||||
0 => Some(FeatureId::SuCompat),
|
0 => Some(FeatureId::SuCompat),
|
||||||
1 => Some(FeatureId::KernelUmount),
|
1 => Some(FeatureId::KernelUmount),
|
||||||
|
2 => Some(FeatureId::EnhancedSecurity),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,6 +33,7 @@ impl FeatureId {
|
|||||||
match self {
|
match self {
|
||||||
FeatureId::SuCompat => "su_compat",
|
FeatureId::SuCompat => "su_compat",
|
||||||
FeatureId::KernelUmount => "kernel_umount",
|
FeatureId::KernelUmount => "kernel_umount",
|
||||||
|
FeatureId::EnhancedSecurity => "enhanced_security",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +45,9 @@ impl FeatureId {
|
|||||||
FeatureId::KernelUmount => {
|
FeatureId::KernelUmount => {
|
||||||
"Kernel Umount - controls whether kernel automatically unmounts modules when not needed"
|
"Kernel Umount - controls whether kernel automatically unmounts modules when not needed"
|
||||||
}
|
}
|
||||||
|
FeatureId::EnhancedSecurity => {
|
||||||
|
"Enhanced Security - disable non‑KSU root elevation and unauthorized UID downgrades"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -50,6 +56,7 @@ fn parse_feature_id(name: &str) -> Result<FeatureId> {
|
|||||||
match name {
|
match name {
|
||||||
"su_compat" | "0" => Ok(FeatureId::SuCompat),
|
"su_compat" | "0" => Ok(FeatureId::SuCompat),
|
||||||
"kernel_umount" | "1" => Ok(FeatureId::KernelUmount),
|
"kernel_umount" | "1" => Ok(FeatureId::KernelUmount),
|
||||||
|
"enhanced_security" | "2" => Ok(FeatureId::EnhancedSecurity),
|
||||||
_ => bail!("Unknown feature: {}", name),
|
_ => bail!("Unknown feature: {}", name),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -223,7 +230,11 @@ pub fn list_features() -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let all_features = [FeatureId::SuCompat, FeatureId::KernelUmount];
|
let all_features = [
|
||||||
|
FeatureId::SuCompat,
|
||||||
|
FeatureId::KernelUmount,
|
||||||
|
FeatureId::EnhancedSecurity,
|
||||||
|
];
|
||||||
|
|
||||||
for feature_id in all_features.iter() {
|
for feature_id in all_features.iter() {
|
||||||
let id = *feature_id as u32;
|
let id = *feature_id as u32;
|
||||||
@@ -282,7 +293,11 @@ pub fn load_config_and_apply() -> Result<()> {
|
|||||||
pub fn save_config() -> Result<()> {
|
pub fn save_config() -> Result<()> {
|
||||||
let mut features = HashMap::new();
|
let mut features = HashMap::new();
|
||||||
|
|
||||||
let all_features = [FeatureId::SuCompat, FeatureId::KernelUmount];
|
let all_features = [
|
||||||
|
FeatureId::SuCompat,
|
||||||
|
FeatureId::KernelUmount,
|
||||||
|
FeatureId::EnhancedSecurity,
|
||||||
|
];
|
||||||
|
|
||||||
for feature_id in all_features.iter() {
|
for feature_id in all_features.iter() {
|
||||||
let id = *feature_id as u32;
|
let id = *feature_id as u32;
|
||||||
|
|||||||
Reference in New Issue
Block a user