Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
)
Expand All @@ -115,6 +119,7 @@ class SettingsViewModel(
loadSSHEnabled()
loadSharedTmpSetting()
loadChrootSetting()
loadProotNoSeccompCompat()
loadVirtualKeyboardLayout()
}

Expand Down Expand Up @@ -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()
}
Expand Down
4 changes: 4 additions & 0 deletions src/main/res/values-en/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@
<string name="chroot_mode_enabled_desc">Enabled - Start Ubuntu via chroot (Root required)</string>
<string name="chroot_mode_disabled_desc">Disabled - Start Ubuntu via proot</string>
<string name="chroot_mode_note">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</string>
<string name="proot_no_seccomp_compat_title">HarmonyOS proot compat mode</string>
<string name="proot_no_seccomp_compat_enabled_desc">Enabled - Inject PROOT_NO_SECCOMP=1 when starting proot</string>
<string name="proot_no_seccomp_compat_disabled_desc">Disabled - No extra environment variable</string>
<string name="proot_no_seccomp_compat_note">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</string>
<string name="chroot_mount_check">Inspect Mounts</string>
<string name="chroot_mount_unmount">Unmount</string>
<string name="chroot_mount_status_idle">chroot mounts have not been inspected yet</string>
Expand Down
4 changes: 4 additions & 0 deletions src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,10 @@
<string name="chroot_mode_enabled_desc">已启用 - 使用 chroot 启动 Ubuntu(需要 Root)</string>
<string name="chroot_mode_disabled_desc">已禁用 - 使用 proot 启动 Ubuntu</string>
<string name="chroot_mode_note">注意:chroot 模式会将 /proc、/sys、/dev(含 /dev/pts)挂载到 Ubuntu 环境中,请各位 Root 用户谨慎对待。并且该模式需要设备已 Root 且授予本应用 Root 权限,否则可能无法启动</string>
<string name="proot_no_seccomp_compat_title">鸿蒙 proot 兼容模式</string>
<string name="proot_no_seccomp_compat_enabled_desc">已启用 - 启动 proot 时注入 PROOT_NO_SECCOMP=1 环境变量</string>
<string name="proot_no_seccomp_compat_disabled_desc">已禁用 - 不注入额外环境变量</string>
<string name="proot_no_seccomp_compat_note">注意:仅适用于存在 proot seccomp 兼容问题的设备(如 HarmonyOS NEXT,表现为 chdir/getcwd/shebang 执行返回 ENOSYS、终端会话无法启动)。开启后 proot 将不再安装 seccomp 过滤器,可恢复正常。其他设备请保持关闭</string>
<string name="chroot_mount_check">检测挂载</string>
<string name="chroot_mount_unmount">解挂载</string>
<string name="chroot_mount_status_idle">尚未检测 chroot 挂载</string>
Expand Down