Skip to content

Commit e21ff32

Browse files
authored
Merge pull request #104 from mithun50/copilot/fix-stop-gateway-button-issue
Fix "Stop Gateway" button not killing gateway process, and misleading storage warning icon
2 parents a32e7ec + bd4ef7d commit e21ff32

2 files changed

Lines changed: 26 additions & 8 deletions

File tree

flutter_app/android/app/src/main/kotlin/com/nxg/openclawproot/GatewayService.kt

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -274,21 +274,39 @@ class GatewayService : Service() {
274274
}
275275

276276
private fun stopGateway() {
277+
val procToStop: Process?
277278
synchronized(lock) {
278279
stopping = true
279280
restartCount = maxRestarts // Prevent auto-restart
280281
uptimeThread?.interrupt()
281282
uptimeThread = null
282283
watchdogThread?.interrupt()
283284
watchdogThread = null
284-
gatewayProcess?.let {
285-
try {
286-
it.destroyForcibly()
287-
} catch (_: Exception) {}
288-
gatewayProcess = null
289-
}
285+
// Interrupt the gateway thread in case it is sleeping during an
286+
// auto-restart delay so it wakes up and sees stopping=true.
287+
gatewayThread?.interrupt()
288+
gatewayThread = null
289+
procToStop = gatewayProcess
290+
gatewayProcess = null
290291
}
291292
emitLog("Gateway stopped by user")
293+
// Gracefully terminate proot via SIGTERM first, allowing its --kill-on-exit
294+
// handler to kill child processes (node.js / openclaw daemon) before proot
295+
// exits. destroyForcibly() (SIGKILL) bypasses proot's exit handler, which
296+
// can leave the gateway daemon alive even after proot is killed.
297+
procToStop?.let { proc ->
298+
Thread({
299+
try {
300+
proc.destroy() // SIGTERM — lets proot clean up its children
301+
if (!proc.waitFor(3, java.util.concurrent.TimeUnit.SECONDS)) {
302+
// proot did not exit cleanly; force-kill it.
303+
proc.destroyForcibly()
304+
}
305+
} catch (_: Exception) {
306+
try { proc.destroyForcibly() } catch (_: Exception) {}
307+
}
308+
}, "gateway-stop").apply { isDaemon = true }.start()
309+
}
292310
}
293311

294312
/** Watchdog: periodically checks if the proot process is alive.

flutter_app/lib/screens/settings_screen.dart

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
120120
title: const Text('Setup Storage'),
121121
subtitle: Text(_storageGranted
122122
? 'Granted — proot can access /sdcard. Revoke if not needed.'
123-
: 'Allow access to shared storage'),
123+
: 'Not granted (recommended) — tap to grant only if needed'),
124124
leading: const Icon(Icons.sd_storage),
125125
trailing: _storageGranted
126126
? const Icon(Icons.warning_amber, color: AppColors.statusAmber)
127-
: const Icon(Icons.warning, color: AppColors.statusAmber),
127+
: const Icon(Icons.check_circle, color: AppColors.statusGreen),
128128
onTap: () async {
129129
await NativeBridge.requestStoragePermission();
130130
// Refresh after returning from permission screen

0 commit comments

Comments
 (0)