manager: Added the SUS Mount Hide Control feature added in SuSFS version 1.5.8

This commit is contained in:
ShirkNeko
2025-06-21 23:39:19 +08:00
parent 285478a778
commit ddbbeafc64
6 changed files with 276 additions and 19 deletions

View File

@@ -138,6 +138,9 @@ fun SuSFSConfigScreen(
var androidDataPath by remember { mutableStateOf("") }
var sdcardPath by remember { mutableStateOf("") }
// SUS挂载隐藏控制状态
var hideSusMountsForAllProcs by remember { mutableStateOf(true) }
// Kstat配置相关状态
var kstatConfigs by remember { mutableStateOf(emptySet<String>()) }
var addKstatPaths by remember { mutableStateOf(emptySet<String>()) }
@@ -223,6 +226,7 @@ fun SuSFSConfigScreen(
sdcardPath = SuSFSManager.getSdcardPath(context)
kstatConfigs = SuSFSManager.getKstatConfigs(context)
addKstatPaths = SuSFSManager.getAddKstatPaths(context)
hideSusMountsForAllProcs = SuSFSManager.getHideSusMountsForAllProcs(context)
// 加载槽位信息
loadSlotInfo()
@@ -1533,8 +1537,13 @@ fun SuSFSConfigScreen(
)
}
SuSFSTab.SUS_MOUNTS -> {
// 检查版本支持
val isSusMountHidingSupported = remember { SuSFSManager.isSusMountHidingSupported() }
SusMountsContent(
susMounts = susMounts,
hideSusMountsForAllProcs = hideSusMountsForAllProcs,
isSusMountHidingSupported = isSusMountHidingSupported,
isLoading = isLoading,
onAddMount = { showAddMountDialog = true },
onRemoveMount = { mount ->
@@ -1545,9 +1554,19 @@ fun SuSFSConfigScreen(
}
isLoading = false
}
},
onToggleHideSusMountsForAllProcs = { hideForAll ->
coroutineScope.launch {
isLoading = true
if (SuSFSManager.setHideSusMountsForAllProcs(context, hideForAll)) {
hideSusMountsForAllProcs = hideForAll
}
isLoading = false
}
}
)
}
SuSFSTab.TRY_UMOUNT -> {
TryUmountContent(
tryUmounts = tryUmounts,

View File

@@ -22,6 +22,8 @@ import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.material.icons.filled.Storage
import androidx.compose.material.icons.filled.Update
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.Visibility
import androidx.compose.material.icons.filled.VisibilityOff
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.Card
@@ -49,6 +51,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.sukisu.ultra.R
import com.sukisu.ultra.ui.util.SuSFSManager
import kotlinx.coroutines.launch
@@ -551,15 +554,134 @@ fun SusPathsContent(
}
}
/**
* SUS挂载隐藏控制卡片组件
*/
@Composable
fun SusMountHidingControlCard(
hideSusMountsForAllProcs: Boolean,
isLoading: Boolean,
onToggleHiding: (Boolean) -> Unit
) {
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface
),
shape = RoundedCornerShape(12.dp)
) {
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
// 标题行
Row(
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = if (hideSusMountsForAllProcs) Icons.Default.VisibilityOff else Icons.Default.Visibility,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(20.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = stringResource(R.string.susfs_hide_mounts_control_title),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onSurface
)
}
// 描述文本
Text(
text = stringResource(R.string.susfs_hide_mounts_control_description),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
lineHeight = 16.sp
)
// 控制开关行
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Column(
modifier = Modifier.weight(1f)
) {
Text(
text = stringResource(R.string.susfs_hide_mounts_for_all_procs_label),
style = MaterialTheme.typography.bodyLarge,
fontWeight = FontWeight.Medium,
color = MaterialTheme.colorScheme.onSurface
)
Spacer(modifier = Modifier.width(4.dp))
Text(
text = if (hideSusMountsForAllProcs) {
stringResource(R.string.susfs_hide_mounts_for_all_procs_enabled_description)
} else {
stringResource(R.string.susfs_hide_mounts_for_all_procs_disabled_description)
},
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
lineHeight = 14.sp
)
}
Switch(
checked = hideSusMountsForAllProcs,
onCheckedChange = onToggleHiding,
enabled = !isLoading
)
}
// 当前设置显示
Text(
text = stringResource(
R.string.susfs_hide_mounts_current_setting,
if (hideSusMountsForAllProcs) {
stringResource(R.string.susfs_hide_mounts_setting_all)
} else {
stringResource(R.string.susfs_hide_mounts_setting_non_ksu)
}
),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.primary,
fontWeight = FontWeight.Medium
)
// 建议文本
Card(
modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.3f)
),
shape = RoundedCornerShape(8.dp)
) {
Text(
text = stringResource(R.string.susfs_hide_mounts_recommendation),
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
lineHeight = 14.sp,
modifier = Modifier.padding(12.dp)
)
}
}
}
}
/**
* SUS挂载内容组件
*/
@Composable
fun SusMountsContent(
susMounts: Set<String>,
hideSusMountsForAllProcs: Boolean,
isSusMountHidingSupported: Boolean,
isLoading: Boolean,
onAddMount: () -> Unit,
onRemoveMount: (String) -> Unit
onRemoveMount: (String) -> Unit,
onToggleHideSusMountsForAllProcs: (Boolean) -> Unit
) {
Column(
modifier = Modifier.fillMaxWidth(),
@@ -589,6 +711,14 @@ fun SusMountsContent(
}
)
// SUS挂载隐藏控制卡片 - 仅在支持的版本显示
if (isSusMountHidingSupported) {
SusMountHidingControlCard(
hideSusMountsForAllProcs = hideSusMountsForAllProcs,
isLoading = isLoading,
onToggleHiding = onToggleHideSusMountsForAllProcs
)
}
if (susMounts.isEmpty()) {
EmptyStateCard(
message = stringResource(R.string.susfs_no_mounts_configured)
@@ -611,6 +741,7 @@ fun SusMountsContent(
}
}
/**
* 尝试卸载内容组件
*/
@@ -801,7 +932,7 @@ fun KstatConfigContent(
}
}
// 静态Kstat配置列表
// 静态Kstat配置列表
if (kstatConfigs.isNotEmpty()) {
Text(
text = stringResource(R.string.static_kstat_config),
@@ -822,7 +953,7 @@ fun KstatConfigContent(
}
}
// Add Kstat路径列表
// Add Kstat路径列表
if (addKstatPaths.isNotEmpty()) {
Text(
text = stringResource(R.string.kstat_path_management),
@@ -845,7 +976,7 @@ fun KstatConfigContent(
}
}
// 空状态显示
// 空状态显示
if (kstatConfigs.isEmpty() && addKstatPaths.isEmpty()) {
EmptyStateCard(
message = stringResource(R.string.no_kstat_config_message)

View File

@@ -35,6 +35,7 @@ object SuSFSManager {
private const val KEY_EXECUTE_IN_POST_FS_DATA = "execute_in_post_fs_data"
private const val KEY_KSTAT_CONFIGS = "kstat_configs"
private const val KEY_ADD_KSTAT_PATHS = "add_kstat_paths"
private const val KEY_HIDE_SUS_MOUNTS_FOR_ALL_PROCS = "hide_sus_mounts_for_all_procs"
// 常量
private const val SUSFS_BINARY_BASE_NAME = "ksu_susfs"
private const val DEFAULT_UNAME = "default"
@@ -77,8 +78,37 @@ object SuSFSManager {
return CommandResult(result.isSuccess, result.out.joinToString("\n"), result.err.joinToString("\n"))
}
// 版本比较
private fun compareVersions(version1: String, version2: String): Int {
val v1Parts = version1.removePrefix("v").split(".").map { it.toIntOrNull() ?: 0 }
val v2Parts = version2.removePrefix("v").split(".").map { it.toIntOrNull() ?: 0 }
// 配置存取方法
val maxLength = maxOf(v1Parts.size, v2Parts.size)
for (i in 0 until maxLength) {
val v1Part = v1Parts.getOrNull(i) ?: 0
val v2Part = v2Parts.getOrNull(i) ?: 0
when {
v1Part > v2Part -> return 1
v1Part < v2Part -> return -1
}
}
return 0
}
// 检查当前SuSFS版本是否支持SUS挂载隐藏控制功能
fun isSusMountHidingSupported(): Boolean {
return try {
val currentVersion = getSuSFSVersion()
compareVersions(currentVersion, "1.5.8") >= 0
} catch (_: Exception) {
true
}
}
// 配置存取方法
fun saveUnameValue(context: Context, value: String) =
getPrefs(context).edit { putString(KEY_UNAME_VALUE, value) }
@@ -119,6 +149,13 @@ object SuSFSManager {
}
}
// SUS挂载隐藏控制
fun saveHideSusMountsForAllProcs(context: Context, hideForAll: Boolean) =
getPrefs(context).edit { putBoolean(KEY_HIDE_SUS_MOUNTS_FOR_ALL_PROCS, hideForAll) }
fun getHideSusMountsForAllProcs(context: Context): Boolean =
getPrefs(context).getBoolean(KEY_HIDE_SUS_MOUNTS_FOR_ALL_PROCS, true)
// 路径和配置管理
fun saveSusPaths(context: Context, paths: Set<String>) =
getPrefs(context).edit { putStringSet(KEY_SUS_PATHS, paths) }
@@ -304,7 +341,8 @@ object SuSFSManager {
"sdcardPath" to getSdcardPath(context),
"enableLog" to getEnableLogState(context),
"kstatConfigs" to getKstatConfigs(context),
"addKstatPaths" to getAddKstatPaths(context)
"addKstatPaths" to getAddKstatPaths(context),
"hideSusMountsForAllProcs" to getHideSusMountsForAllProcs(context)
)
// 生成脚本
@@ -323,7 +361,9 @@ object SuSFSManager {
"post-mount.sh" to ScriptGenerator.generatePostMountScript(
targetPath, config.getSetSafe<String>("susMounts"), config.getSetSafe<String>("tryUmounts")
),
"boot-completed.sh" to ScriptGenerator.generateBootCompletedScript(targetPath)
"boot-completed.sh" to ScriptGenerator.generateBootCompletedScript(
targetPath, config["hideSusMountsForAllProcs"] as Boolean
)
)
// 创建脚本文件
@@ -395,6 +435,25 @@ object SuSFSManager {
return success
}
// SUS挂载隐藏控制
suspend fun setHideSusMountsForAllProcs(context: Context, hideForAll: Boolean): Boolean {
if (!isSusMountHidingSupported()) {
return false
}
val success = executeSusfsCommand(context, "hide_sus_mnts_for_all_procs ${if (hideForAll) 1 else 0}")
if (success) {
saveHideSusMountsForAllProcs(context, hideForAll)
if (isAutoStartEnabled(context)) createMagiskModule(context)
showToast(context, if (hideForAll)
context.getString(R.string.susfs_hide_mounts_all_enabled)
else
context.getString(R.string.susfs_hide_mounts_all_disabled)
)
}
return success
}
// uname和构建时间
@SuppressLint("StringFormatMatches")
suspend fun setUname(context: Context, unameValue: String, buildTimeValue: String): Boolean {

View File

@@ -134,6 +134,16 @@ object ScriptGenerator {
kstatConfigs: Set<String>,
addKstatPaths: Set<String>
) {
// 添加Kstat路径
if (addKstatPaths.isNotEmpty()) {
appendLine("# 添加Kstat路径")
addKstatPaths.forEach { path ->
appendLine("\"${'$'}SUSFS_BIN\" add_sus_kstat '$path'")
appendLine("echo \"$(get_current_time): 添加Kstat路径: $path\" >> \"${'$'}LOG_FILE\"")
}
appendLine()
}
// 添加Kstat静态配置
if (kstatConfigs.isNotEmpty()) {
appendLine("# 添加Kstat静态配置")
@@ -142,19 +152,12 @@ object ScriptGenerator {
if (parts.size >= 13) {
val path = parts[0]
val params = parts.drop(1).joinToString("' '", "'", "'")
appendLine("\"${'$'}SUSFS_BIN\" add_sus_kstat_statically $params")
appendLine("\"${'$'}SUSFS_BIN\" add_sus_kstat_statically '$path' $params")
appendLine("echo \"$(get_current_time): 添加Kstat静态配置: $path\" >> \"${'$'}LOG_FILE\"")
}
}
appendLine()
}
// 添加Kstat路径
if (addKstatPaths.isNotEmpty()) {
appendLine("# 添加Kstat路径")
addKstatPaths.forEach { path ->
appendLine("\"${'$'}SUSFS_BIN\" add_sus_kstat '$path'")
appendLine("echo \"$(get_current_time): 添加Kstat路径: $path\" >> \"${'$'}LOG_FILE\"")
appendLine("\"${'$'}SUSFS_BIN\" update_sus_kstat '$path'")
appendLine("echo \"$(get_current_time): 更新Kstat配置: $path\" >> \"${'$'}LOG_FILE\"")
}
}
appendLine()
}
@@ -351,7 +354,10 @@ object ScriptGenerator {
/**
* 生成boot-completed.sh脚本内容
*/
fun generateBootCompletedScript(targetPath: String): String {
fun generateBootCompletedScript(
targetPath: String,
hideSusMountsForAllProcs: Boolean = true
): String {
return buildString {
appendLine("#!/system/bin/sh")
appendLine("# SuSFS Boot-Completed Script")
@@ -363,6 +369,24 @@ object ScriptGenerator {
appendLine()
appendLine(generateBinaryCheck(targetPath))
appendLine()
// SUS挂载隐藏控制仅限1.5.8及以上版本
appendLine("# 设置SUS挂载隐藏控制仅限1.5.8及以上版本)")
appendLine("SUSFS_VERSION=$(${'$'}SUSFS_BIN show version 2>/dev/null | grep -o 'v[0-9]\\+\\.[0-9]\\+\\.[0-9]\\+' | head -1)")
appendLine("if [ -n \"${'$'}SUSFS_VERSION\" ]; then")
appendLine(" VERSION_NUM=$(echo \"${'$'}SUSFS_VERSION\" | sed 's/v//' | awk -F. '{printf \"%d%02d%02d\", $1, $2, $3}')")
appendLine(" if [ \"${'$'}VERSION_NUM\" -ge 10508 ]; then")
val hideValue = if (hideSusMountsForAllProcs) 1 else 0
appendLine(" \"${'$'}SUSFS_BIN\" hide_sus_mnts_for_all_procs $hideValue")
appendLine(" echo \"$(get_current_time): SUS挂载隐藏控制设置为: ${if (hideSusMountsForAllProcs) "对所有进程隐藏" else "仅对非KSU进程隐藏"}\" >> \"${'$'}LOG_FILE\"")
appendLine(" else")
appendLine(" echo \"$(get_current_time): 当前版本 ${'$'}SUSFS_VERSION 不支持SUS挂载隐藏控制功能需要1.5.8及以上版本\" >> \"${'$'}LOG_FILE\"")
appendLine(" fi")
appendLine("else")
appendLine(" echo \"$(get_current_time): 无法获取SuSFS版本信息跳过SUS挂载隐藏控制设置\" >> \"${'$'}LOG_FILE\"")
appendLine("fi")
appendLine()
appendLine("echo \"$(get_current_time): Boot-Completed脚本执行完成\" >> \"${'$'}LOG_FILE\"")
}
}

View File

@@ -509,4 +509,16 @@
<string name="static_kstat_config">静态 Kstat 配置</string>
<string name="kstat_path_management">Kstat 路径管理</string>
<string name="no_kstat_config_message">暂无 Kstat 配置,点击上方按钮添加配置</string>
<!-- SuSFS 挂载隐藏控制相关字符串 -->
<string name="susfs_hide_mounts_control_title">SUS挂载隐藏控制</string>
<string name="susfs_hide_mounts_control_description">控制SUS挂载对进程的隐藏行为</string>
<string name="susfs_hide_mounts_for_all_procs_label">对所有进程隐藏SUS挂载</string>
<string name="susfs_hide_mounts_for_all_procs_enabled_description">启用后SUS挂载将对所有进程隐藏包括KSU进程</string>
<string name="susfs_hide_mounts_for_all_procs_disabled_description">禁用后SUS挂载仅对非KSU进程隐藏KSU进程可以看到挂载</string>
<string name="susfs_hide_mounts_all_enabled">已启用对所有进程隐藏SUS挂载</string>
<string name="susfs_hide_mounts_all_disabled">已禁用对所有进程隐藏SUS挂载</string>
<string name="susfs_hide_mounts_recommendation">建议在屏幕解锁后或在service.sh或boot-completed.sh阶段设置为禁用这可以修复一些依赖KSU进程挂载的root应用的问题</string>
<string name="susfs_hide_mounts_current_setting">当前设置: %s</string>
<string name="susfs_hide_mounts_setting_all">对所有进程隐藏</string>
<string name="susfs_hide_mounts_setting_non_ksu">仅对非KSU进程隐藏</string>
</resources>

View File

@@ -511,4 +511,16 @@
<string name="static_kstat_config">Static Kstat Configuration</string>
<string name="kstat_path_management">Kstat Path Management</string>
<string name="no_kstat_config_message">No Kstat configuration yet, click the button above to add</string>
<!-- SuSFS Mount Hiding Control Related Strings -->
<string name="susfs_hide_mounts_control_title">SUS Mount Hiding Control</string>
<string name="susfs_hide_mounts_control_description">Control the hiding behavior of SUS mounts for processes</string>
<string name="susfs_hide_mounts_for_all_procs_label">Hide SUS mounts for all processes</string>
<string name="susfs_hide_mounts_for_all_procs_enabled_description">When enabled, SUS mounts will be hidden from all processes, including KSU processes</string>
<string name="susfs_hide_mounts_for_all_procs_disabled_description">When disabled, SUS mounts will only be hidden from non-KSU processes, KSU processes can see the mounts</string>
<string name="susfs_hide_mounts_all_enabled">Enabled hiding SUS mounts for all processes</string>
<string name="susfs_hide_mounts_all_disabled">Disabled hiding SUS mounts for all processes</string>
<string name="susfs_hide_mounts_recommendation">It is recommended to set to disabled after screen is unlocked, or during service.sh or boot-completed.sh stage, as this should fix the issue on some rooted apps that rely on mounts mounted by KSU process</string>
<string name="susfs_hide_mounts_current_setting">Current setting: %s</string>
<string name="susfs_hide_mounts_setting_all">Hide for all processes</string>
<string name="susfs_hide_mounts_setting_non_ksu">Hide only for non-KSU processes</string>
</resources>