Add API for configuring a VM to run in debug mode

Bug: 185211964
Test: run the demo app
Change-Id: I960839037b2f23dbce1552199d9c9e59c36053e2
diff --git a/demo/java/com/android/microdroid/demo/MainActivity.java b/demo/java/com/android/microdroid/demo/MainActivity.java
index 976e37e..6373b55 100644
--- a/demo/java/com/android/microdroid/demo/MainActivity.java
+++ b/demo/java/com/android/microdroid/demo/MainActivity.java
@@ -22,6 +22,10 @@
 import android.system.virtualmachine.VirtualMachineConfig;
 import android.system.virtualmachine.VirtualMachineException;
 import android.system.virtualmachine.VirtualMachineManager;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.ScrollView;
 import android.widget.TextView;
 
 import androidx.appcompat.app.AppCompatActivity;
@@ -47,38 +51,77 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
+        TextView consoleView = (TextView) findViewById(R.id.consoleOutput);
+        Button runStopButton = (Button) findViewById(R.id.runStopButton);
+        ScrollView scrollView = (ScrollView) findViewById(R.id.scrollview);
 
-        // Whenthe console model is updated, append the new line to the text view.
-        TextView view = (TextView) findViewById(R.id.textview);
+        // When the console model is updated, append the new line to the text view.
         VirtualMachineModel model = new ViewModelProvider(this).get(VirtualMachineModel.class);
         model.getConsoleOutput()
                 .observeForever(
                         new Observer<String>() {
                             @Override
                             public void onChanged(String line) {
-                                view.append(line + "\n");
+                                consoleView.append(line + "\n");
+                                scrollView.fullScroll(View.FOCUS_DOWN);
                             }
                         });
+
+        // When the VM status is updated, change the label of the button
+        model.getStatus()
+                .observeForever(
+                        new Observer<VirtualMachine.Status>() {
+                            @Override
+                            public void onChanged(VirtualMachine.Status status) {
+                                if (status == VirtualMachine.Status.RUNNING) {
+                                    runStopButton.setText("Stop");
+                                } else {
+                                    runStopButton.setText("Run");
+                                    consoleView.setText("");
+                                }
+                            }
+                        });
+
+        // When the button is clicked, run or stop the VM
+        runStopButton.setOnClickListener(
+                new View.OnClickListener() {
+                    public void onClick(View v) {
+                        if (model.getStatus().getValue() == VirtualMachine.Status.RUNNING) {
+                            model.stop();
+                        } else {
+                            CheckBox debugModeCheckBox = (CheckBox) findViewById(R.id.debugMode);
+                            final boolean debug = debugModeCheckBox.isChecked();
+                            model.run(debug);
+                        }
+                    }
+                });
     }
 
     /** Models a virtual machine and console output from it. */
     public static class VirtualMachineModel extends AndroidViewModel {
-        private final VirtualMachine mVirtualMachine;
+        private VirtualMachine mVirtualMachine;
         private final MutableLiveData<String> mConsoleOutput = new MutableLiveData<>();
+        private final MutableLiveData<VirtualMachine.Status> mStatus = new MutableLiveData<>();
 
         public VirtualMachineModel(Application app) {
             super(app);
+            mStatus.setValue(VirtualMachine.Status.DELETED);
+        }
 
+        /** Runs a VM */
+        public void run(boolean debug) {
             // Create a VM and run it.
             // TODO(jiyong): remove the call to idsigPath
             try {
-                VirtualMachineConfig config =
+                VirtualMachineConfig.Builder builder =
                         new VirtualMachineConfig.Builder(getApplication(), "assets/vm_config.json")
                                 .idsigPath("/data/local/tmp/virt/MicrodroidDemoApp.apk.idsig")
-                                .build();
+                                .debugMode(debug);
+                VirtualMachineConfig config = builder.build();
                 VirtualMachineManager vmm = VirtualMachineManager.getInstance(getApplication());
                 mVirtualMachine = vmm.create("demo_vm", config);
                 mVirtualMachine.run();
+                mStatus.postValue(mVirtualMachine.getStatus());
             } catch (VirtualMachineException e) {
                 throw new RuntimeException(e);
             }
@@ -105,8 +148,25 @@
                     });
         }
 
+        /** Stops the running VM */
+        public void stop() {
+            try {
+                mVirtualMachine.stop();
+            } catch (VirtualMachineException e) {
+                // Consume
+            }
+            mVirtualMachine = null;
+            mStatus.postValue(VirtualMachine.Status.STOPPED);
+        }
+
+        /** Returns the console output from the VM */
         public LiveData<String> getConsoleOutput() {
             return mConsoleOutput;
         }
+
+        /** Returns the status of the VM */
+        public LiveData<VirtualMachine.Status> getStatus() {
+            return mStatus;
+        }
     }
 }
diff --git a/demo/res/layout/activity_main.xml b/demo/res/layout/activity_main.xml
index 026382f..cd30f35 100644
--- a/demo/res/layout/activity_main.xml
+++ b/demo/res/layout/activity_main.xml
@@ -9,18 +9,43 @@
     android:textAlignment="textStart"
     tools:context=".MainActivity">
 
-    <ScrollView
-        android:id="@+id/scrollview"
+    <LinearLayout
         android:layout_width="match_parent"
-        android:layout_height="match_parent">
+        android:layout_height="match_parent"
+        android:orientation="vertical">
 
-        <TextView
-            android:id="@+id/textview"
+        <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:background="#FFEB3B"
-            android:fontFamily="monospace"
-            android:textColor="#000000" />
-    </ScrollView>
+            android:orientation="horizontal">
+
+            <Button
+                android:id="@+id/runStopButton"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:text="Run" />
+
+            <CheckBox
+                android:id="@+id/debugMode"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_weight="1"
+                android:text="Debug mode" />
+        </LinearLayout>
+
+        <ScrollView
+            android:id="@+id/scrollview"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent">
+
+            <TextView
+                android:id="@+id/consoleOutput"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:background="#FFEB3B"
+                android:fontFamily="monospace"
+                android:textColor="#000000" />
+        </ScrollView>
+    </LinearLayout>
 
 </androidx.constraintlayout.widget.ConstraintLayout>