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>
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
index 062cd04..b5f04a2 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -43,10 +43,12 @@
private static final String KEY_APKPATH = "apkPath";
private static final String KEY_IDSIGPATH = "idsigPath";
private static final String KEY_PAYLOADCONFIGPATH = "payloadConfigPath";
+ private static final String KEY_DEBUGMODE = "debugMode";
// Paths to the APK and its idsig file of this application.
private final String mApkPath;
private final String mIdsigPath;
+ private final boolean mDebugMode;
/**
* Path within the APK to the payload config file that defines software aspects of this config.
@@ -55,10 +57,12 @@
// TODO(jiyong): add more items like # of cpu, size of ram, debuggability, etc.
- private VirtualMachineConfig(String apkPath, String idsigPath, String payloadConfigPath) {
+ private VirtualMachineConfig(
+ String apkPath, String idsigPath, String payloadConfigPath, boolean debugMode) {
mApkPath = apkPath;
mIdsigPath = idsigPath;
mPayloadConfigPath = payloadConfigPath;
+ mDebugMode = debugMode;
}
/** Loads a config from a stream, for example a file. */
@@ -81,7 +85,8 @@
if (payloadConfigPath == null) {
throw new VirtualMachineException("No payloadConfigPath");
}
- return new VirtualMachineConfig(apkPath, idsigPath, payloadConfigPath);
+ final boolean debugMode = b.getBoolean(KEY_DEBUGMODE);
+ return new VirtualMachineConfig(apkPath, idsigPath, payloadConfigPath, debugMode);
}
/** Persists this config to a stream, for example a file. */
@@ -91,6 +96,7 @@
b.putString(KEY_APKPATH, mApkPath);
b.putString(KEY_IDSIGPATH, mIdsigPath);
b.putString(KEY_PAYLOADCONFIGPATH, mPayloadConfigPath);
+ b.putBoolean(KEY_DEBUGMODE, mDebugMode);
b.writeToStream(output);
}
@@ -110,6 +116,7 @@
parcel.apk = ParcelFileDescriptor.open(new File(mApkPath), MODE_READ_ONLY);
parcel.idsig = ParcelFileDescriptor.open(new File(mIdsigPath), MODE_READ_ONLY);
parcel.configPath = mPayloadConfigPath;
+ parcel.debug = mDebugMode;
return parcel;
}
@@ -117,6 +124,7 @@
public static class Builder {
private Context mContext;
private String mPayloadConfigPath;
+ private boolean mDebugMode;
private String mIdsigPath; // TODO(jiyong): remove this
// TODO(jiyong): add more items like # of cpu, size of ram, debuggability, etc.
@@ -124,6 +132,13 @@
public Builder(Context context, String payloadConfigPath) {
mContext = context;
mPayloadConfigPath = payloadConfigPath;
+ mDebugMode = false;
+ }
+
+ /** Enables or disables the debug mode */
+ public Builder debugMode(boolean enableOrDisable) {
+ mDebugMode = enableOrDisable;
+ return this;
}
// TODO(jiyong): remove this. Apps shouldn't need to set the path to the idsig file. It
@@ -137,7 +152,7 @@
/** Builds an immutable {@link VirtualMachineConfig} */
public VirtualMachineConfig build() {
final String apkPath = mContext.getPackageCodePath();
- return new VirtualMachineConfig(apkPath, mIdsigPath, mPayloadConfigPath);
+ return new VirtualMachineConfig(apkPath, mIdsigPath, mPayloadConfigPath, mDebugMode);
}
}
}