Add "stop vm" menu in Terminal

And stop vm in onDestroy() in Activity

Bug: 357827587
Test: click the stop vm menu
Change-Id: I53bb3fcff70488f558590e11fac5bf8949bc0396
diff --git a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
index e6e56d9..2112f89 100644
--- a/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
+++ b/android/TerminalApp/java/com/android/virtualization/terminal/MainActivity.java
@@ -20,6 +20,8 @@
 import android.os.Handler;
 import android.os.Looper;
 import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
 import android.webkit.WebChromeClient;
 import android.webkit.WebView;
 import android.webkit.WebViewClient;
@@ -53,6 +55,12 @@
                 });
     }
 
+    @Override
+    protected void onDestroy() {
+        VmLauncherServices.stopVmLauncherService(this);
+        super.onDestroy();
+    }
+
     private void gotoURL(String url) {
         runOnUiThread(() -> mWebView.loadUrl(url));
     }
@@ -79,4 +87,21 @@
         new Handler(Looper.getMainLooper())
                 .postDelayed(() -> gotoURL("http://" + mVmIpAddr + ":7681"), 2000);
     }
+
+    @Override
+    public boolean onCreateOptionsMenu(Menu menu) {
+        getMenuInflater().inflate(R.menu.main_menu, menu);
+        return true;
+    }
+
+    @Override
+    public boolean onMenuItemSelected(int featureId, MenuItem item) {
+        switch (item.getItemId()) {
+            case R.id.stop_vm:
+                VmLauncherServices.stopVmLauncherService(this);
+                return true;
+            default:
+                return super.onMenuItemSelected(featureId, item);
+        }
+    }
 }
diff --git a/android/TerminalApp/res/menu/main_menu.xml b/android/TerminalApp/res/menu/main_menu.xml
new file mode 100644
index 0000000..cc65098
--- /dev/null
+++ b/android/TerminalApp/res/menu/main_menu.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="utf-8"?>
+<menu xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:id="@+id/stop_vm"
+        android:title="Stop the existing VM instance"/>
+</menu>
\ No newline at end of file
diff --git a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/VmLauncherService.java b/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/VmLauncherService.java
index ec98f4c..fd329e2 100644
--- a/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/VmLauncherService.java
+++ b/android/VmLauncherApp/java/com/android/virtualization/vmlauncher/VmLauncherService.java
@@ -76,6 +76,11 @@
 
     @Override
     public int onStartCommand(Intent intent, int flags, int startId) {
+        if (mVirtualMachine != null
+                && mVirtualMachine.getStatus() == VirtualMachine.STATUS_RUNNING) {
+            Log.d(TAG, "there is already the running VM instance");
+            return START_NOT_STICKY;
+        }
         mExecutorService = Executors.newCachedThreadPool();
 
         ConfigJson json = ConfigJson.from(VM_CONFIG_PATH);
@@ -85,7 +90,9 @@
         try {
             runner = Runner.create(this, config);
         } catch (VirtualMachineException e) {
-            throw new RuntimeException(e);
+            Log.e(TAG, "cannot create runner", e);
+            stopSelf();
+            return START_NOT_STICKY;
         }
         mVirtualMachine = runner.getVm();
         mResultReceiver =
@@ -117,7 +124,18 @@
     @Override
     public void onDestroy() {
         super.onDestroy();
-        mExecutorService.shutdownNow();
+        if (mVirtualMachine != null
+                && mVirtualMachine.getStatus() == VirtualMachine.STATUS_RUNNING) {
+            try {
+                mVirtualMachine.stop();
+                stopForeground(STOP_FOREGROUND_REMOVE);
+            } catch (VirtualMachineException e) {
+                Log.e(TAG, "failed to stop a VM instance", e);
+            }
+            mExecutorService.shutdownNow();
+            mExecutorService = null;
+            mVirtualMachine = null;
+        }
     }
 
     // TODO(b/359523803): Use AVF API to get ip addr when it exists
diff --git a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherServices.java b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherServices.java
index c5bc5fb..565b793 100644
--- a/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherServices.java
+++ b/libs/vm_launcher_lib/java/com/android/virtualization/vmlauncher/VmLauncherServices.java
@@ -59,6 +59,11 @@
         return i;
     }
 
+    public static void stopVmLauncherService(Context context) {
+        Intent i = buildVmLauncherServiceIntent(context);
+        context.stopService(i);
+    }
+
     public static void startVmLauncherService(Context context, VmLauncherServiceCallback callback) {
         Intent i = buildVmLauncherServiceIntent(context);
         if (i == null) {