Do the clean-up in the worker thread
The clean-up is done in the worker thread even when the service gets an
unexpected stop.
In addition a few more refactoring was done.
* the field virtualMachine is removed as it's accessible via the runner
field.
* startForeground is called outside of the worker thread which can block
for a long time, and eventually causes the foreground service timeout
exeception.
Bug: 401835074
Test: run, rerun, resize, close from notification
Change-Id: I89c9416f5942227b22c0207bd2eba812087248d1
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/Runner.kt b/android/TerminalApp/java/com/android/virtualization/terminal/Runner.kt
index 6454cbd..642cb26 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/Runner.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/Runner.kt
@@ -27,7 +27,7 @@
import java.util.concurrent.ForkJoinPool
/** Utility class for creating a VM and waiting for it to finish. */
-internal class Runner private constructor(val vm: VirtualMachine?, callback: Callback) {
+internal class Runner private constructor(val vm: VirtualMachine, callback: Callback) {
/** Get future about VM's exit status. */
val exitStatus = callback.finishedSuccessfully
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
index 9020458..067d540 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/VmLauncherService.kt
@@ -124,6 +124,10 @@
mainWorkerThread.submit({
doStart(notification, displayInfo, diskSize, resultReceiver)
})
+
+ // Do this outside of the main worker thread, so that we don't cause
+ // ForegroundServiceDidNotStartInTimeException
+ startForeground(this.hashCode(), notification)
}
ACTION_SHUTDOWN_VM -> mainWorkerThread.submit({ doShutdown(resultReceiver) })
else -> {
@@ -142,11 +146,6 @@
diskSize: Long,
resultReceiver: ResultReceiver,
) {
- if (virtualMachine != null) {
- Log.d(TAG, "VM instance is already started")
- return
- }
-
val image = InstalledImage.getDefault(this)
val json = ConfigJson.from(this, image.configPath)
val configBuilder = json.toConfigBuilder(this)
@@ -168,8 +167,8 @@
throw RuntimeException("cannot create runner", e)
}
- virtualMachine = runner!!.vm
- val mbc = MemBalloonController(this, virtualMachine!!)
+ val virtualMachine = runner!!.vm
+ val mbc = MemBalloonController(this, virtualMachine)
mbc.start()
runner!!.exitStatus.thenAcceptAsync { success: Boolean ->
@@ -177,10 +176,8 @@
resultReceiver.send(if (success) RESULT_STOP else RESULT_ERROR, null)
stopSelf()
}
- val logDir = getFileStreamPath(virtualMachine!!.name + ".log").toPath()
- Logger.setup(virtualMachine!!, logDir, bgThreads)
-
- startForeground(this.hashCode(), notification)
+ val logDir = getFileStreamPath(virtualMachine.name + ".log").toPath()
+ Logger.setup(virtualMachine, logDir, bgThreads)
resultReceiver.send(RESULT_START, null)
@@ -393,18 +390,21 @@
}
@WorkerThread
- private fun doShutdown(resultReceiver: ResultReceiver) {
+ private fun doShutdown(resultReceiver: ResultReceiver?) {
+ stopForeground(STOP_FOREGROUND_REMOVE)
if (debianService != null && debianService!!.shutdownDebian()) {
// During shutdown, change the notification content to indicate that it's closing
val notification = createNotificationForTerminalClose()
getSystemService<NotificationManager?>(NotificationManager::class.java)
.notify(this.hashCode(), notification)
runner?.exitStatus?.thenAcceptAsync { success: Boolean ->
- resultReceiver.send(if (success) RESULT_STOP else RESULT_ERROR, null)
+ resultReceiver?.send(if (success) RESULT_STOP else RESULT_ERROR, null)
stopSelf()
}
+ runner = null
} else {
// If there is no Debian service or it fails to shutdown, just stop the service.
+ runner?.vm?.stop()
stopSelf()
}
}
@@ -416,22 +416,16 @@
}
override fun onDestroy() {
+ mainWorkerThread.submit({
+ if (runner?.vm?.getStatus() == VirtualMachine.STATUS_RUNNING) {
+ doShutdown(null)
+ }
+ })
portNotifier?.stop()
getSystemService<NotificationManager?>(NotificationManager::class.java).cancelAll()
stopDebianServer()
- if (virtualMachine != null) {
- if (virtualMachine!!.getStatus() == VirtualMachine.STATUS_RUNNING) {
- try {
- virtualMachine!!.stop()
- stopForeground(STOP_FOREGROUND_REMOVE)
- } catch (e: VirtualMachineException) {
- Log.e(TAG, "failed to stop a VM instance", e)
- }
- }
- virtualMachine = null
- }
bgThreads.shutdownNow()
- mainWorkerThread.shutdownNow()
+ mainWorkerThread.shutdown()
super.onDestroy()
}