diff --git a/src/main/java/com/ai/assistance/operit/terminal/provider/type/LocalTerminalProvider.kt b/src/main/java/com/ai/assistance/operit/terminal/provider/type/LocalTerminalProvider.kt index a38dd4f..72766bd 100644 --- a/src/main/java/com/ai/assistance/operit/terminal/provider/type/LocalTerminalProvider.kt +++ b/src/main/java/com/ai/assistance/operit/terminal/provider/type/LocalTerminalProvider.kt @@ -566,6 +566,16 @@ class LocalTerminalProvider( env["PROOT_TMP_DIR"] = File(filesDir, "tmp").absolutePath env["TERM"] = "xterm-256color" env["LANG"] = "en_US.UTF-8" + + // HarmonyOS NEXT proot compatibility: on some HarmonyOS devices proot's + // seccomp filter breaks chdir/getcwd/shebang/static execve with ENOSYS + // (see https://github.com/AAswordman/Operit/issues/1128). Injecting + // PROOT_NO_SECCOMP=1 makes proot skip installing the seccomp filter. + // Opt-in via settings, default off. + val settingsPrefs = context.getSharedPreferences("terminal_settings", Context.MODE_PRIVATE) + if (settingsPrefs.getBoolean("proot_no_seccomp_compat", false)) { + env["PROOT_NO_SECCOMP"] = "1" + } return env } } diff --git a/src/main/java/com/ai/assistance/operit/terminal/ui/SettingsScreen.kt b/src/main/java/com/ai/assistance/operit/terminal/ui/SettingsScreen.kt index ff8b6c2..923bc01 100644 --- a/src/main/java/com/ai/assistance/operit/terminal/ui/SettingsScreen.kt +++ b/src/main/java/com/ai/assistance/operit/terminal/ui/SettingsScreen.kt @@ -100,6 +100,7 @@ fun SettingsScreen( val sharedTmpEnabled by viewModel.sharedTmpEnabled.collectAsState() val chrootEnabled by viewModel.chrootEnabled.collectAsState() + val prootNoSeccompCompat by viewModel.prootNoSeccompCompat.collectAsState() val chrootMountStatus by viewModel.chrootMountStatus.collectAsState() val chrootMountDetails by viewModel.chrootMountDetails.collectAsState() val isInspectingChrootMounts by viewModel.isInspectingChrootMounts.collectAsState() @@ -634,6 +635,59 @@ fun SettingsScreen( } } + // HarmonyOS proot compat (PROOT_NO_SECCOMP) setting card + Card( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + colors = CardDefaults.cardColors(containerColor = SettingsTheme.surfaceColor) + ) { + Column(modifier = Modifier.padding(16.dp)) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Column(modifier = Modifier.weight(1f)) { + Text( + text = context.getString(com.ai.assistance.operit.terminal.R.string.proot_no_seccomp_compat_title), + fontSize = 18.sp, + fontWeight = FontWeight.Bold, + color = SettingsTheme.onSurfaceColor + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = if (prootNoSeccompCompat) { + context.getString(com.ai.assistance.operit.terminal.R.string.proot_no_seccomp_compat_enabled_desc) + } else { + context.getString(com.ai.assistance.operit.terminal.R.string.proot_no_seccomp_compat_disabled_desc) + }, + fontSize = 14.sp, + color = SettingsTheme.onSurfaceVariant + ) + } + Switch( + checked = prootNoSeccompCompat, + onCheckedChange = { enabled -> + viewModel.setProotNoSeccompCompat(enabled) + }, + colors = SwitchDefaults.colors( + checkedThumbColor = SettingsTheme.primaryColor, + checkedTrackColor = SettingsTheme.primaryColor.copy(alpha = 0.5f) + ) + ) + } + + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = context.getString(com.ai.assistance.operit.terminal.R.string.proot_no_seccomp_compat_note), + fontSize = 12.sp, + color = SettingsTheme.onSurfaceVariant.copy(alpha = 0.8f), + lineHeight = 16.sp + ) + } + } + Card( modifier = Modifier .fillMaxWidth() diff --git a/src/main/java/com/ai/assistance/operit/terminal/ui/SettingsViewModel.kt b/src/main/java/com/ai/assistance/operit/terminal/ui/SettingsViewModel.kt index 8179c07..2d0ee70 100644 --- a/src/main/java/com/ai/assistance/operit/terminal/ui/SettingsViewModel.kt +++ b/src/main/java/com/ai/assistance/operit/terminal/ui/SettingsViewModel.kt @@ -89,6 +89,10 @@ class SettingsViewModel( private val _chrootEnabled = MutableStateFlow(false) val chrootEnabled = _chrootEnabled.asStateFlow() + // HarmonyOS proot compat (PROOT_NO_SECCOMP) setting state + private val _prootNoSeccompCompat = MutableStateFlow(false) + val prootNoSeccompCompat = _prootNoSeccompCompat.asStateFlow() + private val _chrootMountStatus = MutableStateFlow( application.getString(com.ai.assistance.operit.terminal.R.string.chroot_mount_status_idle) ) @@ -115,6 +119,7 @@ class SettingsViewModel( loadSSHEnabled() loadSharedTmpSetting() loadChrootSetting() + loadProotNoSeccompCompat() loadVirtualKeyboardLayout() } @@ -507,6 +512,19 @@ class SettingsViewModel( return prefs.getBoolean("chroot_enabled", false) } + private fun loadProotNoSeccompCompat() { + _prootNoSeccompCompat.value = prefs.getBoolean("proot_no_seccomp_compat", false) + } + + fun setProotNoSeccompCompat(enabled: Boolean) { + prefs.edit().putBoolean("proot_no_seccomp_compat", enabled).apply() + _prootNoSeccompCompat.value = enabled + } + + fun isProotNoSeccompCompat(): Boolean { + return prefs.getBoolean("proot_no_seccomp_compat", false) + } + private fun loadVirtualKeyboardLayout() { _virtualKeyboardLayout.value = virtualKeyboardConfigManager.loadLayout() } diff --git a/src/main/res/values-en/strings.xml b/src/main/res/values-en/strings.xml index 0ebf848..b1f0c38 100644 --- a/src/main/res/values-en/strings.xml +++ b/src/main/res/values-en/strings.xml @@ -153,6 +153,10 @@ Enabled - Start Ubuntu via chroot (Root required) Disabled - Start Ubuntu via proot Note: chroot mode mounts /proc, /sys, /dev (including /dev/pts) into the Ubuntu environment. Root users, please use with caution. This mode requires a rooted device and root permission granted to this app, otherwise startup may fail + HarmonyOS proot compat mode + Enabled - Inject PROOT_NO_SECCOMP=1 when starting proot + Disabled - No extra environment variable + Note: Only enable this on devices where the proot seccomp filter breaks the terminal (e.g. HarmonyOS NEXT, where chdir/getcwd/shebang return ENOSYS and terminal sessions fail to start). When enabled, proot runs without installing the seccomp filter. Keep it off on other devices Inspect Mounts Unmount chroot mounts have not been inspected yet diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml index 1d95974..7c47799 100644 --- a/src/main/res/values/strings.xml +++ b/src/main/res/values/strings.xml @@ -153,6 +153,10 @@ 已启用 - 使用 chroot 启动 Ubuntu(需要 Root) 已禁用 - 使用 proot 启动 Ubuntu 注意:chroot 模式会将 /proc、/sys、/dev(含 /dev/pts)挂载到 Ubuntu 环境中,请各位 Root 用户谨慎对待。并且该模式需要设备已 Root 且授予本应用 Root 权限,否则可能无法启动 + 鸿蒙 proot 兼容模式 + 已启用 - 启动 proot 时注入 PROOT_NO_SECCOMP=1 环境变量 + 已禁用 - 不注入额外环境变量 + 注意:仅适用于存在 proot seccomp 兼容问题的设备(如 HarmonyOS NEXT,表现为 chdir/getcwd/shebang 执行返回 ENOSYS、终端会话无法启动)。开启后 proot 将不再安装 seccomp 过滤器,可恢复正常。其他设备请保持关闭 检测挂载 解挂载 尚未检测 chroot 挂载