blob: 6373b55cfa962de50ba98897f412be07a0100e87 [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.microdroid.demo;
import android.app.Application;
import android.os.Bundle;
import android.system.virtualmachine.VirtualMachine;
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;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
import androidx.lifecycle.ViewModelProvider;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* This app is to demonstrate the use of APIs in the android.system.virtualmachine library.
* Currently, this app starts a virtual machine running Microdroid and shows the console output from
* the virtual machine to the UI.
*/
public class MainActivity extends AppCompatActivity {
@Override
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);
// 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) {
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 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.Builder builder =
new VirtualMachineConfig.Builder(getApplication(), "assets/vm_config.json")
.idsigPath("/data/local/tmp/virt/MicrodroidDemoApp.apk.idsig")
.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);
}
// Read console output from the VM in the background
ExecutorService executorService = Executors.newFixedThreadPool(1);
executorService.execute(
new Runnable() {
@Override
public void run() {
try {
BufferedReader reader =
new BufferedReader(
new InputStreamReader(
mVirtualMachine.getConsoleOutputStream()));
while (true) {
String line = reader.readLine();
mConsoleOutput.postValue(line);
}
} catch (IOException | VirtualMachineException e) {
// Consume
}
}
});
}
/** 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;
}
}
}