manager: Updated the susfs binary file

- made the umount manager publicly
available

- and removed the “try umount” functionality

- susfs interface and fixed path issues (on Android 16).
This commit is contained in:
ShirkNeko
2025-11-23 23:51:40 +08:00
parent 84be28708e
commit 2c1beac1ca
19 changed files with 118 additions and 618 deletions

View File

@@ -505,18 +505,15 @@ fun SettingScreen(navigator: DestinationsNavigator) {
)
}
}
val lkmMode = Natives.isLkmMode
KsuIsValid {
if (lkmMode) {
SettingItem(
icon = Icons.Filled.FolderOff,
title = stringResource(R.string.umount_path_manager),
summary = stringResource(R.string.umount_path_manager_summary),
onClick = {
navigator.navigate(UmountManagerScreenDestination)
}
)
}
SettingItem(
icon = Icons.Filled.FolderOff,
title = stringResource(R.string.umount_path_manager),
summary = stringResource(R.string.umount_path_manager_summary),
onClick = {
navigator.navigate(UmountManagerScreenDestination)
}
)
}
if (showBottomsheet) {
@@ -560,7 +557,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
}
)
}
if (lkmMode) {
if (Natives.isLkmMode) {
UninstallItem(navigator) {
loadingDialog.withLoading(it)
}

View File

@@ -47,7 +47,6 @@ enum class SuSFSTab(val displayNameRes: Int) {
SUS_PATHS(R.string.susfs_tab_sus_paths),
SUS_LOOP_PATHS(R.string.susfs_tab_sus_loop_paths),
SUS_MAPS(R.string.susfs_tab_sus_maps),
TRY_UMOUNT(R.string.susfs_tab_try_umount),
KSTAT_CONFIG(R.string.susfs_tab_kstat_config),
PATH_SETTINGS(R.string.susfs_tab_path_settings),
ENABLED_FEATURES(R.string.susfs_tab_enabled_features);
@@ -98,7 +97,6 @@ fun SuSFSConfigScreen(
var susPaths by remember { mutableStateOf(emptySet<String>()) }
var susLoopPaths by remember { mutableStateOf(emptySet<String>()) }
var susMaps by remember { mutableStateOf(emptySet<String>()) }
var tryUmounts by remember { mutableStateOf(emptySet<String>()) }
var androidDataPath by remember { mutableStateOf("") }
var sdcardPath by remember { mutableStateOf("") }
@@ -123,7 +121,6 @@ fun SuSFSConfigScreen(
var showAddLoopPathDialog by remember { mutableStateOf(false) }
var showAddSusMapDialog by remember { mutableStateOf(false) }
var showAddAppPathDialog by remember { mutableStateOf(false) }
var showAddUmountDialog by remember { mutableStateOf(false) }
var showAddKstatStaticallyDialog by remember { mutableStateOf(false) }
var showAddKstatDialog by remember { mutableStateOf(false) }
@@ -131,7 +128,6 @@ fun SuSFSConfigScreen(
var editingPath by remember { mutableStateOf<String?>(null) }
var editingLoopPath by remember { mutableStateOf<String?>(null) }
var editingSusMap by remember { mutableStateOf<String?>(null) }
var editingUmount by remember { mutableStateOf<String?>(null) }
var editingKstatConfig by remember { mutableStateOf<String?>(null) }
var editingKstatPath by remember { mutableStateOf<String?>(null) }
@@ -139,7 +135,6 @@ fun SuSFSConfigScreen(
var showResetPathsDialog by remember { mutableStateOf(false) }
var showResetLoopPathsDialog by remember { mutableStateOf(false) }
var showResetSusMapsDialog by remember { mutableStateOf(false) }
var showResetUmountsDialog by remember { mutableStateOf(false) }
var showResetKstatDialog by remember { mutableStateOf(false) }
// 备份还原相关状态
@@ -299,7 +294,6 @@ fun SuSFSConfigScreen(
susPaths = SuSFSManager.getSusPaths(context)
susLoopPaths = SuSFSManager.getSusLoopPaths(context)
susMaps = SuSFSManager.getSusMaps(context)
tryUmounts = SuSFSManager.getTryUmounts(context)
androidDataPath = SuSFSManager.getAndroidDataPath(context)
sdcardPath = SuSFSManager.getSdcardPath(context)
kstatConfigs = SuSFSManager.getKstatConfigs(context)
@@ -471,7 +465,6 @@ fun SuSFSConfigScreen(
susPaths = SuSFSManager.getSusPaths(context)
susLoopPaths = SuSFSManager.getSusLoopPaths(context)
susMaps = SuSFSManager.getSusMaps(context)
tryUmounts = SuSFSManager.getTryUmounts(context)
androidDataPath = SuSFSManager.getAndroidDataPath(context)
sdcardPath = SuSFSManager.getSdcardPath(context)
kstatConfigs = SuSFSManager.getKstatConfigs(context)
@@ -642,33 +635,6 @@ fun SuSFSConfigScreen(
existingSusPaths = susPaths
)
AddTryUmountDialog(
showDialog = showAddUmountDialog,
onDismiss = {
showAddUmountDialog = false
editingUmount = null
},
onConfirm = { path, mode ->
coroutineScope.launch {
isLoading = true
val success = if (editingUmount != null) {
SuSFSManager.editTryUmount(context, editingUmount!!, path, mode)
} else {
SuSFSManager.addTryUmount(context, path, mode)
}
if (success) {
tryUmounts = SuSFSManager.getTryUmounts(context)
}
isLoading = false
showAddUmountDialog = false
editingUmount = null
}
},
isLoading = isLoading,
initialPath = editingUmount?.split("|")?.get(0) ?: "",
initialMode = editingUmount?.split("|")?.get(1)?.toIntOrNull() ?: 0
)
AddKstatStaticallyDialog(
showDialog = showAddKstatStaticallyDialog,
onDismiss = {
@@ -829,27 +795,6 @@ fun SuSFSConfigScreen(
isDestructive = true
)
ConfirmDialog(
showDialog = showResetUmountsDialog,
onDismiss = { showResetUmountsDialog = false },
onConfirm = {
coroutineScope.launch {
isLoading = true
SuSFSManager.saveTryUmounts(context, emptySet())
tryUmounts = emptySet()
if (SuSFSManager.isAutoStartEnabled(context)) {
SuSFSManager.configureAutoStart(context, true)
}
isLoading = false
showResetUmountsDialog = false
}
},
titleRes = R.string.susfs_reset_umounts_title,
messageRes = R.string.susfs_reset_umounts_message,
isLoading = isLoading,
isDestructive = true
)
ConfirmDialog(
showDialog = showResetKstatDialog,
onDismiss = { showResetKstatDialog = false },
@@ -1049,28 +994,6 @@ fun SuSFSConfigScreen(
}
}
SuSFSTab.TRY_UMOUNT -> {
OutlinedButton(
onClick = { showResetUmountsDialog = true },
enabled = !isLoading && tryUmounts.isNotEmpty(),
shape = RoundedCornerShape(8.dp),
modifier = Modifier
.fillMaxWidth()
.height(40.dp)
) {
Icon(
imageVector = Icons.Default.RestoreFromTrash,
contentDescription = null,
modifier = Modifier.size(16.dp)
)
Spacer(modifier = Modifier.width(6.dp))
Text(
stringResource(R.string.susfs_reset_umounts_title),
fontWeight = FontWeight.Medium
)
}
}
SuSFSTab.KSTAT_CONFIG -> {
OutlinedButton(
onClick = { showResetKstatDialog = true },
@@ -1260,6 +1183,18 @@ fun SuSFSConfigScreen(
}
isLoading = false
}
},
umountForZygoteIsoService = umountForZygoteIsoService,
onUmountForZygoteIsoServiceChange = { enabled ->
coroutineScope.launch {
isLoading = true
val success =
SuSFSManager.setUmountForZygoteIsoService(context, enabled)
if (success) {
umountForZygoteIsoService = enabled
}
isLoading = false
}
}
)
}
@@ -1326,39 +1261,6 @@ fun SuSFSConfigScreen(
)
}
SuSFSTab.TRY_UMOUNT -> {
TryUmountContent(
tryUmounts = tryUmounts,
umountForZygoteIsoService = umountForZygoteIsoService,
isLoading = isLoading,
onAddUmount = { showAddUmountDialog = true },
onRemoveUmount = { umountEntry ->
coroutineScope.launch {
isLoading = true
if (SuSFSManager.removeTryUmount(context, umountEntry)) {
tryUmounts = SuSFSManager.getTryUmounts(context)
}
isLoading = false
}
},
onEditUmount = { umountEntry ->
editingUmount = umountEntry
showAddUmountDialog = true
},
onToggleUmountForZygoteIsoService = { enabled ->
coroutineScope.launch {
isLoading = true
val success =
SuSFSManager.setUmountForZygoteIsoService(context, enabled)
if (success) {
umountForZygoteIsoService = enabled
}
isLoading = false
}
}
)
}
SuSFSTab.KSTAT_CONFIG -> {
KstatConfigContent(
kstatConfigs = kstatConfigs,
@@ -1470,7 +1372,9 @@ private fun BasicSettingsContent(
enableAvcLogSpoofing: Boolean,
onEnableAvcLogSpoofingChange: (Boolean) -> Unit,
hideSusMountsForAllProcs: Boolean,
onHideSusMountsForAllProcsChange: (Boolean) -> Unit
onHideSusMountsForAllProcsChange: (Boolean) -> Unit,
umountForZygoteIsoService: Boolean,
onUmountForZygoteIsoServiceChange: (Boolean) -> Unit
) {
var scriptLocationExpanded by remember { mutableStateOf(false) }
val isAbDevice = produceState(initialValue = false) {
@@ -1856,6 +1760,59 @@ private fun BasicSettingsContent(
)
}
// 卸载 Zygote 隔离服务开关仅在1.5.8+版本显示)
if (isSusVersion158) {
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface
),
shape = RoundedCornerShape(12.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier.weight(1f)
) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = Icons.Default.Security,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(18.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = stringResource(R.string.umount_zygote_iso_service),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.onSurface
)
}
Spacer(modifier = Modifier.height(6.dp))
Text(
text = stringResource(R.string.umount_zygote_iso_service_description),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
lineHeight = 14.sp
)
}
Switch(
checked = umountForZygoteIsoService,
onCheckedChange = onUmountForZygoteIsoServiceChange,
enabled = !isLoading
)
}
}
}
// 槽位信息按钮
if (isAbDevice) {
Card(

View File

@@ -401,126 +401,6 @@ fun AppIcon(
}
/**
* 添加尝试卸载对话框
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AddTryUmountDialog(
showDialog: Boolean,
onDismiss: () -> Unit,
onConfirm: (String, Int) -> Unit,
isLoading: Boolean,
initialPath: String = "",
initialMode: Int = 0
) {
var newUmountPath by remember { mutableStateOf("") }
var newUmountMode by remember { mutableIntStateOf(0) }
var umountModeExpanded by remember { mutableStateOf(false) }
// 当对话框显示时,设置初始值
LaunchedEffect(showDialog, initialPath, initialMode) {
if (showDialog) {
newUmountPath = initialPath
newUmountMode = initialMode
}
}
if (showDialog) {
AlertDialog(
onDismissRequest = onDismiss,
title = {
Text(
stringResource(if (initialPath.isNotEmpty()) R.string.susfs_edit_try_umount else R.string.susfs_add_try_umount),
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold
)
},
text = {
Column(
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
OutlinedTextField(
value = newUmountPath,
onValueChange = { newUmountPath = it },
label = { Text(stringResource(R.string.susfs_path_label)) },
placeholder = { Text(stringResource(R.string.susfs_path_placeholder)) },
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(8.dp)
)
ExposedDropdownMenuBox(
expanded = umountModeExpanded,
onExpandedChange = { umountModeExpanded = !umountModeExpanded }
) {
OutlinedTextField(
value = if (newUmountMode == 0)
stringResource(R.string.susfs_umount_mode_normal)
else
stringResource(R.string.susfs_umount_mode_detach),
onValueChange = { },
readOnly = true,
label = { Text(stringResource(R.string.susfs_umount_mode_label)) },
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = umountModeExpanded) },
modifier = Modifier
.fillMaxWidth()
.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryEditable, true),
shape = RoundedCornerShape(8.dp)
)
ExposedDropdownMenu(
expanded = umountModeExpanded,
onDismissRequest = { umountModeExpanded = false }
) {
DropdownMenuItem(
text = { Text(stringResource(R.string.susfs_umount_mode_normal)) },
onClick = {
newUmountMode = 0
umountModeExpanded = false
}
)
DropdownMenuItem(
text = { Text(stringResource(R.string.susfs_umount_mode_detach)) },
onClick = {
newUmountMode = 1
umountModeExpanded = false
}
)
}
}
}
},
confirmButton = {
Button(
onClick = {
if (newUmountPath.isNotBlank()) {
onConfirm(newUmountPath.trim(), newUmountMode)
newUmountPath = ""
newUmountMode = 0
}
},
enabled = newUmountPath.isNotBlank() && !isLoading,
shape = RoundedCornerShape(8.dp)
) {
Text(stringResource(if (initialPath.isNotEmpty()) R.string.susfs_save else R.string.add))
}
},
dismissButton = {
TextButton(
onClick = {
onDismiss()
newUmountPath = ""
newUmountMode = 0
},
shape = RoundedCornerShape(8.dp)
) {
Text(stringResource(R.string.cancel))
}
},
shape = RoundedCornerShape(12.dp)
)
}
}
/**
* 添加Kstat静态配置对话框
*/

View File

@@ -52,7 +52,7 @@ fun SusPathsContent(
val (appPathGroups, otherPaths) = remember(susPaths) {
val appPathRegex = Regex(".*/Android/data/([^/]+)/?.*")
val uidPathRegex = Regex("/sys/fs/cgroup/uid_([0-9]+)")
val uidPathRegex = Regex("/sys/fs/cgroup(?:/[^/]+)*/uid_([0-9]+)")
val appPathMap = mutableMapOf<String, MutableList<String>>()
val uidToPackageMap = mutableMapOf<String, String>()
val others = mutableListOf<String>()
@@ -420,134 +420,6 @@ fun SusMapsContent(
}
}
/**
* 尝试卸载内容组件
*/
@Composable
fun TryUmountContent(
tryUmounts: Set<String>,
umountForZygoteIsoService: Boolean,
isLoading: Boolean,
onAddUmount: () -> Unit,
onRemoveUmount: (String) -> Unit,
onEditUmount: ((String) -> Unit)? = null,
onToggleUmountForZygoteIsoService: (Boolean) -> Unit
) {
Box(modifier = Modifier.fillMaxSize()) {
LazyColumn(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
if (isSusVersion158()) {
item {
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface
),
shape = RoundedCornerShape(12.dp)
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(12.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier.weight(1f)
) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = Icons.Default.Security,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(18.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = stringResource(R.string.umount_zygote_iso_service),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.onSurface
)
}
Spacer(modifier = Modifier.height(6.dp))
Text(
text = stringResource(R.string.umount_zygote_iso_service_description),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
lineHeight = 14.sp
)
}
Switch(
checked = umountForZygoteIsoService,
onCheckedChange = onToggleUmountForZygoteIsoService,
enabled = !isLoading
)
}
}
}
}
if (tryUmounts.isEmpty()) {
item {
EmptyStateCard(
message = stringResource(R.string.susfs_no_umounts_configured)
)
}
} else {
items(tryUmounts.toList()) { umountEntry ->
val parts = umountEntry.split("|")
val path = if (parts.isNotEmpty()) parts[0] else umountEntry
val mode = if (parts.size > 1) parts[1] else "0"
val modeText = if (mode == "0")
stringResource(R.string.susfs_umount_mode_normal_short)
else
stringResource(R.string.susfs_umount_mode_detach_short)
PathItemCard(
path = path,
icon = Icons.Default.Storage,
additionalInfo = stringResource(R.string.susfs_umount_mode_display, modeText, mode),
onDelete = { onRemoveUmount(umountEntry) },
onEdit = if (onEditUmount != null) { { onEditUmount(umountEntry) } } else null,
isLoading = isLoading
)
}
}
item {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Button(
onClick = onAddUmount,
modifier = Modifier
.weight(1f)
.height(48.dp),
shape = RoundedCornerShape(8.dp)
) {
Icon(
imageVector = Icons.Default.Add,
contentDescription = null,
modifier = Modifier.size(24.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text(text = stringResource(R.string.add))
}
}
}
}
}
}
/**
* Kstat配置内容组件
*/

View File

@@ -23,6 +23,7 @@ import com.sukisu.ultra.ui.util.getRootShell
import com.sukisu.ultra.ui.util.getSuSFSVersion
import com.sukisu.ultra.ui.util.getSuSFSFeatures
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
import com.topjohnwu.superuser.io.SuFile
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
@@ -44,7 +45,6 @@ object SuSFSManager {
private const val KEY_SUS_LOOP_PATHS = "sus_loop_paths"
private const val KEY_SUS_MAPS = "sus_maps"
private const val KEY_TRY_UMOUNTS = "try_umounts"
private const val KEY_ANDROID_DATA_PATH = "android_data_path"
private const val KEY_SDCARD_PATH = "sdcard_path"
private const val KEY_ENABLE_LOG = "enable_log"
@@ -70,7 +70,7 @@ object SuSFSManager {
const val MAX_SUSFS_VERSION = "2.0.0"
private const val BACKUP_FILE_EXTENSION = ".susfs_backup"
private const val MEDIA_DATA_PATH = "/data/media/0/Android/data"
private const val CGROUP_UID_PATH_PREFIX = "/sys/fs/cgroup/uid_"
private const val CGROUP_BASE_PATH = "/sys/fs/cgroup"
data class SlotInfo(val slotName: String, val uname: String, val buildTime: String)
data class CommandResult(val isSuccess: Boolean, val output: String, val errorOutput: String = "")
@@ -156,7 +156,6 @@ object SuSFSManager {
val susPaths: Set<String>,
val susLoopPaths: Set<String>,
val susMaps: Set<String>,
val tryUmounts: Set<String>,
val androidDataPath: String,
val sdcardPath: String,
val enableLog: Boolean,
@@ -178,7 +177,6 @@ object SuSFSManager {
susPaths.isNotEmpty() ||
susLoopPaths.isNotEmpty() ||
susMaps.isNotEmpty() ||
tryUmounts.isNotEmpty() ||
kstatConfigs.isNotEmpty() ||
addKstatPaths.isNotEmpty()
}
@@ -268,7 +266,6 @@ object SuSFSManager {
susPaths = getSusPaths(context),
susLoopPaths = getSusLoopPaths(context),
susMaps = getSusMaps(context),
tryUmounts = getTryUmounts(context),
androidDataPath = getAndroidDataPath(context),
sdcardPath = getSdcardPath(context),
enableLog = getEnableLogState(context),
@@ -377,12 +374,6 @@ object SuSFSManager {
fun getSusMaps(context: Context): Set<String> =
getPrefs(context).getStringSet(KEY_SUS_MAPS, emptySet()) ?: emptySet()
fun saveTryUmounts(context: Context, umounts: Set<String>) =
getPrefs(context).edit { putStringSet(KEY_TRY_UMOUNTS, umounts) }
fun getTryUmounts(context: Context): Set<String> =
getPrefs(context).getStringSet(KEY_TRY_UMOUNTS, emptySet()) ?: emptySet()
fun saveKstatConfigs(context: Context, configs: Set<String>) =
getPrefs(context).edit { putStringSet(KEY_KSTAT_CONFIGS, configs) }
@@ -484,7 +475,44 @@ object SuSFSManager {
}
}
private fun buildUidPath(uid: Int): String = "$CGROUP_UID_PATH_PREFIX$uid"
private fun checkPathExists(path: String): Boolean {
return try {
val shell = try {
getRootShell()
} catch (_: Exception) {
null
}
val file = if (shell != null) {
SuFile(path).apply { setShell(shell) }
} else {
File(path)
}
file.exists() && file.isDirectory
} catch (_: Exception) {
false
}
}
private fun buildUidPath(uid: Int): String {
val possiblePaths = listOf(
"$CGROUP_BASE_PATH/uid_$uid",
"$CGROUP_BASE_PATH/apps/uid_$uid",
"$CGROUP_BASE_PATH/system/uid_$uid",
"$CGROUP_BASE_PATH/freezer/uid_$uid",
"$CGROUP_BASE_PATH/memory/uid_$uid",
"$CGROUP_BASE_PATH/cpuset/uid_$uid",
"$CGROUP_BASE_PATH/cpu/uid_$uid"
)
for (path in possiblePaths) {
if (checkPathExists(path)) {
return path
}
}
return possiblePaths[0]
}
// 快捷添加应用路径
@@ -537,7 +565,6 @@ object SuSFSManager {
KEY_SUS_PATHS to getSusPaths(context),
KEY_SUS_LOOP_PATHS to getSusLoopPaths(context),
KEY_SUS_MAPS to getSusMaps(context),
KEY_TRY_UMOUNTS to getTryUmounts(context),
KEY_ANDROID_DATA_PATH to getAndroidDataPath(context),
KEY_SDCARD_PATH to getSdcardPath(context),
KEY_ENABLE_LOG to getEnableLogState(context),
@@ -849,7 +876,6 @@ object SuSFSManager {
val featureMap = mapOf(
"CONFIG_KSU_SUSFS_SUS_PATH" to context.getString(R.string.sus_path_feature_label),
"CONFIG_KSU_SUSFS_TRY_UMOUNT" to context.getString(R.string.try_umount_feature_label),
"CONFIG_KSU_SUSFS_SPOOF_UNAME" to context.getString(R.string.spoof_uname_feature_label),
"CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG" to context.getString(R.string.spoof_cmdline_feature_label),
"CONFIG_KSU_SUSFS_OPEN_REDIRECT" to context.getString(R.string.open_redirect_feature_label),
@@ -877,7 +903,6 @@ object SuSFSManager {
private fun getDefaultDisabledFeatures(context: Context): List<EnabledFeature> {
val defaultFeatures = listOf(
"sus_path_feature_label" to context.getString(R.string.sus_path_feature_label),
"try_umount_feature_label" to context.getString(R.string.try_umount_feature_label),
"spoof_uname_feature_label" to context.getString(R.string.spoof_uname_feature_label),
"spoof_cmdline_feature_label" to context.getString(R.string.spoof_cmdline_feature_label),
"open_redirect_feature_label" to context.getString(R.string.open_redirect_feature_label),
@@ -1167,59 +1192,6 @@ object SuSFSManager {
}
}
// 添加尝试卸载
suspend fun addTryUmount(context: Context, path: String, mode: Int): Boolean {
val commandSuccess = executeSusfsCommand(context, "add_try_umount '$path' $mode")
saveTryUmounts(context, getTryUmounts(context) + "$path|$mode")
if (isAutoStartEnabled(context)) updateMagiskModule(context)
showToast(context, if (commandSuccess) {
context.getString(R.string.susfs_try_umount_added_success, path)
} else {
context.getString(R.string.susfs_try_umount_added_saved, path)
})
return true
}
suspend fun removeTryUmount(context: Context, umountEntry: String): Boolean {
saveTryUmounts(context, getTryUmounts(context) - umountEntry)
if (isAutoStartEnabled(context)) updateMagiskModule(context)
val path = umountEntry.split("|").firstOrNull() ?: umountEntry
showToast(context, "Removed Try to uninstall: $path")
return true
}
// 编辑尝试卸载
suspend fun editTryUmount(context: Context, oldEntry: String, newPath: String, newMode: Int): Boolean {
return try {
val currentUmounts = getTryUmounts(context).toMutableSet()
if (!currentUmounts.remove(oldEntry)) {
showToast(context, "Original umount entry not found: $oldEntry")
return false
}
saveTryUmounts(context, currentUmounts)
val success = addTryUmount(context, newPath, newMode)
if (success) {
showToast(context, "Try umount updated: $oldEntry -> $newPath|$newMode")
return true
} else {
// 如果添加新条目失败,恢复旧条目
currentUmounts.add(oldEntry)
saveTryUmounts(context, currentUmounts)
if (isAutoStartEnabled(context)) updateMagiskModule(context)
showToast(context, "Failed to update umount entry, reverted to original")
return false
}
} catch (e: Exception) {
e.printStackTrace()
showToast(context, "Error updating try umount: ${e.message}")
false
}
}
// Zygote隔离服务卸载控制
suspend fun setUmountForZygoteIsoService(context: Context, enabled: Boolean): Boolean {
if (!isSusVersion158()) {

View File

@@ -429,21 +429,6 @@ object ScriptGenerator {
appendLine(generateBinaryCheck(config.targetPath))
appendLine()
// 添加尝试卸载
if (config.tryUmounts.isNotEmpty()) {
appendLine("# 添加尝试卸载")
config.tryUmounts.forEach { umount ->
val parts = umount.split("|")
if (parts.size == 2) {
val path = parts[0]
val mode = parts[1]
appendLine($$"\"$SUSFS_BIN\" add_try_umount '$$path' $$mode")
appendLine($$"echo \"$(get_current_time): 添加尝试卸载: $$path (模式: $$mode)\" >> \"$LOG_FILE\"")
}
}
appendLine()
}
appendLine($$"echo \"$(get_current_time): Post-Mount脚本执行完成\" >> \"$LOG_FILE\"")
}
}