VirtualMachine API changes pre-@SystemApi
This is the set of code changes proposed in
https://r.android.com/2192077, except for the migration to @SystemApi:
- Replace enums with integer constants. Map the AIDL constants to the
API constants. (Some of this was already done in
https://r.android.com/2203781.)
- Make connectToVsockServer synchronous rather than using Future.
- Make VirtualMachine AutoCloseable (close() == stop()).
- Add VirtualMachine getters to match the builder's setters.
- Lots of nullability declarations.
- Added @RequiresPermission as appropriate.
- Rename onDied, DeathReason to onStopped, StopReason.
- Assorted documentation tweaks.
Also modify the clients to match.
I want to do this now separately because that CL is going to take a
while to get in, and I want to submit some further changes that modify
VirtualMachineConfig (b/243513572).
Bug: 243512115
Bug: 236811123
Test: atest MicrodroidTests MicrodroidHostTestCases
Test: Install & run demo app
Change-Id: I4bee00ffd23732bc9dfdca8c4c104b487f24907c
diff --git a/demo/java/com/android/microdroid/demo/MainActivity.java b/demo/java/com/android/microdroid/demo/MainActivity.java
index 6266c18..1fdce03 100644
--- a/demo/java/com/android/microdroid/demo/MainActivity.java
+++ b/demo/java/com/android/microdroid/demo/MainActivity.java
@@ -24,7 +24,6 @@
import android.system.virtualmachine.VirtualMachine;
import android.system.virtualmachine.VirtualMachineCallback;
import android.system.virtualmachine.VirtualMachineConfig;
-import android.system.virtualmachine.VirtualMachineConfig.DebugLevel;
import android.system.virtualmachine.VirtualMachineException;
import android.system.virtualmachine.VirtualMachineManager;
import android.util.Log;
@@ -49,7 +48,6 @@
import java.io.InputStreamReader;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
/**
* This app is to demonstrate the use of APIs in the android.system.virtualmachine library.
@@ -75,10 +73,11 @@
// When the button is clicked, run or stop the VM
runStopButton.setOnClickListener(
v -> {
- if (model.getStatus().getValue() == VirtualMachine.Status.RUNNING) {
+ Integer status = model.getStatus().getValue();
+ if (status != null && status == VirtualMachine.STATUS_RUNNING) {
model.stop();
} else {
- CheckBox debugModeCheckBox = (CheckBox) findViewById(R.id.debugMode);
+ CheckBox debugModeCheckBox = findViewById(R.id.debugMode);
final boolean debug = debugModeCheckBox.isChecked();
model.run(debug);
}
@@ -88,7 +87,7 @@
model.getStatus()
.observeForever(
status -> {
- if (status == VirtualMachine.Status.RUNNING) {
+ if (status == VirtualMachine.STATUS_RUNNING) {
runStopButton.setText("Stop");
// Clear the outputs from the previous run
consoleView.setText("");
@@ -150,12 +149,12 @@
private final MutableLiveData<String> mConsoleOutput = new MutableLiveData<>();
private final MutableLiveData<String> mLogOutput = new MutableLiveData<>();
private final MutableLiveData<String> mPayloadOutput = new MutableLiveData<>();
- private final MutableLiveData<VirtualMachine.Status> mStatus = new MutableLiveData<>();
+ private final MutableLiveData<Integer> mStatus = new MutableLiveData<>();
private ExecutorService mExecutorService;
public VirtualMachineModel(Application app) {
super(app);
- mStatus.setValue(VirtualMachine.Status.DELETED);
+ mStatus.setValue(VirtualMachine.STATUS_DELETED);
}
/** Runs a VM */
@@ -169,8 +168,8 @@
private final ExecutorService mService = mExecutorService;
@Override
- public void onPayloadStarted(
- VirtualMachine vm, ParcelFileDescriptor stream) {
+ public void onPayloadStarted(VirtualMachine vm,
+ ParcelFileDescriptor stream) {
if (stream == null) {
mPayloadOutput.postValue("(no output available)");
return;
@@ -189,25 +188,13 @@
}
mPayloadOutput.postValue("(Payload is ready. Testing VM service...)");
- Future<IBinder> service;
- try {
- service = vm.connectToVsockServer(ITestService.SERVICE_PORT);
- } catch (VirtualMachineException e) {
- mPayloadOutput.postValue(
- String.format(
- "(Exception while connecting VM's binder"
- + " service: %s)",
- e.getMessage()));
- return;
- }
-
- mService.execute(() -> testVMService(service));
+ mService.execute(() -> testVmService(vm));
}
- private void testVMService(Future<IBinder> service) {
+ private void testVmService(VirtualMachine vm) {
IBinder binder;
try {
- binder = service.get();
+ binder = vm.connectToVsockServer(ITestService.SERVICE_PORT);
} catch (Exception e) {
if (!Thread.interrupted()) {
mPayloadOutput.postValue(
@@ -256,9 +243,9 @@
}
@Override
- public void onDied(VirtualMachine vm, int reason) {
+ public void onStopped(VirtualMachine vm, int reason) {
mService.shutdownNow();
- mStatus.postValue(VirtualMachine.Status.STOPPED);
+ mStatus.postValue(VirtualMachine.STATUS_STOPPED);
}
@Override
@@ -273,7 +260,7 @@
VirtualMachineConfig.Builder builder =
new VirtualMachineConfig.Builder(getApplication(), "assets/vm_config.json");
if (debug) {
- builder.debugLevel(DebugLevel.FULL);
+ builder.setDebugLevel(VirtualMachineConfig.DEBUG_LEVEL_FULL);
}
VirtualMachineConfig config = builder.build();
VirtualMachineManager vmm = VirtualMachineManager.getInstance(getApplication());
@@ -306,7 +293,7 @@
}
mVirtualMachine = null;
mExecutorService.shutdownNow();
- mStatus.postValue(VirtualMachine.Status.STOPPED);
+ mStatus.postValue(VirtualMachine.STATUS_STOPPED);
}
/** Returns the console output from the VM */
@@ -325,7 +312,7 @@
}
/** Returns the status of the VM */
- public LiveData<VirtualMachine.Status> getStatus() {
+ public LiveData<Integer> getStatus() {
return mStatus;
}
}
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index cc99006..2b4d185 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -18,16 +18,43 @@
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
+import static android.system.virtualmachine.VirtualMachineCallback.ERROR_PAYLOAD_CHANGED;
+import static android.system.virtualmachine.VirtualMachineCallback.ERROR_PAYLOAD_INVALID_CONFIG;
+import static android.system.virtualmachine.VirtualMachineCallback.ERROR_PAYLOAD_VERIFICATION_FAILED;
+import static android.system.virtualmachine.VirtualMachineCallback.ERROR_UNKNOWN;
+import static android.system.virtualmachine.VirtualMachineCallback.STOP_REASON_BOOTLOADER_INSTANCE_IMAGE_CHANGED;
+import static android.system.virtualmachine.VirtualMachineCallback.STOP_REASON_BOOTLOADER_PUBLIC_KEY_MISMATCH;
+import static android.system.virtualmachine.VirtualMachineCallback.STOP_REASON_CRASH;
+import static android.system.virtualmachine.VirtualMachineCallback.STOP_REASON_ERROR;
+import static android.system.virtualmachine.VirtualMachineCallback.STOP_REASON_HANGUP;
+import static android.system.virtualmachine.VirtualMachineCallback.STOP_REASON_INFRASTRUCTURE_ERROR;
+import static android.system.virtualmachine.VirtualMachineCallback.STOP_REASON_KILLED;
+import static android.system.virtualmachine.VirtualMachineCallback.STOP_REASON_MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE;
+import static android.system.virtualmachine.VirtualMachineCallback.STOP_REASON_MICRODROID_INVALID_PAYLOAD_CONFIG;
+import static android.system.virtualmachine.VirtualMachineCallback.STOP_REASON_MICRODROID_PAYLOAD_HAS_CHANGED;
+import static android.system.virtualmachine.VirtualMachineCallback.STOP_REASON_MICRODROID_PAYLOAD_VERIFICATION_FAILED;
+import static android.system.virtualmachine.VirtualMachineCallback.STOP_REASON_MICRODROID_UNKNOWN_RUNTIME_ERROR;
+import static android.system.virtualmachine.VirtualMachineCallback.STOP_REASON_PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED;
+import static android.system.virtualmachine.VirtualMachineCallback.STOP_REASON_PVM_FIRMWARE_PUBLIC_KEY_MISMATCH;
+import static android.system.virtualmachine.VirtualMachineCallback.STOP_REASON_REBOOT;
+import static android.system.virtualmachine.VirtualMachineCallback.STOP_REASON_SHUTDOWN;
+import static android.system.virtualmachine.VirtualMachineCallback.STOP_REASON_UNKNOWN;
+
+import static java.util.Objects.requireNonNull;
import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
+import android.system.virtualizationcommon.ErrorCode;
+import android.system.virtualizationservice.DeathReason;
import android.system.virtualizationservice.IVirtualMachine;
import android.system.virtualizationservice.IVirtualMachineCallback;
import android.system.virtualizationservice.IVirtualizationService;
@@ -45,6 +72,8 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
@@ -55,9 +84,6 @@
import java.util.Optional;
import java.util.WeakHashMap;
import java.util.concurrent.Executor;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import java.util.zip.ZipFile;
@@ -68,7 +94,7 @@
*
* @hide
*/
-public class VirtualMachine {
+public class VirtualMachine implements AutoCloseable {
private static final Map<Context, Map<String, WeakReference<VirtualMachine>>> sInstances =
new WeakHashMap<>();
@@ -92,18 +118,35 @@
/** Name of the virtualization service. */
private static final String SERVICE_NAME = "android.system.virtualizationservice";
- /** Status of a virtual machine */
- public enum Status {
- /** The virtual machine has just been created, or {@link #stop()} was called on it. */
- STOPPED,
- /** The virtual machine is running. */
- RUNNING,
- /**
- * The virtual machine is deleted. This is a irreversable state. Once a virtual machine is
- * deleted, it can never be undone, which means all its secrets are permanently lost.
- */
- DELETED,
- }
+ /** The permission needed to create or run a virtual machine. */
+ public static final String MANAGE_VIRTUAL_MACHINE_PERMISSION =
+ "android.permission.MANAGE_VIRTUAL_MACHINE";
+
+ /**
+ * Status of a virtual machine
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ STATUS_STOPPED,
+ STATUS_RUNNING,
+ STATUS_DELETED
+ })
+ public @interface Status {}
+
+ /** The virtual machine has just been created, or {@link #stop()} was called on it. */
+ public static final int STATUS_STOPPED = 0;
+
+ /** The virtual machine is running. */
+ public static final int STATUS_RUNNING = 1;
+
+ /**
+ * The virtual machine has been deleted. This is an irreversible state. Once a virtual machine
+ * is deleted all its secrets are permanently lost, and it cannot be run. A new virtual machine
+ * with the same name and config may be created, with new and different secrets.
+ */
+ public static final int STATUS_DELETED = 2;
/** Lock for internal synchronization. */
private final Object mLock = new Object();
@@ -166,8 +209,6 @@
@Nullable private ParcelFileDescriptor mLogReader;
@Nullable private ParcelFileDescriptor mLogWriter;
- private final ExecutorService mExecutorService = Executors.newCachedThreadPool();
-
@NonNull private final Context mContext;
static {
@@ -179,8 +220,8 @@
throws VirtualMachineException {
mContext = context;
mPackageName = context.getPackageName();
- mName = name;
- mConfig = config;
+ mName = requireNonNull(name, "Name must not be null");
+ mConfig = requireNonNull(config, "Config must not be null");
mConfigFilePath = getConfigFilePath(context, name);
final File vmRoot = new File(context.getFilesDir(), VM_DIR);
@@ -193,15 +234,12 @@
/**
* Creates a virtual machine with the given name and config. Once a virtual machine is created
* it is persisted until it is deleted by calling {@link #delete()}. The created virtual machine
- * is in {@link Status#STOPPED} state. To run the VM, call {@link #run()}.
+ * is in {@link #STATUS_STOPPED} state. To run the VM, call {@link #run()}.
*/
@NonNull
static VirtualMachine create(
@NonNull Context context, @NonNull String name, @NonNull VirtualMachineConfig config)
throws VirtualMachineException {
- if (config == null) {
- throw new VirtualMachineException("null config");
- }
VirtualMachine vm = new VirtualMachine(context, name, config);
try {
@@ -332,29 +370,29 @@
*
* @hide
*/
- @NonNull
- public Status getStatus() throws VirtualMachineException {
+ @Status
+ public int getStatus() throws VirtualMachineException {
try {
if (mVirtualMachine != null) {
switch (mVirtualMachine.getState()) {
case VirtualMachineState.NOT_STARTED:
- return Status.STOPPED;
+ return STATUS_STOPPED;
case VirtualMachineState.STARTING:
case VirtualMachineState.STARTED:
case VirtualMachineState.READY:
case VirtualMachineState.FINISHED:
- return Status.RUNNING;
+ return STATUS_RUNNING;
case VirtualMachineState.DEAD:
- return Status.STOPPED;
+ return STATUS_STOPPED;
}
}
} catch (RemoteException e) {
throw new VirtualMachineException(e);
}
if (!mConfigFilePath.exists()) {
- return Status.DELETED;
+ return STATUS_DELETED;
}
- return Status.STOPPED;
+ return STATUS_STOPPED;
}
/**
@@ -363,8 +401,7 @@
*
* @hide
*/
- public void setCallback(
- @NonNull @CallbackExecutor Executor executor,
+ public void setCallback(@NonNull @CallbackExecutor Executor executor,
@NonNull VirtualMachineCallback callback) {
synchronized (mLock) {
mCallback = callback;
@@ -406,12 +443,13 @@
/**
* Runs this virtual machine. The returning of this method however doesn't mean that the VM has
* actually started running or the OS has booted there. Such events can be notified by
- * registering a callback object (not implemented currently).
+ * registering a callback using {@link #setCallback(Executor, VirtualMachineCallback)}.
*
* @hide
*/
+ @RequiresPermission(MANAGE_VIRTUAL_MACHINE_PERMISSION)
public void run() throws VirtualMachineException {
- if (getStatus() != Status.STOPPED) {
+ if (getStatus() != STATUS_STOPPED) {
throw new VirtualMachineException(this + " is not in stopped state");
}
@@ -472,8 +510,8 @@
IBinder.DeathRecipient deathRecipient = () -> {
if (onDiedCalled.compareAndSet(false, true)) {
- executeCallback((cb) -> cb.onDied(VirtualMachine.this,
- VirtualMachineCallback.DEATH_REASON_VIRTUALIZATIONSERVICE_DIED));
+ executeCallback((cb) -> cb.onStopped(VirtualMachine.this,
+ VirtualMachineCallback.STOP_REASON_VIRTUALIZATION_SERVICE_DIED));
}
};
@@ -485,28 +523,37 @@
executeCallback(
(cb) -> cb.onPayloadStarted(VirtualMachine.this, stream));
}
+
@Override
public void onPayloadReady(int cid) {
executeCallback((cb) -> cb.onPayloadReady(VirtualMachine.this));
}
+
@Override
public void onPayloadFinished(int cid, int exitCode) {
executeCallback(
(cb) -> cb.onPayloadFinished(VirtualMachine.this, exitCode));
}
+
@Override
public void onError(int cid, int errorCode, String message) {
+ int translatedError = getTranslatedError(errorCode);
executeCallback(
- (cb) -> cb.onError(VirtualMachine.this, errorCode, message));
+ (cb) -> cb.onError(VirtualMachine.this, translatedError,
+ message));
}
+
@Override
public void onDied(int cid, int reason) {
- // TODO(b/236811123) translate `reason` into a stable reason numbers
service.asBinder().unlinkToDeath(deathRecipient, 0);
+ int translatedReason = getTranslatedReason(reason);
if (onDiedCalled.compareAndSet(false, true)) {
- executeCallback((cb) -> cb.onDied(VirtualMachine.this, reason));
+ executeCallback(
+ (cb) -> cb.onStopped(VirtualMachine.this,
+ translatedReason));
}
}
+
@Override
public void onRamdump(int cid, ParcelFileDescriptor ramdump) {
executeCallback(
@@ -521,6 +568,60 @@
}
}
+ @VirtualMachineCallback.ErrorCode
+ private int getTranslatedError(int reason) {
+ switch (reason) {
+ case ErrorCode.PAYLOAD_VERIFICATION_FAILED:
+ return ERROR_PAYLOAD_VERIFICATION_FAILED;
+ case ErrorCode.PAYLOAD_CHANGED:
+ return ERROR_PAYLOAD_CHANGED;
+ case ErrorCode.PAYLOAD_CONFIG_INVALID:
+ return ERROR_PAYLOAD_INVALID_CONFIG;
+ default:
+ return ERROR_UNKNOWN;
+ }
+ }
+
+ @VirtualMachineCallback.StopReason
+ private int getTranslatedReason(int reason) {
+ switch (reason) {
+ case DeathReason.INFRASTRUCTURE_ERROR:
+ return STOP_REASON_INFRASTRUCTURE_ERROR;
+ case DeathReason.KILLED:
+ return STOP_REASON_KILLED;
+ case DeathReason.SHUTDOWN:
+ return STOP_REASON_SHUTDOWN;
+ case DeathReason.ERROR:
+ return STOP_REASON_ERROR;
+ case DeathReason.REBOOT:
+ return STOP_REASON_REBOOT;
+ case DeathReason.CRASH:
+ return STOP_REASON_CRASH;
+ case DeathReason.PVM_FIRMWARE_PUBLIC_KEY_MISMATCH:
+ return STOP_REASON_PVM_FIRMWARE_PUBLIC_KEY_MISMATCH;
+ case DeathReason.PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED:
+ return STOP_REASON_PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED;
+ case DeathReason.BOOTLOADER_PUBLIC_KEY_MISMATCH:
+ return STOP_REASON_BOOTLOADER_PUBLIC_KEY_MISMATCH;
+ case DeathReason.BOOTLOADER_INSTANCE_IMAGE_CHANGED:
+ return STOP_REASON_BOOTLOADER_INSTANCE_IMAGE_CHANGED;
+ case DeathReason.MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE:
+ return STOP_REASON_MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE;
+ case DeathReason.MICRODROID_PAYLOAD_HAS_CHANGED:
+ return STOP_REASON_MICRODROID_PAYLOAD_HAS_CHANGED;
+ case DeathReason.MICRODROID_PAYLOAD_VERIFICATION_FAILED:
+ return STOP_REASON_MICRODROID_PAYLOAD_VERIFICATION_FAILED;
+ case DeathReason.MICRODROID_INVALID_PAYLOAD_CONFIG:
+ return STOP_REASON_MICRODROID_INVALID_PAYLOAD_CONFIG;
+ case DeathReason.MICRODROID_UNKNOWN_RUNTIME_ERROR:
+ return STOP_REASON_MICRODROID_UNKNOWN_RUNTIME_ERROR;
+ case DeathReason.HANGUP:
+ return STOP_REASON_HANGUP;
+ default:
+ return STOP_REASON_UNKNOWN;
+ }
+ }
+
/**
* Returns the stream object representing the console output from the virtual machine.
*
@@ -550,7 +651,7 @@
/**
* Stops this virtual machine. Stopping a virtual machine is like pulling the plug on a real
* computer; the machine halts immediately. Software running on the virtual machine is not
- * notified with the event. A stopped virtual machine can be re-started by calling {@link
+ * notified of the event. A stopped virtual machine can be re-started by calling {@link
* #run()}.
*
* @hide
@@ -566,15 +667,25 @@
}
/**
+ * Stops this virtual machine. See {@link #stop()}.
+ *
+ * @hide
+ */
+ @Override
+ public void close() throws VirtualMachineException {
+ stop();
+ }
+
+ /**
* Deletes this virtual machine. Deleting a virtual machine means deleting any persisted data
- * associated with it including the per-VM secret. This is an irreversable action. A virtual
+ * associated with it including the per-VM secret. This is an irreversible action. A virtual
* machine once deleted can never be restored. A new virtual machine created with the same name
* and the same config is different from an already deleted virtual machine.
*
* @hide
*/
public void delete() throws VirtualMachineException {
- if (getStatus() != Status.STOPPED) {
+ if (getStatus() != STATUS_STOPPED) {
throw new VirtualMachineException("Virtual machine is not stopped");
}
final File vmRootDir = mConfigFilePath.getParentFile();
@@ -599,7 +710,7 @@
*/
@NonNull
public Optional<Integer> getCid() throws VirtualMachineException {
- if (getStatus() != Status.RUNNING) {
+ if (getStatus() != STATUS_RUNNING) {
return Optional.empty();
}
try {
@@ -629,7 +740,7 @@
if (!oldConfig.isCompatibleWith(newConfig)) {
throw new VirtualMachineException("incompatible config");
}
- if (getStatus() != Status.STOPPED) {
+ if (getStatus() != STATUS_STOPPED) {
throw new VirtualMachineException(
"can't change config while virtual machine is not stopped");
}
@@ -649,19 +760,19 @@
private static native IBinder nativeConnectToVsockServer(IBinder vmBinder, int port);
/**
- * Connects to a VM's RPC server via vsock, and returns a root IBinder object. Guest VMs are
+ * Connect to a VM's binder service via vsock and return the root IBinder object. Guest VMs are
* expected to set up vsock servers in their payload. After the host app receives the {@link
* VirtualMachineCallback#onPayloadReady(VirtualMachine)}, it can use this method to
- * establish an RPC session to the guest VMs.
+ * establish a connection to the guest VM.
*
* @hide
*/
- public Future<IBinder> connectToVsockServer(int port) throws VirtualMachineException {
- if (getStatus() != Status.RUNNING) {
+ @NonNull
+ public IBinder connectToVsockServer(int port) throws VirtualMachineException {
+ if (getStatus() != STATUS_RUNNING) {
throw new VirtualMachineException("VM is not running");
}
- return mExecutorService.submit(
- () -> nativeConnectToVsockServer(mVirtualMachine.asBinder(), port));
+ return nativeConnectToVsockServer(mVirtualMachine.asBinder(), port);
}
/**
@@ -669,11 +780,12 @@
*
* @hide
*/
+ @NonNull
public ParcelFileDescriptor connectVsock(int port) throws VirtualMachineException {
try {
return mVirtualMachine.connectVsock(port);
} catch (RemoteException e) {
- throw new VirtualMachineException("failed to connect Vsock", e);
+ throw new VirtualMachineException("failed to connect vsock", e);
}
}
@@ -731,7 +843,6 @@
throws VirtualMachineException {
try {
ZipFile zipFile = new ZipFile(context.getPackageCodePath());
- String payloadPath = config.getPayloadConfigPath();
InputStream inputStream =
zipFile.getInputStream(zipFile.getEntry(config.getPayloadConfigPath()));
List<String> apkList =
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineCallback.java b/javalib/src/android/system/virtualmachine/VirtualMachineCallback.java
index c802678..c89b8bb 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineCallback.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineCallback.java
@@ -19,6 +19,7 @@
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.SuppressLint;
import android.os.ParcelFileDescriptor;
import java.lang.annotation.Retention;
@@ -30,6 +31,7 @@
*
* @hide
*/
+@SuppressLint("CallbackInterface") // Guidance has changed, lint is out of date (b/245552641)
public interface VirtualMachineCallback {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@@ -43,90 +45,95 @@
/** Error code for all other errors not listed below. */
int ERROR_UNKNOWN = 0;
-
/**
* Error code indicating that the payload can't be verified due to various reasons (e.g invalid
* merkle tree, invalid formats, etc).
*/
int ERROR_PAYLOAD_VERIFICATION_FAILED = 1;
-
/** Error code indicating that the payload is verified, but has changed since the last boot. */
int ERROR_PAYLOAD_CHANGED = 2;
-
/** Error code indicating that the payload config is invalid. */
int ERROR_PAYLOAD_INVALID_CONFIG = 3;
/** @hide */
@Retention(RetentionPolicy.SOURCE)
@IntDef({
- DEATH_REASON_VIRTUALIZATIONSERVICE_DIED,
- DEATH_REASON_INFRASTRUCTURE_ERROR,
- DEATH_REASON_KILLED,
- DEATH_REASON_UNKNOWN,
- DEATH_REASON_SHUTDOWN,
- DEATH_REASON_ERROR,
- DEATH_REASON_REBOOT,
- DEATH_REASON_CRASH,
- DEATH_REASON_HANGUP,
+ STOP_REASON_VIRTUALIZATION_SERVICE_DIED,
+ STOP_REASON_INFRASTRUCTURE_ERROR,
+ STOP_REASON_KILLED,
+ STOP_REASON_UNKNOWN,
+ STOP_REASON_SHUTDOWN,
+ STOP_REASON_ERROR,
+ STOP_REASON_REBOOT,
+ STOP_REASON_CRASH,
+ STOP_REASON_PVM_FIRMWARE_PUBLIC_KEY_MISMATCH,
+ STOP_REASON_PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED,
+ STOP_REASON_BOOTLOADER_PUBLIC_KEY_MISMATCH,
+ STOP_REASON_BOOTLOADER_INSTANCE_IMAGE_CHANGED,
+ STOP_REASON_MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE,
+ STOP_REASON_MICRODROID_PAYLOAD_HAS_CHANGED,
+ STOP_REASON_MICRODROID_PAYLOAD_VERIFICATION_FAILED,
+ STOP_REASON_MICRODROID_INVALID_PAYLOAD_CONFIG,
+ STOP_REASON_MICRODROID_UNKNOWN_RUNTIME_ERROR,
+ STOP_REASON_HANGUP,
})
- @interface DeathReason {}
+ @interface StopReason {}
- /**
- * virtualizationservice itself died, taking the VM down with it. This is a negative number to
- * avoid conflicting with the other death reasons which match the ones in the AIDL interface.
- */
- int DEATH_REASON_VIRTUALIZATIONSERVICE_DIED = -1;
+ /** The virtualization service itself died, taking the VM down with it. */
+ // This is a negative number to avoid conflicting with the other death reasons which match
+ // the ones in the AIDL interface.
+ int STOP_REASON_VIRTUALIZATION_SERVICE_DIED = -1;
/** There was an error waiting for the VM. */
- int DEATH_REASON_INFRASTRUCTURE_ERROR = 0;
+ int STOP_REASON_INFRASTRUCTURE_ERROR = 0;
/** The VM was killed. */
- int DEATH_REASON_KILLED = 1;
+ int STOP_REASON_KILLED = 1;
/** The VM died for an unknown reason. */
- int DEATH_REASON_UNKNOWN = 2;
+ int STOP_REASON_UNKNOWN = 2;
/** The VM requested to shut down. */
- int DEATH_REASON_SHUTDOWN = 3;
+ int STOP_REASON_SHUTDOWN = 3;
/** crosvm had an error starting the VM. */
- int DEATH_REASON_ERROR = 4;
+ int STOP_REASON_ERROR = 4;
/** The VM requested to reboot, possibly as the result of a kernel panic. */
- int DEATH_REASON_REBOOT = 5;
+ int STOP_REASON_REBOOT = 5;
/** The VM or crosvm crashed. */
- int DEATH_REASON_CRASH = 6;
+ int STOP_REASON_CRASH = 6;
/** The pVM firmware failed to verify the VM because the public key doesn't match. */
- int DEATH_REASON_PVM_FIRMWARE_PUBLIC_KEY_MISMATCH = 7;
+ int STOP_REASON_PVM_FIRMWARE_PUBLIC_KEY_MISMATCH = 7;
/** The pVM firmware failed to verify the VM because the instance image changed. */
- int DEATH_REASON_PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED = 8;
+ int STOP_REASON_PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED = 8;
/** The bootloader failed to verify the VM because the public key doesn't match. */
- int DEATH_REASON_BOOTLOADER_PUBLIC_KEY_MISMATCH = 9;
+ int STOP_REASON_BOOTLOADER_PUBLIC_KEY_MISMATCH = 9;
/** The bootloader failed to verify the VM because the instance image changed. */
- int DEATH_REASON_BOOTLOADER_INSTANCE_IMAGE_CHANGED = 10;
+ int STOP_REASON_BOOTLOADER_INSTANCE_IMAGE_CHANGED = 10;
/** The microdroid failed to connect to VirtualizationService's RPC server. */
- int DEATH_REASON_MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE = 11;
+ int STOP_REASON_MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE = 11;
/** The payload for microdroid is changed. */
- int DEATH_REASON_MICRODROID_PAYLOAD_HAS_CHANGED = 12;
+ int STOP_REASON_MICRODROID_PAYLOAD_HAS_CHANGED = 12;
/** The microdroid failed to verify given payload APK. */
- int DEATH_REASON_MICRODROID_PAYLOAD_VERIFICATION_FAILED = 13;
+ int STOP_REASON_MICRODROID_PAYLOAD_VERIFICATION_FAILED = 13;
/** The VM config for microdroid is invalid (e.g. missing tasks). */
- int DEATH_REASON_MICRODROID_INVALID_PAYLOAD_CONFIG = 14;
+ int STOP_REASON_MICRODROID_INVALID_PAYLOAD_CONFIG = 14;
/** There was a runtime error while running microdroid manager. */
- int DEATH_REASON_MICRODROID_UNKNOWN_RUNTIME_ERROR = 15;
+ int STOP_REASON_MICRODROID_UNKNOWN_RUNTIME_ERROR = 15;
/** The VM killed due to hangup */
- int DEATH_REASON_HANGUP = 16;
+ int STOP_REASON_HANGUP = 16;
/**
* Called when the payload starts in the VM. The stream, if non-null, provides access
@@ -136,7 +143,7 @@
/**
* Called when the payload in the VM is ready to serve. See
- * {@link VirtualMachine#connectToVsockServer(int)} ()}.
+ * {@link VirtualMachine#connectToVsockServer(int)}.
*/
void onPayloadReady(@NonNull VirtualMachine vm);
@@ -146,8 +153,8 @@
/** Called when an error occurs in the VM. */
void onError(@NonNull VirtualMachine vm, @ErrorCode int errorCode, @NonNull String message);
- /** Called when the VM has ended. */
- void onDied(@NonNull VirtualMachine vm, @DeathReason int reason);
+ /** Called when the VM has stopped. */
+ void onStopped(@NonNull VirtualMachine vm, @StopReason int reason);
/** Called when kernel panic occurs and as a result ramdump is generated from the VM. */
void onRamdump(@NonNull VirtualMachine vm, @NonNull ParcelFileDescriptor ramdump);
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
index 63a3f43..7f41874 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -18,11 +18,14 @@
import static android.os.ParcelFileDescriptor.MODE_READ_ONLY;
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.IntDef;
import android.annotation.NonNull;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.PackageInfoFlags;
-import android.content.pm.Signature; // This actually is certificate!
+import android.content.pm.Signature;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.sysprop.HypervisorProperties;
@@ -33,10 +36,11 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.Objects;
/**
* Represents a configuration of a virtual machine. A configuration consists of hardware
@@ -61,32 +65,40 @@
@NonNull private final String mApkPath;
@NonNull private final Signature[] mCerts;
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({
+ DEBUG_LEVEL_NONE,
+ DEBUG_LEVEL_APP_ONLY,
+ DEBUG_LEVEL_FULL
+ })
+ public @interface DebugLevel {}
+
/**
- * A debug level defines the set of debug features that the VM can be configured to.
+ * Not debuggable at all. No log is exported from the VM. Debugger can't be attached to the
+ * app process running in the VM. This is the default level.
*
* @hide
*/
- public enum DebugLevel {
- /**
- * Not debuggable at all. No log is exported from the VM. Debugger can't be attached to the
- * app process running in the VM. This is the default level.
- */
- NONE,
+ public static final int DEBUG_LEVEL_NONE = 0;
- /**
- * Only the app is debuggable. Log from the app is exported from the VM. Debugger can be
- * attached to the app process. Rest of the VM is not debuggable.
- */
- APP_ONLY,
+ /**
+ * Only the app is debuggable. Log from the app is exported from the VM. Debugger can be
+ * attached to the app process. Rest of the VM is not debuggable.
+ *
+ * @hide
+ */
+ public static final int DEBUG_LEVEL_APP_ONLY = 1;
- /**
- * Fully debuggable. All logs (both logcat and kernel message) are exported. All processes
- * running in the VM can be attached to the debugger. Rooting is possible.
- */
- FULL,
- }
+ /**
+ * Fully debuggable. All logs (both logcat and kernel message) are exported. All processes
+ * running in the VM can be attached to the debugger. Rooting is possible.
+ *
+ * @hide
+ */
+ public static final int DEBUG_LEVEL_FULL = 2;
- private final DebugLevel mDebugLevel;
+ @DebugLevel private final int mDebugLevel;
/**
* Whether to run the VM in protected mode, so the host can't access its memory.
@@ -112,7 +124,7 @@
@NonNull String apkPath,
@NonNull Signature[] certs,
@NonNull String payloadConfigPath,
- DebugLevel debugLevel,
+ @DebugLevel int debugLevel,
boolean protectedVm,
int memoryMib,
int numCpus) {
@@ -151,7 +163,11 @@
if (payloadConfigPath == null) {
throw new VirtualMachineException("No payloadConfigPath");
}
- final DebugLevel debugLevel = DebugLevel.values()[b.getInt(KEY_DEBUGLEVEL)];
+ @DebugLevel final int debugLevel = b.getInt(KEY_DEBUGLEVEL);
+ if (debugLevel != DEBUG_LEVEL_NONE && debugLevel != DEBUG_LEVEL_APP_ONLY
+ && debugLevel != DEBUG_LEVEL_FULL) {
+ throw new VirtualMachineException("Invalid debugLevel: " + debugLevel);
+ }
final boolean protectedVm = b.getBoolean(KEY_PROTECTED_VM);
final int memoryMib = b.getInt(KEY_MEMORY_MIB);
final int numCpus = b.getInt(KEY_NUM_CPUS);
@@ -171,7 +187,7 @@
String[] certs = certList.toArray(new String[0]);
b.putStringArray(KEY_CERTS, certs);
b.putString(KEY_PAYLOADCONFIGPATH, mPayloadConfigPath);
- b.putInt(KEY_DEBUGLEVEL, mDebugLevel.ordinal());
+ b.putInt(KEY_DEBUGLEVEL, mDebugLevel);
b.putBoolean(KEY_PROTECTED_VM, mProtectedVm);
b.putInt(KEY_NUM_CPUS, mNumCpus);
if (mMemoryMib > 0) {
@@ -191,6 +207,44 @@
}
/**
+ * Returns the debug level for the VM.
+ *
+ * @hide
+ */
+ @NonNull
+ @DebugLevel
+ public int getDebugLevel() {
+ return mDebugLevel;
+ }
+
+ /**
+ * Returns whether the VM's memory will be protected from the host.
+ *
+ * @hide
+ */
+ public boolean isProtectedVm() {
+ return mProtectedVm;
+ }
+
+ /**
+ * Returns the amount of RAM that will be made available to the VM.
+ *
+ * @hide
+ */
+ public int getMemoryMib() {
+ return mMemoryMib;
+ }
+
+ /**
+ * Returns the number of vCPUs that the VM will have.
+ *
+ * @hide
+ */
+ public int getNumCpus() {
+ return mNumCpus;
+ }
+
+ /**
* Tests if this config is compatible with other config. Being compatible means that the configs
* can be interchangeably used for the same virtual machine. Compatible changes includes the
* number of CPUs and the size of the RAM, and change of the payload as long as the payload is
@@ -224,15 +278,15 @@
parcel.apk = ParcelFileDescriptor.open(new File(mApkPath), MODE_READ_ONLY);
parcel.payload = VirtualMachineAppConfig.Payload.configPath(mPayloadConfigPath);
switch (mDebugLevel) {
- case NONE:
- parcel.debugLevel = VirtualMachineAppConfig.DebugLevel.NONE;
- break;
- case APP_ONLY:
+ case DEBUG_LEVEL_APP_ONLY:
parcel.debugLevel = VirtualMachineAppConfig.DebugLevel.APP_ONLY;
break;
- case FULL:
+ case DEBUG_LEVEL_FULL:
parcel.debugLevel = VirtualMachineAppConfig.DebugLevel.FULL;
break;
+ default:
+ parcel.debugLevel = VirtualMachineAppConfig.DebugLevel.NONE;
+ break;
}
parcel.protectedVm = mProtectedVm;
parcel.memoryMib = mMemoryMib;
@@ -248,10 +302,10 @@
*
* @hide
*/
- public static class Builder {
+ public static final class Builder {
private final Context mContext;
private final String mPayloadConfigPath;
- private DebugLevel mDebugLevel;
+ @DebugLevel private int mDebugLevel;
private boolean mProtectedVm;
private int mMemoryMib;
private int mNumCpus;
@@ -262,9 +316,10 @@
* @hide
*/
public Builder(@NonNull Context context, @NonNull String payloadConfigPath) {
- mContext = Objects.requireNonNull(context);
- mPayloadConfigPath = Objects.requireNonNull(payloadConfigPath);
- mDebugLevel = DebugLevel.NONE;
+ mContext = requireNonNull(context, "context must not be null");
+ mPayloadConfigPath = requireNonNull(payloadConfigPath,
+ "payloadConfigPath must not be null");
+ mDebugLevel = DEBUG_LEVEL_NONE;
mProtectedVm = false;
mNumCpus = 1;
}
@@ -274,7 +329,8 @@
*
* @hide
*/
- public Builder debugLevel(DebugLevel debugLevel) {
+ @NonNull
+ public Builder setDebugLevel(@DebugLevel int debugLevel) {
mDebugLevel = debugLevel;
return this;
}
@@ -284,7 +340,8 @@
*
* @hide
*/
- public Builder protectedVm(boolean protectedVm) {
+ @NonNull
+ public Builder setProtectedVm(boolean protectedVm) {
mProtectedVm = protectedVm;
return this;
}
@@ -295,7 +352,8 @@
*
* @hide
*/
- public Builder memoryMib(int memoryMib) {
+ @NonNull
+ public Builder setMemoryMib(int memoryMib) {
mMemoryMib = memoryMib;
return this;
}
@@ -305,7 +363,8 @@
*
* @hide
*/
- public Builder numCpus(int num) {
+ @NonNull
+ public Builder setNumCpus(int num) {
mNumCpus = num;
return this;
}
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineException.java b/javalib/src/android/system/virtualmachine/VirtualMachineException.java
index d6aeab3..88b5ea3 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineException.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineException.java
@@ -16,21 +16,27 @@
package android.system.virtualmachine;
-/** @hide */
+import android.annotation.Nullable;
+
+/**
+ * Exception thrown when operations on virtual machines fail.
+ *
+ * @hide
+ */
public class VirtualMachineException extends Exception {
public VirtualMachineException() {
super();
}
- public VirtualMachineException(String message) {
+ public VirtualMachineException(@Nullable String message) {
super(message);
}
- public VirtualMachineException(String message, Throwable cause) {
+ public VirtualMachineException(@Nullable String message, @Nullable Throwable cause) {
super(message, cause);
}
- public VirtualMachineException(Throwable cause) {
+ public VirtualMachineException(@Nullable Throwable cause) {
super(cause);
}
}
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineManager.java b/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
index 1ffc6bb..ad5864e 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
@@ -16,13 +16,16 @@
package android.system.virtualmachine;
+import static java.util.Objects.requireNonNull;
+
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
import android.content.Context;
import java.lang.ref.WeakReference;
import java.util.Map;
-import java.util.Objects;
import java.util.WeakHashMap;
/**
@@ -46,8 +49,9 @@
* @hide
*/
@NonNull
+ @SuppressLint("ManagerLookup") // Optional API
public static VirtualMachineManager getInstance(@NonNull Context context) {
- Objects.requireNonNull(context);
+ requireNonNull(context, "context must not be null");
synchronized (sInstances) {
VirtualMachineManager vmm =
sInstances.containsKey(context) ? sInstances.get(context).get() : null;
@@ -65,13 +69,17 @@
/**
* Creates a new {@link VirtualMachine} with the given name and config. Creating a virtual
* machine with the same name as an existing virtual machine is an error. The existing virtual
- * machine has to be deleted before its name can be reused. Every call to this methods creates a
- * new (and different) virtual machine even if the name and the config are the same as the
- * deleted one.
+ * machine has to be deleted before its name can be reused.
*
+ * Each successful call to this method creates a new (and different) virtual machine even if the
+ * name and the config are the same as a deleted one. The new virtual machine will initially
+ * be stopped.
+ *
+ * @throws VirtualMachineException If there is an existing virtual machine with the given name
* @hide
*/
@NonNull
+ @RequiresPermission(VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION)
public VirtualMachine create(
@NonNull String name, @NonNull VirtualMachineConfig config)
throws VirtualMachineException {
diff --git a/tests/benchmark/src/java/com/android/microdroid/benchmark/BenchmarkVmListener.java b/tests/benchmark/src/java/com/android/microdroid/benchmark/BenchmarkVmListener.java
index da08a47..cbb9a0a 100644
--- a/tests/benchmark/src/java/com/android/microdroid/benchmark/BenchmarkVmListener.java
+++ b/tests/benchmark/src/java/com/android/microdroid/benchmark/BenchmarkVmListener.java
@@ -49,7 +49,7 @@
try {
IBenchmarkService benchmarkService =
IBenchmarkService.Stub.asInterface(
- vm.connectToVsockServer(IBenchmarkService.SERVICE_PORT).get());
+ vm.connectToVsockServer(IBenchmarkService.SERVICE_PORT));
assertThat(benchmarkService).isNotNull();
mListener.onPayloadReady(vm, benchmarkService);
diff --git a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
index 263956b..e0e2274 100644
--- a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
+++ b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
@@ -16,6 +16,9 @@
package com.android.microdroid.benchmark;
+import static android.system.virtualmachine.VirtualMachineConfig.DEBUG_LEVEL_FULL;
+import static android.system.virtualmachine.VirtualMachineConfig.DEBUG_LEVEL_NONE;
+
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static com.google.common.truth.Truth.assertThat;
@@ -27,7 +30,6 @@
import android.os.RemoteException;
import android.system.virtualmachine.VirtualMachine;
import android.system.virtualmachine.VirtualMachineConfig;
-import android.system.virtualmachine.VirtualMachineConfig.DebugLevel;
import android.system.virtualmachine.VirtualMachineException;
import android.util.Log;
@@ -91,7 +93,7 @@
VirtualMachineConfig.Builder builder =
mInner.newVmConfigBuilder("assets/vm_config.json");
VirtualMachineConfig normalConfig =
- builder.debugLevel(DebugLevel.NONE).memoryMib(mem).build();
+ builder.setDebugLevel(DEBUG_LEVEL_NONE).setMemoryMib(mem).build();
mInner.forceCreateNewVirtualMachine("test_vm_minimum_memory", normalConfig);
if (tryBootVm(TAG, "test_vm_minimum_memory").payloadStarted) return true;
@@ -140,12 +142,12 @@
List<Double> userspaceBootTimeMetrics = new ArrayList<>();
for (int i = 0; i < trialCount; i++) {
- VirtualMachineConfig.Builder builder =
- mInner.newVmConfigBuilder("assets/vm_config.json");
// To grab boot events from log, set debug mode to FULL
- VirtualMachineConfig normalConfig =
- builder.debugLevel(DebugLevel.FULL).memoryMib(256).build();
+ VirtualMachineConfig normalConfig = mInner.newVmConfigBuilder("assets/vm_config.json")
+ .setDebugLevel(DEBUG_LEVEL_FULL)
+ .setMemoryMib(256)
+ .build();
mInner.forceCreateNewVirtualMachine("test_vm_boot_time", normalConfig);
BootResult result = tryBootVm(TAG, "test_vm_boot_time");
@@ -189,10 +191,9 @@
@Test
public void testVsockTransferFromHostToVM() throws Exception {
- VirtualMachineConfig config =
- mInner.newVmConfigBuilder("assets/vm_config_io.json")
- .debugLevel(DebugLevel.FULL)
- .build();
+ VirtualMachineConfig config = mInner.newVmConfigBuilder("assets/vm_config_io.json")
+ .setDebugLevel(DEBUG_LEVEL_FULL)
+ .build();
List<Double> transferRates = new ArrayList<>(IO_TEST_TRIAL_COUNT);
for (int i = 0; i < IO_TEST_TRIAL_COUNT; ++i) {
@@ -216,10 +217,9 @@
}
private void testVirtioBlkReadRate(boolean isRand) throws Exception {
- VirtualMachineConfig config =
- mInner.newVmConfigBuilder("assets/vm_config_io.json")
- .debugLevel(DebugLevel.FULL)
- .build();
+ VirtualMachineConfig config = mInner.newVmConfigBuilder("assets/vm_config_io.json")
+ .setDebugLevel(DEBUG_LEVEL_FULL)
+ .build();
List<Double> readRates = new ArrayList<>(IO_TEST_TRIAL_COUNT);
for (int i = 0; i < IO_TEST_TRIAL_COUNT + 1; ++i) {
@@ -284,8 +284,8 @@
final String vmName = "test_vm_mem_usage";
VirtualMachineConfig config =
mInner.newVmConfigBuilder("assets/vm_config_io.json")
- .debugLevel(DebugLevel.NONE)
- .memoryMib(256)
+ .setDebugLevel(DEBUG_LEVEL_NONE)
+ .setMemoryMib(256)
.build();
mInner.forceCreateNewVirtualMachine(vmName, config);
VirtualMachine vm = mInner.getVirtualMachineManager().get(vmName);
diff --git a/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java b/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
index a1dee6d..2856a30 100644
--- a/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
+++ b/tests/helper/src/java/com/android/microdroid/test/device/MicrodroidDeviceTestBase.java
@@ -80,7 +80,7 @@
/** Create a new VirtualMachineConfig.Builder with the parameterized protection mode. */
public VirtualMachineConfig.Builder newVmConfigBuilder(String payloadConfigPath) {
return new VirtualMachineConfig.Builder(mContext, payloadConfigPath)
- .protectedVm(mProtectedVm);
+ .setProtectedVm(mProtectedVm);
}
/**
@@ -239,7 +239,7 @@
@Override
@CallSuper
- public void onDied(VirtualMachine vm, int reason) {
+ public void onStopped(VirtualMachine vm, int reason) {
vm.clearCallback();
mExecutorService.shutdown();
}
@@ -328,16 +328,16 @@
}
@Override
- public void onDied(VirtualMachine vm, int reason) {
+ public void onStopped(VirtualMachine vm, int reason) {
deathReason.complete(reason);
- super.onDied(vm, reason);
+ super.onStopped(vm, reason);
}
};
long apiCallNanoTime = System.nanoTime();
listener.runToFinish(logTag, vm);
return new BootResult(
payloadStarted.getNow(false),
- deathReason.getNow(VirtualMachineCallback.DEATH_REASON_INFRASTRUCTURE_ERROR),
+ deathReason.getNow(VmEventListener.STOP_REASON_INFRASTRUCTURE_ERROR),
apiCallNanoTime,
endTime.getNow(apiCallNanoTime) - apiCallNanoTime,
listener.getVcpuStartedNanoTime(),
diff --git a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
index 4b40293..707930f 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -15,6 +15,9 @@
*/
package com.android.microdroid.test;
+import static android.system.virtualmachine.VirtualMachineConfig.DEBUG_LEVEL_FULL;
+import static android.system.virtualmachine.VirtualMachineConfig.DEBUG_LEVEL_NONE;
+
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
@@ -26,8 +29,6 @@
import android.system.virtualmachine.VirtualMachine;
import android.system.virtualmachine.VirtualMachineCallback;
import android.system.virtualmachine.VirtualMachineConfig;
-import android.system.virtualmachine.VirtualMachineConfig.DebugLevel;
-import android.system.virtualmachine.VirtualMachineException;
import android.util.Log;
import com.android.compatibility.common.util.CddTest;
@@ -53,7 +54,6 @@
import java.util.concurrent.CompletableFuture;
import co.nstant.in.cbor.CborDecoder;
-import co.nstant.in.cbor.CborException;
import co.nstant.in.cbor.model.Array;
import co.nstant.in.cbor.model.DataItem;
import co.nstant.in.cbor.model.MajorType;
@@ -86,7 +86,7 @@
"9.17/C-1-1",
"9.17/C-2-1"
})
- public void connectToVmService() throws VirtualMachineException, InterruptedException {
+ public void connectToVmService() throws Exception {
assume()
.withMessage("SKip on 5.4 kernel. b/218303240")
.that(KERNEL_VERSION)
@@ -98,10 +98,10 @@
String primaryAbi = Build.SUPPORTED_ABIS[0];
switch(primaryAbi) {
case "x86_64":
- builder.memoryMib(MIN_MEM_X86_64);
+ builder.setMemoryMib(MIN_MEM_X86_64);
break;
case "arm64-v8a":
- builder.memoryMib(MIN_MEM_ARM64);
+ builder.setMemoryMib(MIN_MEM_ARM64);
break;
}
}
@@ -123,7 +123,7 @@
private void testVMService(VirtualMachine vm) {
try {
ITestService testService = ITestService.Stub.asInterface(
- vm.connectToVsockServer(ITestService.SERVICE_PORT).get());
+ vm.connectToVsockServer(ITestService.SERVICE_PORT));
testResults.mAddInteger = testService.addInteger(123, 456);
testResults.mAppRunProp =
testService.readProperty("debug.microdroid.app.run");
@@ -163,15 +163,15 @@
}
@Test
- public void bootFailsWhenLowMem() throws VirtualMachineException, InterruptedException {
+ public void bootFailsWhenLowMem() throws Exception {
for (int memMib : new int[]{ 10, 20, 40 }) {
VirtualMachineConfig lowMemConfig = mInner.newVmConfigBuilder("assets/vm_config.json")
- .memoryMib(memMib)
- .debugLevel(DebugLevel.NONE)
+ .setMemoryMib(memMib)
+ .setDebugLevel(DEBUG_LEVEL_NONE)
.build();
VirtualMachine vm = mInner.forceCreateNewVirtualMachine("low_mem", lowMemConfig);
final CompletableFuture<Boolean> onPayloadReadyExecuted = new CompletableFuture<>();
- final CompletableFuture<Boolean> onDiedExecuted = new CompletableFuture<>();
+ final CompletableFuture<Boolean> onStoppedExecuted = new CompletableFuture<>();
VmEventListener listener =
new VmEventListener() {
@Override
@@ -180,14 +180,14 @@
super.onPayloadReady(vm);
}
@Override
- public void onDied(VirtualMachine vm, int reason) {
- onDiedExecuted.complete(true);
- super.onDied(vm, reason);
+ public void onStopped(VirtualMachine vm, int reason) {
+ onStoppedExecuted.complete(true);
+ super.onStopped(vm, reason);
}
};
listener.runToFinish(TAG, vm);
- // Assert that onDied() was executed but onPayloadReady() was never run
- assertThat(onDiedExecuted.getNow(false)).isTrue();
+ // Assert that onStopped() was executed but onPayloadReady() was never run
+ assertThat(onStoppedExecuted.getNow(false)).isTrue();
assertThat(onPayloadReadyExecuted.getNow(false)).isFalse();
}
}
@@ -197,15 +197,14 @@
"9.17/C-1-1",
"9.17/C-2-7"
})
- public void changingDebugLevelInvalidatesVmIdentity()
- throws VirtualMachineException, InterruptedException, IOException {
+ public void changingDebugLevelInvalidatesVmIdentity() throws Exception {
assume()
.withMessage("SKip on 5.4 kernel. b/218303240")
.that(KERNEL_VERSION)
.isNotEqualTo("5.4");
VirtualMachineConfig.Builder builder = mInner.newVmConfigBuilder("assets/vm_config.json");
- VirtualMachineConfig normalConfig = builder.debugLevel(DebugLevel.NONE).build();
+ VirtualMachineConfig normalConfig = builder.setDebugLevel(DEBUG_LEVEL_NONE).build();
mInner.forceCreateNewVirtualMachine("test_vm", normalConfig);
assertThat(tryBootVm(TAG, "test_vm").payloadStarted).isTrue();
@@ -223,19 +222,18 @@
// Launch the same VM with different debug level. The Java API prohibits this (thankfully).
// For testing, we do that by creating a new VM with debug level, and copy the old instance
// image to the new VM instance image.
- VirtualMachineConfig debugConfig = builder.debugLevel(DebugLevel.FULL).build();
+ VirtualMachineConfig debugConfig = builder.setDebugLevel(DEBUG_LEVEL_FULL).build();
mInner.forceCreateNewVirtualMachine("test_vm", debugConfig);
Files.copy(vmInstanceBackup.toPath(), vmInstance.toPath(), REPLACE_EXISTING);
assertThat(tryBootVm(TAG, "test_vm").payloadStarted).isFalse();
}
- private class VmCdis {
+ private static class VmCdis {
public byte[] cdiAttest;
public byte[] cdiSeal;
}
- private VmCdis launchVmAndGetCdis(String instanceName)
- throws VirtualMachineException, InterruptedException {
+ private VmCdis launchVmAndGetCdis(String instanceName) throws Exception {
VirtualMachine vm = mInner.getVirtualMachineManager().get(instanceName);
final VmCdis vmCdis = new VmCdis();
final CompletableFuture<Exception> exception = new CompletableFuture<>();
@@ -245,7 +243,7 @@
public void onPayloadReady(VirtualMachine vm) {
try {
ITestService testService = ITestService.Stub.asInterface(
- vm.connectToVsockServer(ITestService.SERVICE_PORT).get());
+ vm.connectToVsockServer(ITestService.SERVICE_PORT));
vmCdis.cdiAttest = testService.insecurelyExposeAttestationCdi();
vmCdis.cdiSeal = testService.insecurelyExposeSealingCdi();
forceStop(vm);
@@ -264,15 +262,14 @@
"9.17/C-1-1",
"9.17/C-2-7"
})
- public void instancesOfSameVmHaveDifferentCdis()
- throws VirtualMachineException, InterruptedException {
+ public void instancesOfSameVmHaveDifferentCdis() throws Exception {
assume()
.withMessage("SKip on 5.4 kernel. b/218303240")
.that(KERNEL_VERSION)
.isNotEqualTo("5.4");
VirtualMachineConfig normalConfig = mInner.newVmConfigBuilder("assets/vm_config.json")
- .debugLevel(DebugLevel.FULL)
+ .setDebugLevel(DEBUG_LEVEL_FULL)
.build();
mInner.forceCreateNewVirtualMachine("test_vm_a", normalConfig);
mInner.forceCreateNewVirtualMachine("test_vm_b", normalConfig);
@@ -292,15 +289,14 @@
"9.17/C-1-1",
"9.17/C-2-7"
})
- public void sameInstanceKeepsSameCdis()
- throws VirtualMachineException, InterruptedException {
+ public void sameInstanceKeepsSameCdis() throws Exception {
assume()
.withMessage("SKip on 5.4 kernel. b/218303240")
.that(KERNEL_VERSION)
.isNotEqualTo("5.4");
VirtualMachineConfig normalConfig = mInner.newVmConfigBuilder("assets/vm_config.json")
- .debugLevel(DebugLevel.FULL)
+ .setDebugLevel(DEBUG_LEVEL_FULL)
.build();
mInner.forceCreateNewVirtualMachine("test_vm", normalConfig);
@@ -317,18 +313,16 @@
"9.17/C-1-1",
"9.17/C-2-7"
})
- public void bccIsSuperficiallyWellFormed()
- throws VirtualMachineException, InterruptedException, CborException {
+ public void bccIsSuperficiallyWellFormed() throws Exception {
assume()
.withMessage("SKip on 5.4 kernel. b/218303240")
.that(KERNEL_VERSION)
.isNotEqualTo("5.4");
VirtualMachineConfig normalConfig = mInner.newVmConfigBuilder("assets/vm_config.json")
- .debugLevel(DebugLevel.FULL)
+ .setDebugLevel(DEBUG_LEVEL_FULL)
.build();
VirtualMachine vm = mInner.forceCreateNewVirtualMachine("bcc_vm", normalConfig);
- final VmCdis vmCdis = new VmCdis();
final CompletableFuture<byte[]> bcc = new CompletableFuture<>();
final CompletableFuture<Exception> exception = new CompletableFuture<>();
VmEventListener listener =
@@ -337,7 +331,7 @@
public void onPayloadReady(VirtualMachine vm) {
try {
ITestService testService = ITestService.Stub.asInterface(
- vm.connectToVsockServer(ITestService.SERVICE_PORT).get());
+ vm.connectToVsockServer(ITestService.SERVICE_PORT));
bcc.complete(testService.getBcc());
forceStop(vm);
} catch (Exception e) {
@@ -398,10 +392,9 @@
file.writeByte(b ^ 1);
}
- private RandomAccessFile prepareInstanceImage(String vmName)
- throws VirtualMachineException, InterruptedException, IOException {
+ private RandomAccessFile prepareInstanceImage(String vmName) throws Exception {
VirtualMachineConfig config = mInner.newVmConfigBuilder("assets/vm_config.json")
- .debugLevel(DebugLevel.FULL)
+ .setDebugLevel(DEBUG_LEVEL_FULL)
.build();
mInner.forceCreateNewVirtualMachine(vmName, config);
@@ -413,8 +406,7 @@
return new RandomAccessFile(instanceImgPath, "rw");
}
- private void assertThatPartitionIsMissing(UUID partitionUuid)
- throws VirtualMachineException, InterruptedException, IOException {
+ private void assertThatPartitionIsMissing(UUID partitionUuid) throws Exception {
RandomAccessFile instanceFile = prepareInstanceImage("test_vm_integrity");
assertThat(findPartitionDataOffset(instanceFile, partitionUuid).isPresent())
.isFalse();
@@ -422,7 +414,7 @@
// Flips a bit of given partition, and then see if boot fails.
private void assertThatBootFailsAfterCompromisingPartition(UUID partitionUuid)
- throws VirtualMachineException, InterruptedException, IOException {
+ throws Exception {
RandomAccessFile instanceFile = prepareInstanceImage("test_vm_integrity");
OptionalLong offset = findPartitionDataOffset(instanceFile, partitionUuid);
assertThat(offset.isPresent()).isTrue();
@@ -433,7 +425,7 @@
assertThat(result.payloadStarted).isFalse();
// This failure should shut the VM down immediately and shouldn't trigger a hangup.
- assertThat(result.deathReason).isNotEqualTo(VirtualMachineCallback.DEATH_REASON_HANGUP);
+ assertThat(result.deathReason).isNotEqualTo(VirtualMachineCallback.STOP_REASON_HANGUP);
}
@Test
@@ -441,8 +433,7 @@
"9.17/C-1-1",
"9.17/C-2-7"
})
- public void bootFailsWhenMicrodroidDataIsCompromised()
- throws VirtualMachineException, InterruptedException, IOException {
+ public void bootFailsWhenMicrodroidDataIsCompromised() throws Exception {
assertThatBootFailsAfterCompromisingPartition(MICRODROID_PARTITION_UUID);
}
@@ -451,8 +442,7 @@
"9.17/C-1-1",
"9.17/C-2-7"
})
- public void bootFailsWhenPvmFwDataIsCompromised()
- throws VirtualMachineException, InterruptedException, IOException {
+ public void bootFailsWhenPvmFwDataIsCompromised() throws Exception {
if (mProtectedVm) {
assertThatBootFailsAfterCompromisingPartition(PVM_FW_PARTITION_UUID);
} else {
@@ -462,25 +452,23 @@
}
@Test
- public void bootFailsWhenConfigIsInvalid()
- throws VirtualMachineException, InterruptedException, IOException {
+ public void bootFailsWhenConfigIsInvalid() throws Exception {
VirtualMachineConfig.Builder builder =
mInner.newVmConfigBuilder("assets/vm_config_no_task.json");
- VirtualMachineConfig normalConfig = builder.debugLevel(DebugLevel.FULL).build();
+ VirtualMachineConfig normalConfig = builder.setDebugLevel(DEBUG_LEVEL_FULL).build();
mInner.forceCreateNewVirtualMachine("test_vm_invalid_config", normalConfig);
BootResult bootResult = tryBootVm(TAG, "test_vm_invalid_config");
assertThat(bootResult.payloadStarted).isFalse();
assertThat(bootResult.deathReason).isEqualTo(
- VirtualMachineCallback.DEATH_REASON_MICRODROID_INVALID_PAYLOAD_CONFIG);
+ VirtualMachineCallback.STOP_REASON_MICRODROID_INVALID_PAYLOAD_CONFIG);
}
@Test
- public void sameInstancesShareTheSameVmObject()
- throws VirtualMachineException, InterruptedException, IOException {
+ public void sameInstancesShareTheSameVmObject() throws Exception {
VirtualMachineConfig.Builder builder =
mInner.newVmConfigBuilder("assets/vm_config.json");
- VirtualMachineConfig normalConfig = builder.debugLevel(DebugLevel.NONE).build();
+ VirtualMachineConfig normalConfig = builder.setDebugLevel(DEBUG_LEVEL_NONE).build();
VirtualMachine vm = mInner.forceCreateNewVirtualMachine("test_vm", normalConfig);
VirtualMachine vm2 = mInner.getVirtualMachineManager().get("test_vm");
assertThat(vm).isEqualTo(vm2);