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 @@ -274,21 +274,39 @@ class GatewayService : Service() {
}

private fun stopGateway() {
val procToStop: Process?
synchronized(lock) {
stopping = true
restartCount = maxRestarts // Prevent auto-restart
uptimeThread?.interrupt()
uptimeThread = null
watchdogThread?.interrupt()
watchdogThread = null
gatewayProcess?.let {
try {
it.destroyForcibly()
} catch (_: Exception) {}
gatewayProcess = null
}
// Interrupt the gateway thread in case it is sleeping during an
// auto-restart delay so it wakes up and sees stopping=true.
gatewayThread?.interrupt()
gatewayThread = null
procToStop = gatewayProcess
gatewayProcess = null
}
emitLog("Gateway stopped by user")
// Gracefully terminate proot via SIGTERM first, allowing its --kill-on-exit
// handler to kill child processes (node.js / openclaw daemon) before proot
// exits. destroyForcibly() (SIGKILL) bypasses proot's exit handler, which
// can leave the gateway daemon alive even after proot is killed.
procToStop?.let { proc ->
Thread({
try {
proc.destroy() // SIGTERM — lets proot clean up its children
if (!proc.waitFor(3, java.util.concurrent.TimeUnit.SECONDS)) {
// proot did not exit cleanly; force-kill it.
proc.destroyForcibly()
}
} catch (_: Exception) {
try { proc.destroyForcibly() } catch (_: Exception) {}
}
}, "gateway-stop").apply { isDaemon = true }.start()
}
}

/** Watchdog: periodically checks if the proot process is alive.
Expand Down
4 changes: 2 additions & 2 deletions flutter_app/lib/screens/settings_screen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
title: const Text('Setup Storage'),
subtitle: Text(_storageGranted
? 'Granted — proot can access /sdcard. Revoke if not needed.'
: 'Allow access to shared storage'),
: 'Not granted (recommended) — tap to grant only if needed'),
leading: const Icon(Icons.sd_storage),
trailing: _storageGranted
? const Icon(Icons.warning_amber, color: AppColors.statusAmber)
: const Icon(Icons.warning, color: AppColors.statusAmber),
: const Icon(Icons.check_circle, color: AppColors.statusGreen),
onTap: () async {
await NativeBridge.requestStoragePermission();
// Refresh after returning from permission screen
Expand Down
Loading