Merge "[cleanup] Remove unneeded microdroid_service_context in microdroid"
diff --git a/demo/Android.bp b/demo/Android.bp
index 2b234a6..a291ee1 100644
--- a/demo/Android.bp
+++ b/demo/Android.bp
@@ -12,14 +12,9 @@
"com.android.microdroid.testservice-java",
"com.google.android.material_material",
],
- libs: [
- // We need to compile against the .impl library which includes the hidden
- // APIs. Once the APIs are promoted to @SystemApi we can switch to
- // framework-virtualization, which contains API stubs.
- "framework-virtualization.impl",
- ],
+ sdk_version: "system_current",
jni_libs: ["MicrodroidTestNativeLib"],
- platform_apis: true,
+ jni_uses_platform_apis: true,
use_embedded_native_libs: true,
v4_signature: true,
min_sdk_version: "33",
diff --git a/javalib/api/system-current.txt b/javalib/api/system-current.txt
index d802177..ea2d23e 100644
--- a/javalib/api/system-current.txt
+++ b/javalib/api/system-current.txt
@@ -1 +1,109 @@
// Signature format: 2.0
+package android.system.virtualmachine {
+
+ public class VirtualMachine implements java.lang.AutoCloseable {
+ method public void clearCallback();
+ method public void close() throws android.system.virtualmachine.VirtualMachineException;
+ method @NonNull public android.os.IBinder connectToVsockServer(int) throws android.system.virtualmachine.VirtualMachineException;
+ method @NonNull public android.os.ParcelFileDescriptor connectVsock(int) throws android.system.virtualmachine.VirtualMachineException;
+ method public int getCid() throws android.system.virtualmachine.VirtualMachineException;
+ method @NonNull public android.system.virtualmachine.VirtualMachineConfig getConfig();
+ method @NonNull public java.io.InputStream getConsoleOutput() throws android.system.virtualmachine.VirtualMachineException;
+ method @NonNull public java.io.InputStream getLogOutput() throws android.system.virtualmachine.VirtualMachineException;
+ method @NonNull public String getName();
+ method public int getStatus();
+ method @RequiresPermission(android.system.virtualmachine.VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION) public void run() throws android.system.virtualmachine.VirtualMachineException;
+ method public void setCallback(@NonNull java.util.concurrent.Executor, @NonNull android.system.virtualmachine.VirtualMachineCallback);
+ method @NonNull public android.system.virtualmachine.VirtualMachineConfig setConfig(@NonNull android.system.virtualmachine.VirtualMachineConfig) throws android.system.virtualmachine.VirtualMachineException;
+ method public void stop() throws android.system.virtualmachine.VirtualMachineException;
+ method @NonNull public android.system.virtualmachine.VirtualMachineDescriptor toDescriptor() throws android.system.virtualmachine.VirtualMachineException;
+ field public static final String MANAGE_VIRTUAL_MACHINE_PERMISSION = "android.permission.MANAGE_VIRTUAL_MACHINE";
+ field public static final int STATUS_DELETED = 2; // 0x2
+ field public static final int STATUS_RUNNING = 1; // 0x1
+ field public static final int STATUS_STOPPED = 0; // 0x0
+ field public static final String USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION = "android.permission.USE_CUSTOM_VIRTUAL_MACHINE";
+ }
+
+ public interface VirtualMachineCallback {
+ method public void onError(@NonNull android.system.virtualmachine.VirtualMachine, int, @NonNull String);
+ method public void onPayloadFinished(@NonNull android.system.virtualmachine.VirtualMachine, int);
+ method public void onPayloadReady(@NonNull android.system.virtualmachine.VirtualMachine);
+ method public void onPayloadStarted(@NonNull android.system.virtualmachine.VirtualMachine);
+ method public void onRamdump(@NonNull android.system.virtualmachine.VirtualMachine, @NonNull android.os.ParcelFileDescriptor);
+ method public void onStopped(@NonNull android.system.virtualmachine.VirtualMachine, int);
+ field public static final int ERROR_PAYLOAD_CHANGED = 2; // 0x2
+ field public static final int ERROR_PAYLOAD_INVALID_CONFIG = 3; // 0x3
+ field public static final int ERROR_PAYLOAD_VERIFICATION_FAILED = 1; // 0x1
+ field public static final int ERROR_UNKNOWN = 0; // 0x0
+ field public static final int STOP_REASON_BOOTLOADER_INSTANCE_IMAGE_CHANGED = 10; // 0xa
+ field public static final int STOP_REASON_BOOTLOADER_PUBLIC_KEY_MISMATCH = 9; // 0x9
+ field public static final int STOP_REASON_CRASH = 6; // 0x6
+ field public static final int STOP_REASON_ERROR = 4; // 0x4
+ field public static final int STOP_REASON_HANGUP = 16; // 0x10
+ field public static final int STOP_REASON_INFRASTRUCTURE_ERROR = 0; // 0x0
+ field public static final int STOP_REASON_KILLED = 1; // 0x1
+ field public static final int STOP_REASON_MICRODROID_FAILED_TO_CONNECT_TO_VIRTUALIZATION_SERVICE = 11; // 0xb
+ field public static final int STOP_REASON_MICRODROID_INVALID_PAYLOAD_CONFIG = 14; // 0xe
+ field public static final int STOP_REASON_MICRODROID_PAYLOAD_HAS_CHANGED = 12; // 0xc
+ field public static final int STOP_REASON_MICRODROID_PAYLOAD_VERIFICATION_FAILED = 13; // 0xd
+ field public static final int STOP_REASON_MICRODROID_UNKNOWN_RUNTIME_ERROR = 15; // 0xf
+ field public static final int STOP_REASON_PVM_FIRMWARE_INSTANCE_IMAGE_CHANGED = 8; // 0x8
+ field public static final int STOP_REASON_PVM_FIRMWARE_PUBLIC_KEY_MISMATCH = 7; // 0x7
+ field public static final int STOP_REASON_REBOOT = 5; // 0x5
+ field public static final int STOP_REASON_SHUTDOWN = 3; // 0x3
+ field public static final int STOP_REASON_UNKNOWN = 2; // 0x2
+ field public static final int STOP_REASON_VIRTUALIZATION_SERVICE_DIED = -1; // 0xffffffff
+ }
+
+ public final class VirtualMachineConfig {
+ method @NonNull public String getApkPath();
+ method @NonNull public int getDebugLevel();
+ method @IntRange(from=0) public int getMemoryMib();
+ method @IntRange(from=1) public int getNumCpus();
+ method @Nullable public String getPayloadBinaryPath();
+ method @Nullable public String getPayloadConfigPath();
+ method public boolean isCompatibleWith(@NonNull android.system.virtualmachine.VirtualMachineConfig);
+ method public boolean isProtectedVm();
+ field public static final int DEBUG_LEVEL_APP_ONLY = 1; // 0x1
+ field public static final int DEBUG_LEVEL_FULL = 2; // 0x2
+ field public static final int DEBUG_LEVEL_NONE = 0; // 0x0
+ }
+
+ public static final class VirtualMachineConfig.Builder {
+ ctor public VirtualMachineConfig.Builder(@NonNull android.content.Context);
+ method @NonNull public android.system.virtualmachine.VirtualMachineConfig build();
+ method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setApkPath(@NonNull String);
+ method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setDebugLevel(int);
+ method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setMemoryMib(@IntRange(from=0) int);
+ method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setNumCpus(@IntRange(from=1) int);
+ method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setPayloadBinaryPath(@NonNull String);
+ method @NonNull @RequiresPermission(android.system.virtualmachine.VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION) public android.system.virtualmachine.VirtualMachineConfig.Builder setPayloadConfigPath(@NonNull String);
+ method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setProtectedVm(boolean);
+ }
+
+ public final class VirtualMachineDescriptor implements android.os.Parcelable {
+ method public int describeContents();
+ method public void writeToParcel(@NonNull android.os.Parcel, int);
+ field @NonNull public static final android.os.Parcelable.Creator<android.system.virtualmachine.VirtualMachineDescriptor> CREATOR;
+ }
+
+ public class VirtualMachineException extends java.lang.Exception {
+ ctor public VirtualMachineException(@Nullable String);
+ ctor public VirtualMachineException(@Nullable String, @Nullable Throwable);
+ ctor public VirtualMachineException(@Nullable Throwable);
+ }
+
+ public class VirtualMachineManager {
+ method @NonNull @RequiresPermission(android.system.virtualmachine.VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION) public android.system.virtualmachine.VirtualMachine create(@NonNull String, @NonNull android.system.virtualmachine.VirtualMachineConfig) throws android.system.virtualmachine.VirtualMachineException;
+ method public void delete(@NonNull String) throws android.system.virtualmachine.VirtualMachineException;
+ method @Nullable public android.system.virtualmachine.VirtualMachine get(@NonNull String) throws android.system.virtualmachine.VirtualMachineException;
+ method public int getCapabilities();
+ method @NonNull public static android.system.virtualmachine.VirtualMachineManager getInstance(@NonNull android.content.Context);
+ method @NonNull public android.system.virtualmachine.VirtualMachine getOrCreate(@NonNull String, @NonNull android.system.virtualmachine.VirtualMachineConfig) throws android.system.virtualmachine.VirtualMachineException;
+ method @NonNull public android.system.virtualmachine.VirtualMachine importFromDescriptor(@NonNull String, @NonNull android.system.virtualmachine.VirtualMachineDescriptor) throws android.system.virtualmachine.VirtualMachineException;
+ field public static final int CAPABILITY_NON_PROTECTED_VM = 2; // 0x2
+ field public static final int CAPABILITY_PROTECTED_VM = 1; // 0x1
+ }
+
+}
+
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index 4435576..193d213 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -48,6 +48,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.content.Context;
import android.os.Binder;
import android.os.IBinder;
@@ -94,14 +95,15 @@
/**
* Represents an VM instance, with its own configuration and state. Instances are persistent and are
* created or retrieved via {@link VirtualMachineManager}.
- * <p>
- * The {@link #run} method actually starts up the VM and allows the payload code to execute. It
- * will continue until it exits or {@link #stop} is called. Updates on the state of the VM can
- * be received using {@link #setCallback}. The app can communicate with the VM using
- * {@link #connectToVsockServer} or {@link #connectVsock}.
+ *
+ * <p>The {@link #run} method actually starts up the VM and allows the payload code to execute. It
+ * will continue until it exits or {@link #stop} is called. Updates on the state of the VM can be
+ * received using {@link #setCallback}. The app can communicate with the VM using {@link
+ * #connectToVsockServer} or {@link #connectVsock}.
*
* @hide
*/
+@SystemApi
public class VirtualMachine implements AutoCloseable {
/** Name of the directory under the files directory where all VMs created for the app exist. */
private static final String VM_DIR = "vm";
@@ -379,9 +381,8 @@
void delete(Context context, String name) throws VirtualMachineException {
synchronized (mLock) {
checkStopped();
+ deleteVmDirectory(context, name);
}
-
- deleteVmDirectory(context, name);
}
static void deleteVmDirectory(Context context, String name) throws VirtualMachineException {
@@ -425,6 +426,7 @@
*
* @hide
*/
+ @SystemApi
@NonNull
public String getName() {
return mName;
@@ -439,6 +441,7 @@
*
* @hide
*/
+ @SystemApi
@NonNull
public VirtualMachineConfig getConfig() {
synchronized (mLock) {
@@ -451,6 +454,7 @@
*
* @hide
*/
+ @SystemApi
@Status
public int getStatus() {
IVirtualMachine virtualMachine;
@@ -525,7 +529,9 @@
*
* @hide
*/
- public void setCallback(@NonNull @CallbackExecutor Executor executor,
+ @SystemApi
+ public void setCallback(
+ @NonNull @CallbackExecutor Executor executor,
@NonNull VirtualMachineCallback callback) {
synchronized (mCallbackLock) {
mCallback = callback;
@@ -538,6 +544,7 @@
*
* @hide
*/
+ @SystemApi
public void clearCallback() {
synchronized (mCallbackLock) {
mCallback = null;
@@ -571,9 +578,10 @@
* calling {@code run()}.
*
* @throws VirtualMachineException if the virtual machine is not stopped or could not be
- * started.
+ * started.
* @hide
*/
+ @SystemApi
@RequiresPermission(MANAGE_VIRTUAL_MACHINE_PERMISSION)
public void run() throws VirtualMachineException {
synchronized (mLock) {
@@ -717,6 +725,7 @@
* @throws VirtualMachineException if the stream could not be created.
* @hide
*/
+ @SystemApi
@NonNull
public InputStream getConsoleOutput() throws VirtualMachineException {
synchronized (mLock) {
@@ -731,6 +740,7 @@
* @throws VirtualMachineException if the stream could not be created.
* @hide
*/
+ @SystemApi
@NonNull
public InputStream getLogOutput() throws VirtualMachineException {
synchronized (mLock) {
@@ -742,12 +752,12 @@
/**
* 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 of the event. A stopped virtual machine can be re-started by calling {@link
- * #run()}.
+ * notified of the event. A stopped virtual machine can be re-started by calling {@link #run()}.
*
* @throws VirtualMachineException if the virtual machine could not be stopped.
* @hide
*/
+ @SystemApi
public void stop() throws VirtualMachineException {
synchronized (mLock) {
if (mVirtualMachine == null) {
@@ -770,6 +780,7 @@
* @throws VirtualMachineException if the virtual machine could not be stopped.
* @hide
*/
+ @SystemApi
@Override
public void close() throws VirtualMachineException {
stop();
@@ -802,6 +813,7 @@
* @throws VirtualMachineException if the virtual machine is not running.
* @hide
*/
+ @SystemApi
public int getCid() throws VirtualMachineException {
synchronized (mLock) {
try {
@@ -817,14 +829,15 @@
* like the number of CPU and size of the RAM, depending on the situation (e.g. the size of the
* application to run on the virtual machine, etc.)
*
- * The new config must be {@link VirtualMachineConfig#isCompatibleWith compatible with} the
+ * <p>The new config must be {@link VirtualMachineConfig#isCompatibleWith compatible with} the
* existing config.
*
* @return the old config
* @throws VirtualMachineException if the virtual machine is not stopped, or the new config is
- * incompatible.
+ * incompatible.
* @hide
*/
+ @SystemApi
@NonNull
public VirtualMachineConfig setConfig(@NonNull VirtualMachineConfig newConfig)
throws VirtualMachineException {
@@ -834,6 +847,10 @@
throw new VirtualMachineException("incompatible config");
}
checkStopped();
+
+ // Delete any existing file before recreating; that ensures any VirtualMachineDescriptor
+ // that refers to the old file does not see the new config.
+ mConfigFilePath.delete();
newConfig.serialize(mConfigFilePath);
mConfig = newConfig;
return oldConfig;
@@ -846,13 +863,14 @@
/**
* 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 a connection to the guest VM.
+ * VirtualMachineCallback#onPayloadReady(VirtualMachine)}, it can use this method to establish a
+ * connection to the guest VM.
*
* @throws VirtualMachineException if the virtual machine is not running or the connection
- * failed.
+ * failed.
* @hide
*/
+ @SystemApi
@NonNull
public IBinder connectToVsockServer(int port) throws VirtualMachineException {
synchronized (mLock) {
@@ -870,6 +888,7 @@
* @throws VirtualMachineException if connecting fails.
* @hide
*/
+ @SystemApi
@NonNull
public ParcelFileDescriptor connectVsock(int port) throws VirtualMachineException {
synchronized (mLock) {
@@ -887,22 +906,27 @@
* Captures the current state of the VM in a {@link VirtualMachineDescriptor} instance. The VM
* needs to be stopped to avoid inconsistency in its state representation.
*
+ * <p>The state of the VM is not actually copied until {@link
+ * VirtualMachineManager#importFromDescriptor} is called. It is recommended that the VM not be
+ * started until that operation is complete.
+ *
* @return a {@link VirtualMachineDescriptor} instance that represents the VM's state.
* @throws VirtualMachineException if the virtual machine is not stopped, or the state could not
* be captured.
* @hide
*/
+ @SystemApi
@NonNull
public VirtualMachineDescriptor toDescriptor() throws VirtualMachineException {
synchronized (mLock) {
checkStopped();
- }
- try {
- return new VirtualMachineDescriptor(
- ParcelFileDescriptor.open(mConfigFilePath, MODE_READ_ONLY),
- ParcelFileDescriptor.open(mInstanceFilePath, MODE_READ_ONLY));
- } catch (IOException e) {
- throw new VirtualMachineException(e);
+ try {
+ return new VirtualMachineDescriptor(
+ ParcelFileDescriptor.open(mConfigFilePath, MODE_READ_ONLY),
+ ParcelFileDescriptor.open(mInstanceFilePath, MODE_READ_ONLY));
+ } catch (IOException e) {
+ throw new VirtualMachineException(e);
+ }
}
}
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineCallback.java b/javalib/src/android/system/virtualmachine/VirtualMachineCallback.java
index 1f94a8b..f3c4831 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.SuppressLint;
+import android.annotation.SystemApi;
import android.os.ParcelFileDescriptor;
import java.lang.annotation.Retention;
@@ -30,7 +31,8 @@
*
* @hide
*/
-@SuppressLint("CallbackInterface") // Guidance has changed, lint is out of date (b/245552641)
+@SystemApi
+@SuppressLint("CallbackInterface") // Guidance has changed, lint is out of date (b/245552641)
public interface VirtualMachineCallback {
/** @hide */
@Retention(RetentionPolicy.SOURCE)
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
index a660306..8678b99 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -26,6 +26,7 @@
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
import android.content.Context;
import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
@@ -51,6 +52,7 @@
*
* @hide
*/
+@SystemApi
public final class VirtualMachineConfig {
// These define the schema of the config file persisted on disk.
private static final int VERSION = 2;
@@ -76,12 +78,12 @@
public @interface 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.
+ * 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 static final int DEBUG_LEVEL_NONE = 0;
+ @SystemApi 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
@@ -89,7 +91,7 @@
*
* @hide
*/
- public static final int DEBUG_LEVEL_APP_ONLY = 1;
+ @SystemApi public static final int DEBUG_LEVEL_APP_ONLY = 1;
/**
* Fully debuggable. All logs (both logcat and kernel message) are exported. All processes
@@ -97,7 +99,7 @@
*
* @hide
*/
- public static final int DEBUG_LEVEL_FULL = 2;
+ @SystemApi public static final int DEBUG_LEVEL_FULL = 2;
@DebugLevel private final int mDebugLevel;
@@ -270,28 +272,31 @@
*
* @hide
*/
+ @SystemApi
@NonNull
public String getApkPath() {
return mApkPath;
}
/**
- * Returns the path within the APK to the payload config file that defines software aspects
- * of the VM.
+ * Returns the path within the APK to the payload config file that defines software aspects of
+ * the VM.
*
* @hide
*/
+ @SystemApi // TODO(b/243512115): Switch back to @TestApi
@Nullable
public String getPayloadConfigPath() {
return mPayloadConfigPath;
}
/**
- * Returns the path within the {@code lib/<ABI>} directory of the APK to the payload binary
- * file that will be executed within the VM.
+ * Returns the path within the {@code lib/<ABI>} directory of the APK to the payload binary file
+ * that will be executed within the VM.
*
* @hide
*/
+ @SystemApi
@Nullable
public String getPayloadBinaryPath() {
return mPayloadBinaryPath;
@@ -302,6 +307,7 @@
*
* @hide
*/
+ @SystemApi
@NonNull
@DebugLevel
public int getDebugLevel() {
@@ -313,6 +319,7 @@
*
* @hide
*/
+ @SystemApi
public boolean isProtectedVm() {
return mProtectedVm;
}
@@ -322,6 +329,7 @@
*
* @hide
*/
+ @SystemApi
@IntRange(from = 0)
public int getMemoryMib() {
return mMemoryMib;
@@ -332,6 +340,7 @@
*
* @hide
*/
+ @SystemApi
@IntRange(from = 1)
public int getNumCpus() {
return mNumCpus;
@@ -345,6 +354,7 @@
*
* @hide
*/
+ @SystemApi
public boolean isCompatibleWith(@NonNull VirtualMachineConfig other) {
return this.mDebugLevel == other.mDebugLevel
&& this.mProtectedVm == other.mProtectedVm
@@ -397,6 +407,7 @@
*
* @hide
*/
+ @SystemApi
public static final class Builder {
private final Context mContext;
@Nullable private String mApkPath;
@@ -413,6 +424,7 @@
*
* @hide
*/
+ @SystemApi
public Builder(@NonNull Context context) {
mContext = requireNonNull(context, "context must not be null");
mDebugLevel = DEBUG_LEVEL_NONE;
@@ -424,6 +436,7 @@
*
* @hide
*/
+ @SystemApi
@NonNull
public VirtualMachineConfig build() {
String apkPath = (mApkPath == null) ? mContext.getPackageCodePath() : mApkPath;
@@ -443,6 +456,7 @@
*
* @hide
*/
+ @SystemApi
@NonNull
public Builder setApkPath(@NonNull String apkPath) {
mApkPath = requireNonNull(apkPath);
@@ -450,13 +464,14 @@
}
/**
- * Sets the path within the APK to the payload config file that defines software aspects
- * of the VM. The file is a JSON file; see
+ * Sets the path within the APK to the payload config file that defines software aspects of
+ * the VM. The file is a JSON file; see
* packages/modules/Virtualization/microdroid/payload/config/src/lib.rs for the format.
*
* @hide
*/
@RequiresPermission(VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION)
+ @SystemApi // TODO(b/243512115): Switch to @TestApi
@NonNull
public Builder setPayloadConfigPath(@NonNull String payloadConfigPath) {
mPayloadConfigPath = requireNonNull(payloadConfigPath);
@@ -469,6 +484,7 @@
*
* @hide
*/
+ @SystemApi
@NonNull
public Builder setPayloadBinaryPath(@NonNull String payloadBinaryPath) {
mPayloadBinaryPath = requireNonNull(payloadBinaryPath);
@@ -480,6 +496,7 @@
*
* @hide
*/
+ @SystemApi
@NonNull
public Builder setDebugLevel(@DebugLevel int debugLevel) {
mDebugLevel = debugLevel;
@@ -487,12 +504,13 @@
}
/**
- * Sets whether to protect the VM memory from the host. No default is provided, this
- * must be set explicitly.
+ * Sets whether to protect the VM memory from the host. No default is provided, this must be
+ * set explicitly.
*
* @see VirtualMachineManager#getCapabilities
* @hide
*/
+ @SystemApi
@NonNull
public Builder setProtectedVm(boolean protectedVm) {
mProtectedVm = protectedVm;
@@ -501,11 +519,12 @@
}
/**
- * Sets the amount of RAM to give the VM, in mebibytes. If zero or not explicitly set
- * than a default size will be used.
+ * Sets the amount of RAM to give the VM, in mebibytes. If zero or not explicitly set then a
+ * default size will be used.
*
* @hide
*/
+ @SystemApi
@NonNull
public Builder setMemoryMib(@IntRange(from = 0) int memoryMib) {
mMemoryMib = memoryMib;
@@ -513,11 +532,12 @@
}
/**
- * Sets the number of vCPUs in the VM. Defaults to 1. Cannot be more than the number of
- * real CPUs (as returned by {@link Runtime#availableProcessors()}).
+ * Sets the number of vCPUs in the VM. Defaults to 1. Cannot be more than the number of real
+ * CPUs (as returned by {@link Runtime#availableProcessors()}).
*
* @hide
*/
+ @SystemApi
@NonNull
public Builder setNumCpus(@IntRange(from = 1) int num) {
mNumCpus = num;
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineDescriptor.java b/javalib/src/android/system/virtualmachine/VirtualMachineDescriptor.java
index b51cbce..edaf5b4 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineDescriptor.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineDescriptor.java
@@ -19,6 +19,7 @@
import static java.util.Objects.requireNonNull;
import android.annotation.NonNull;
+import android.annotation.SystemApi;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
@@ -32,6 +33,7 @@
*
* @hide
*/
+@SystemApi
public final class VirtualMachineDescriptor implements Parcelable {
@NonNull private final ParcelFileDescriptor mConfigFd;
@NonNull private final ParcelFileDescriptor mInstanceImgFd;
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineException.java b/javalib/src/android/system/virtualmachine/VirtualMachineException.java
index 828775a..985eb70 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineException.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineException.java
@@ -17,12 +17,14 @@
package android.system.virtualmachine;
import android.annotation.Nullable;
+import android.annotation.SystemApi;
/**
* Exception thrown when operations on virtual machines fail.
*
* @hide
*/
+@SystemApi
public class VirtualMachineException extends Exception {
public VirtualMachineException(@Nullable String message) {
super(message);
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineManager.java b/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
index 0e96f43..a520ab4 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
@@ -23,6 +23,7 @@
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
import android.content.Context;
import android.sysprop.HypervisorProperties;
import android.util.ArrayMap;
@@ -37,16 +38,17 @@
/**
* Manages {@link VirtualMachine virtual machine} instances created by an app. Each instance is
- * created from a {@link VirtualMachineConfig configuration} that defines the shape of the VM
- * (RAM, CPUs), the code to execute within it, etc.
- * <p>
- * Each virtual machine instance is named; the configuration and related state of each is
+ * created from a {@link VirtualMachineConfig configuration} that defines the shape of the VM (RAM,
+ * CPUs), the code to execute within it, etc.
+ *
+ * <p>Each virtual machine instance is named; the configuration and related state of each is
* persisted in the app's private data directory and an instance can be retrieved given the name.
- * <p>
- * The app can then start, stop and otherwise interact with the VM.
+ *
+ * <p>The app can then start, stop and otherwise interact with the VM.
*
* @hide
*/
+@SystemApi
public class VirtualMachineManager {
/**
* A lock used to synchronize the creation of virtual machines. It protects {@link #mVmsByName},
@@ -95,8 +97,9 @@
*
* @hide
*/
+ @SystemApi
@NonNull
- @SuppressLint("ManagerLookup") // Optional API
+ @SuppressLint("ManagerLookup") // TODO(b/249093790): remove
public static VirtualMachineManager getInstance(@NonNull Context context) {
requireNonNull(context, "context must not be null");
synchronized (sInstances) {
@@ -117,6 +120,7 @@
* @see #CAPABILITY_NON_PROTECTED_VM
* @hide
*/
+ @SystemApi
@Capability
public int getCapabilities() {
@Capability int result = 0;
@@ -134,18 +138,18 @@
* 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.
*
- * 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
+ * <p>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 the VM cannot be created, or there is an existing VM with
- * the given name.
+ * the given name.
* @hide
*/
+ @SystemApi
@NonNull
@RequiresPermission(VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION)
- public VirtualMachine create(
- @NonNull String name, @NonNull VirtualMachineConfig config)
+ public VirtualMachine create(@NonNull String name, @NonNull VirtualMachineConfig config)
throws VirtualMachineException {
synchronized (sCreateLock) {
return createLocked(name, config);
@@ -169,6 +173,7 @@
* retrieved.
* @hide
*/
+ @SystemApi
@Nullable
public VirtualMachine get(@NonNull String name) throws VirtualMachineException {
synchronized (sCreateLock) {
@@ -199,6 +204,7 @@
* @hide
*/
@NonNull
+ @SystemApi
public VirtualMachine importFromDescriptor(
@NonNull String name, @NonNull VirtualMachineDescriptor vmDescriptor)
throws VirtualMachineException {
@@ -216,9 +222,9 @@
* @throws VirtualMachineException if the virtual machine could not be created or retrieved.
* @hide
*/
+ @SystemApi
@NonNull
- public VirtualMachine getOrCreate(
- @NonNull String name, @NonNull VirtualMachineConfig config)
+ public VirtualMachine getOrCreate(@NonNull String name, @NonNull VirtualMachineConfig config)
throws VirtualMachineException {
synchronized (sCreateLock) {
VirtualMachine vm = getLocked(name);
@@ -237,10 +243,11 @@
* with the same name is different from an already deleted virtual machine even if it has the
* same config.
*
- * @throws VirtualMachineException if the virtual machine does not exist, is not stopped,
- * or cannot be deleted.
+ * @throws VirtualMachineException if the virtual machine does not exist, is not stopped, or
+ * cannot be deleted.
* @hide
*/
+ @SystemApi
public void delete(@NonNull String name) throws VirtualMachineException {
synchronized (sCreateLock) {
VirtualMachine vm = getVmByName(name);
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index a53b401..a706dbe 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -25,9 +25,7 @@
use crate::instance::{ApexData, ApkData, InstanceDisk, MicrodroidData, RootHash};
use crate::vm_payload_service::register_vm_payload_service;
use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::ErrorCode::ErrorCode;
-use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::{
- IVirtualMachineService, VM_BINDER_SERVICE_PORT,
-};
+use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::IVirtualMachineService;
use android_system_virtualization_payload::aidl::android::system::virtualization::payload::IVmPayloadService::{
VM_APK_CONTENTS_PATH,
VM_PAYLOAD_SERVICE_SOCKET_NAME,
@@ -160,8 +158,11 @@
}
fn get_vms_rpc_binder() -> Result<Strong<dyn IVirtualMachineService>> {
- get_vsock_rpc_interface(VMADDR_CID_HOST, VM_BINDER_SERVICE_PORT as u32)
- .context("Cannot connect to RPC service")
+ // The host is running a VirtualMachineService for this VM on a port equal
+ // to the CID of this VM.
+ let port = vsock::get_local_cid().context("Could not determine local CID")?;
+ get_vsock_rpc_interface(VMADDR_CID_HOST, port)
+ .context("Could not connect to IVirtualMachineService")
}
fn main() -> Result<()> {
diff --git a/tests/benchmark/Android.bp b/tests/benchmark/Android.bp
index 1747183..10cdac5 100644
--- a/tests/benchmark/Android.bp
+++ b/tests/benchmark/Android.bp
@@ -16,16 +16,13 @@
"com.android.microdroid.testservice-java",
"truth-prebuilt",
],
- // We need to compile against the .impl library which includes the hidden
- // APIs. Once the APIs are promoted to @SystemApi we can switch to
- // framework-virtualization, which contains API stubs.
- libs: ["framework-virtualization.impl"],
jni_libs: [
"MicrodroidBenchmarkNativeLib",
"MicrodroidIdleNativeLib",
"libiovsock_host_jni",
],
- platform_apis: true,
+ jni_uses_platform_apis: true,
+ sdk_version: "test_current",
use_embedded_native_libs: true,
compile_multilib: "64",
}
diff --git a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
index 1996f4b..c47e915 100644
--- a/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
+++ b/tests/benchmark_hostside/java/android/avf/test/AVFHostTestCase.java
@@ -149,7 +149,9 @@
}
private void appStartupHelper(String launchIntentPackage) throws Exception {
- assumeTrue("Skip on non-protected VMs", isProtectedVmSupported());
+ assumeTrue(
+ "Skip on non-protected VMs",
+ ((TestDevice) getDevice()).supportsMicrodroid(/*protectedVm=*/ true));
StartupTimeMetricCollection mCollection =
new StartupTimeMetricCollection(getPackageName(launchIntentPackage), ROUND_COUNT);
diff --git a/tests/helper/Android.bp b/tests/helper/Android.bp
index 7473dab..61c5dcd 100644
--- a/tests/helper/Android.bp
+++ b/tests/helper/Android.bp
@@ -6,9 +6,7 @@
name: "MicrodroidTestHelper",
srcs: ["src/java/com/android/microdroid/test/common/*.java"],
host_supported: true,
- libs: [
- "framework-annotations-lib",
- ],
+ sdk_version: "system_current",
}
java_library_static {
@@ -20,8 +18,5 @@
"MicrodroidTestHelper",
"truth-prebuilt",
],
- // We need to compile against the .impl library which includes the hidden
- // APIs. Once the APIs are promoted to @SystemApi we can switch to
- // framework-virtualization, which contains API stubs.
- libs: ["framework-virtualization.impl"],
+ sdk_version: "system_current",
}
diff --git a/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java b/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java
index 1fc163b..94f7e99 100644
--- a/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java
+++ b/tests/helper/src/java/com/android/microdroid/test/common/DeviceProperties.java
@@ -18,15 +18,11 @@
import static java.util.Objects.requireNonNull;
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-
/** This class can be used in both host tests and device tests to get the device properties. */
public final class DeviceProperties {
/** PropertyGetter is used to get the property associated to a given key. */
public interface PropertyGetter {
- @Nullable
- String getProperty(@NonNull String key) throws Exception;
+ String getProperty(String key) throws Exception;
}
private static final String KEY_VENDOR_DEVICE = "ro.product.vendor.device";
@@ -34,15 +30,14 @@
private static final String CUTTLEFISH_DEVICE_PREFIX = "vsoc_";
- @NonNull private final PropertyGetter mPropertyGetter;
+ private final PropertyGetter mPropertyGetter;
- private DeviceProperties(@NonNull PropertyGetter propertyGetter) {
+ private DeviceProperties(PropertyGetter propertyGetter) {
mPropertyGetter = requireNonNull(propertyGetter);
}
/** Creates a new instance of {@link DeviceProperties}. */
- @NonNull
- public static DeviceProperties create(@NonNull PropertyGetter propertyGetter) {
+ public static DeviceProperties create(PropertyGetter propertyGetter) {
return new DeviceProperties(propertyGetter);
}
@@ -54,7 +49,6 @@
return vendorDeviceName != null && vendorDeviceName.startsWith(CUTTLEFISH_DEVICE_PREFIX);
}
- @Nullable
public String getMetricsTag() {
return getProperty(KEY_METRICS_TAG);
}
diff --git a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
index ac37ee0..e5aa908 100644
--- a/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
+++ b/tests/hostside/helper/java/com/android/microdroid/test/host/MicrodroidHostTestCaseBase.java
@@ -237,8 +237,4 @@
.stdoutTrimmed()
.isEqualTo("microdroid");
}
-
- public boolean isProtectedVmSupported() throws DeviceNotAvailableException {
- return getDevice().getBooleanProperty("ro.boot.hypervisor.protected_vm.supported", false);
- }
}
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index cffaae1..f0c89c9 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -51,7 +51,6 @@
import com.android.tradefed.util.xml.AbstractXmlParser;
import org.json.JSONArray;
-import org.json.JSONException;
import org.json.JSONObject;
import org.junit.After;
import org.junit.Before;
@@ -433,7 +432,9 @@
@CddTest(requirements = {"9.17/C-2-1", "9.17/C-2-2", "9.17/C-2-6"})
public void testBootFailsWhenProtectedVmStartsWithImagesSignedWithDifferentKey()
throws Exception {
- assumeTrue("Protected VMs are not supported", isProtectedVmSupported());
+ assumeTrue(
+ "Protected VMs are not supported",
+ getAndroidDevice().supportsMicrodroid(/*protectedVm=*/ true));
File key = findTestFile("test.com.android.virt.pem");
Map<String, File> keyOverrides = Map.of();
@@ -490,12 +491,12 @@
private boolean isTombstoneGeneratedWithConfig(String configPath) throws Exception {
// Note this test relies on logcat values being printed by tombstone_transmit on
// and the reeceiver on host (virtualization_service)
- mMicrodroidDevice = MicrodroidBuilder
- .fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
- .debugLevel("full")
- .memoryMib(minMemorySize())
- .numCpus(NUM_VCPUS)
- .build((TestDevice) getDevice());
+ mMicrodroidDevice =
+ MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
+ .debugLevel("full")
+ .memoryMib(minMemorySize())
+ .numCpus(NUM_VCPUS)
+ .build(getAndroidDevice());
mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
mMicrodroidDevice.enableAdbRoot();
@@ -546,7 +547,7 @@
ConfigUtils.uploadConfigForPushedAtoms(getDevice(), PACKAGE_NAME, atomIds);
// Create VM with microdroid
- TestDevice device = (TestDevice) getDevice();
+ TestDevice device = getAndroidDevice();
final String configPath = "assets/vm_config_apex.json"; // path inside the APK
ITestDevice microdroid =
MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
@@ -614,7 +615,7 @@
.debugLevel("full")
.memoryMib(minMemorySize())
.numCpus(NUM_VCPUS)
- .build((TestDevice) getDevice());
+ .build(getAndroidDevice());
mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
CommandRunner microdroid = new CommandRunner(mMicrodroidDevice);
@@ -676,12 +677,12 @@
@Test
public void testMicrodroidRamUsage() throws Exception {
final String configPath = "assets/vm_config.json";
- mMicrodroidDevice = MicrodroidBuilder
- .fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
- .debugLevel("full")
- .memoryMib(minMemorySize())
- .numCpus(NUM_VCPUS)
- .build((TestDevice) getDevice());
+ mMicrodroidDevice =
+ MicrodroidBuilder.fromDevicePath(getPathForPackage(PACKAGE_NAME), configPath)
+ .debugLevel("full")
+ .memoryMib(minMemorySize())
+ .numCpus(NUM_VCPUS)
+ .build(getAndroidDevice());
mMicrodroidDevice.waitForBootComplete(BOOT_COMPLETE_TIMEOUT);
mMicrodroidDevice.enableAdbRoot();
@@ -716,9 +717,10 @@
}
@Test
- public void testCustomVirtualMachinePermission()
- throws DeviceNotAvailableException, IOException, JSONException {
- assumeTrue("Protected VMs are not supported", isProtectedVmSupported());
+ public void testCustomVirtualMachinePermission() throws Exception {
+ assumeTrue(
+ "Protected VMs are not supported",
+ getAndroidDevice().supportsMicrodroid(/*protectedVm=*/ true));
CommandRunner android = new CommandRunner(getDevice());
// Pull etc/microdroid.json
@@ -767,7 +769,7 @@
@After
public void shutdown() throws Exception {
if (mMicrodroidDevice != null) {
- ((TestDevice) getDevice()).shutdownMicrodroid(mMicrodroidDevice);
+ getAndroidDevice().shutdownMicrodroid(mMicrodroidDevice);
}
cleanUpVirtualizationTestSetup(getDevice());
@@ -785,4 +787,10 @@
SHELL_PACKAGE_NAME,
"android.permission.USE_CUSTOM_VIRTUAL_MACHINE");
}
+
+ private TestDevice getAndroidDevice() {
+ TestDevice androidDevice = (TestDevice) getDevice();
+ assertThat(androidDevice).isNotNull();
+ return androidDevice;
+ }
}
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index 707dca1..df7c6c0 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -19,15 +19,12 @@
"truth-prebuilt",
"compatibility-common-util-devicesidelib",
],
- // We need to compile against the .impl library which includes the hidden
- // APIs. Once the APIs are promoted to @SystemApi we can switch to
- // framework-virtualization, which contains API stubs.
- libs: ["framework-virtualization.impl"],
+ sdk_version: "test_current",
jni_libs: [
"MicrodroidTestNativeLib",
"MicrodroidIdleNativeLib",
],
- platform_apis: true,
+ jni_uses_platform_apis: true,
use_embedded_native_libs: true,
// We only support 64-bit ABI, but CTS demands all APKs to be multi-ABI.
compile_multilib: "both",
diff --git a/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl b/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl
index f2d92af..3fdb48a 100644
--- a/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl
+++ b/virtualizationservice/aidl/android/system/virtualmachineservice/IVirtualMachineService.aidl
@@ -21,12 +21,6 @@
interface IVirtualMachineService {
/**
* Port number that VirtualMachineService listens on connections from the guest VMs for the
- * VirtualMachineService binder service.
- */
- const int VM_BINDER_SERVICE_PORT = 5000;
-
- /**
- * Port number that VirtualMachineService listens on connections from the guest VMs for the
* tombtones
*/
const int VM_TOMBSTONES_SERVICE_PORT = 2000;
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 578960c..040c0d8 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -16,7 +16,7 @@
use crate::atom::{write_vm_booted_stats, write_vm_creation_stats};
use crate::composite::make_composite_image;
-use crate::crosvm::{CrosvmConfig, DiskFile, PayloadState, VmInstance, VmState};
+use crate::crosvm::{CrosvmConfig, DiskFile, PayloadState, VmContext, VmInstance, VmState};
use crate::payload::{add_microdroid_payload_images, add_microdroid_system_images};
use crate::selinux::{getfilecon, SeContext};
use android_os_permissions_aidl::aidl::android::os::IPermissionController;
@@ -41,22 +41,22 @@
IVirtualizationServiceInternal::{BnVirtualizationServiceInternal, IVirtualizationServiceInternal},
};
use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::{
- BnVirtualMachineService, IVirtualMachineService, VM_BINDER_SERVICE_PORT,
- VM_TOMBSTONES_SERVICE_PORT,
+ BnVirtualMachineService, IVirtualMachineService, VM_TOMBSTONES_SERVICE_PORT,
};
use anyhow::{anyhow, bail, Context, Result};
use apkverify::{HashAlgorithm, V4Signature};
use binder::{
self, BinderFeatures, ExceptionCode, Interface, LazyServiceGuard, ParcelFileDescriptor,
- SpIBinder, Status, StatusCode, Strong, ThreadState,
+ Status, StatusCode, Strong, ThreadState,
};
use disk::QcowFile;
use libc::VMADDR_CID_HOST;
use log::{debug, error, info, warn};
use microdroid_payload_config::{OsConfig, Task, TaskType, VmPayloadConfig};
-use rpcbinder::run_vsock_rpc_server_with_factory;
+use rpcbinder::RpcServer;
use rustutils::system_properties;
use semver::VersionReq;
+use std::collections::HashMap;
use std::convert::TryInto;
use std::ffi::CStr;
use std::fs::{create_dir, File, OpenOptions};
@@ -80,7 +80,8 @@
/// The first CID to assign to a guest VM managed by the VirtualizationService. CIDs lower than this
/// are reserved for the host or other usage.
-const FIRST_GUEST_CID: Cid = 10;
+const GUEST_CID_MIN: Cid = 2048;
+const GUEST_CID_MAX: Cid = 65535;
const SYSPROP_LAST_CID: &str = "virtualizationservice.state.last_cid";
@@ -100,6 +101,19 @@
const UNFORMATTED_STORAGE_MAGIC: &str = "UNFORMATTED-STORAGE";
+fn is_valid_guest_cid(cid: Cid) -> bool {
+ (GUEST_CID_MIN..=GUEST_CID_MAX).contains(&cid)
+}
+
+fn next_guest_cid(cid: Cid) -> Cid {
+ assert!(is_valid_guest_cid(cid));
+ if cid == GUEST_CID_MAX {
+ GUEST_CID_MIN
+ } else {
+ cid + 1
+ }
+}
+
/// Singleton service for allocating globally-unique VM resources, such as the CID, and running
/// singleton servers, like tombstone receiver.
#[derive(Debug, Default)]
@@ -136,25 +150,65 @@
/// The mutable state of the VirtualizationServiceInternal. There should only be one instance
/// of this struct.
#[derive(Debug, Default)]
-struct GlobalState {}
+struct GlobalState {
+ /// CIDs currently allocated to running VMs. A CID is never recycled as long
+ /// as there is a strong reference held by a GlobalVmContext.
+ held_cids: HashMap<Cid, Weak<Cid>>,
+}
impl GlobalState {
/// Get the next available CID, or an error if we have run out. The last CID used is stored in
/// a system property so that restart of virtualizationservice doesn't reuse CID while the host
/// Android is up.
- fn allocate_cid(&mut self) -> Result<Cid> {
- let cid = match system_properties::read(SYSPROP_LAST_CID)? {
- Some(val) => match val.parse::<Cid>() {
- Ok(num) => num.checked_add(1).ok_or_else(|| anyhow!("ran out of CIDs"))?,
+ fn allocate_cid(&mut self) -> Result<Arc<Cid>> {
+ // Garbage collect unused CIDs.
+ self.held_cids.retain(|_, cid| cid.strong_count() > 0);
+
+ // Start trying to find a CID from the last used CID + 1. This ensures
+ // that we do not eagerly recycle CIDs. It makes debugging easier but
+ // also means that retrying to allocate a CID, eg. because it is
+ // erroneously occupied by a process, will not recycle the same CID.
+ let last_cid_prop =
+ system_properties::read(SYSPROP_LAST_CID)?.and_then(|val| match val.parse::<Cid>() {
+ Ok(num) => {
+ if is_valid_guest_cid(num) {
+ Some(num)
+ } else {
+ error!("Invalid value '{}' of property '{}'", num, SYSPROP_LAST_CID);
+ None
+ }
+ }
Err(_) => {
error!("Invalid value '{}' of property '{}'", val, SYSPROP_LAST_CID);
- FIRST_GUEST_CID
+ None
}
- },
- None => FIRST_GUEST_CID,
+ });
+
+ let first_cid = if let Some(last_cid) = last_cid_prop {
+ next_guest_cid(last_cid)
+ } else {
+ GUEST_CID_MIN
};
- system_properties::write(SYSPROP_LAST_CID, &format!("{}", cid))?;
- Ok(cid)
+
+ let cid = self
+ .find_available_cid(first_cid..=GUEST_CID_MAX)
+ .or_else(|| self.find_available_cid(GUEST_CID_MIN..first_cid));
+
+ if let Some(cid) = cid {
+ let cid_arc = Arc::new(cid);
+ self.held_cids.insert(cid, Arc::downgrade(&cid_arc));
+ system_properties::write(SYSPROP_LAST_CID, &format!("{}", cid))?;
+ Ok(cid_arc)
+ } else {
+ Err(anyhow!("Could not find an available CID."))
+ }
+ }
+
+ fn find_available_cid<I>(&self, mut range: I) -> Option<Cid>
+ where
+ I: Iterator<Item = Cid>,
+ {
+ range.find(|cid| !self.held_cids.contains_key(cid))
}
}
@@ -162,14 +216,14 @@
#[derive(Debug, Default)]
struct GlobalVmContext {
/// The unique CID assigned to the VM for vsock communication.
- cid: Cid,
- /// Keeps our service process running as long as this VM instance exists.
+ cid: Arc<Cid>,
+ /// Keeps our service process running as long as this VM context exists.
#[allow(dead_code)]
lazy_service_guard: LazyServiceGuard,
}
impl GlobalVmContext {
- fn create(cid: Cid) -> Strong<dyn IGlobalVmContext> {
+ fn create(cid: Arc<Cid>) -> Strong<dyn IGlobalVmContext> {
let binder = GlobalVmContext { cid, ..Default::default() };
BnGlobalVmContext::new_binder(binder, BinderFeatures::default())
}
@@ -179,7 +233,7 @@
impl IGlobalVmContext for GlobalVmContext {
fn getCid(&self) -> binder::Result<i32> {
- Ok(self.cid as i32)
+ Ok(*self.cid as i32)
}
}
@@ -341,8 +395,10 @@
}
fn handle_stream_connection_tombstoned() -> Result<()> {
+ // Should not listen for tombstones on a guest VM's port.
+ assert!(!is_valid_guest_cid(VM_TOMBSTONES_SERVICE_PORT as Cid));
let listener =
- VsockListener::bind_with_cid_port(VMADDR_CID_HOST, VM_TOMBSTONES_SERVICE_PORT as u32)?;
+ VsockListener::bind_with_cid_port(VMADDR_CID_HOST, VM_TOMBSTONES_SERVICE_PORT as Cid)?;
for incoming_stream in listener.incoming() {
let mut incoming_stream = match incoming_stream {
Err(e) => {
@@ -394,22 +450,31 @@
let global_service =
BnVirtualizationServiceInternal::new_binder(global_service, BinderFeatures::default());
- let service = VirtualizationService { global_service, state: Default::default() };
+ VirtualizationService { global_service, state: Default::default() }
+ }
- // binder server for vm
- // reference to state (not the state itself) is copied
- let state = service.state.clone();
- std::thread::spawn(move || {
- debug!("VirtualMachineService is starting as an RPC service.");
- if run_vsock_rpc_server_with_factory(VM_BINDER_SERVICE_PORT as u32, |cid| {
- VirtualMachineService::factory(cid, &state)
- }) {
- debug!("RPC server has shut down gracefully");
- } else {
- panic!("Premature termination of RPC server");
+ fn create_vm_context(&self) -> Result<(VmContext, Cid)> {
+ const NUM_ATTEMPTS: usize = 5;
+
+ for _ in 0..NUM_ATTEMPTS {
+ let global_context = self.global_service.allocateGlobalVmContext()?;
+ let cid = global_context.getCid()? as Cid;
+ let service = VirtualMachineService::new_binder(self.state.clone(), cid).as_binder();
+
+ // Start VM service listening for connections from the new CID on port=CID.
+ // TODO(b/245727626): Only accept connections from the new VM.
+ let port = cid;
+ match RpcServer::new_vsock(service, port) {
+ Ok(vm_server) => {
+ vm_server.start();
+ return Ok((VmContext::new(global_context, vm_server), cid));
+ }
+ Err(err) => {
+ warn!("Could not start RpcServer on port {}: {}", port, err);
+ }
}
- });
- service
+ }
+ bail!("Too many attempts to create VM context failed.");
}
fn create_vm_internal(
@@ -435,8 +500,13 @@
check_use_custom_virtual_machine()?;
}
- let vm_context = self.global_service.allocateGlobalVmContext()?;
- let cid = vm_context.getCid()? as Cid;
+ let (vm_context, cid) = self.create_vm_context().map_err(|e| {
+ error!("Failed to create VmContext: {:?}", e);
+ Status::new_service_specific_error_str(
+ -1,
+ Some(format!("Failed to create VmContext: {:?}", e)),
+ )
+ })?;
let state = &mut *self.state.lock().unwrap();
let console_fd = console_fd.map(clone_file).transpose()?;
@@ -1188,17 +1258,6 @@
}
impl VirtualMachineService {
- fn factory(cid: Cid, state: &Arc<Mutex<State>>) -> Option<SpIBinder> {
- if let Some(vm) = state.lock().unwrap().get_vm(cid) {
- let mut vm_service = vm.vm_service.lock().unwrap();
- let service = vm_service.get_or_insert_with(|| Self::new_binder(state.clone(), cid));
- Some(service.as_binder())
- } else {
- error!("connection from cid={} is not from a guest VM", cid);
- None
- }
- }
-
fn new_binder(state: Arc<Mutex<State>>, cid: Cid) -> Strong<dyn IVirtualMachineService> {
BnVirtualMachineService::new_binder(
VirtualMachineService { state, cid },
diff --git a/virtualizationservice/src/crosvm.rs b/virtualizationservice/src/crosvm.rs
index 68324c5..76e18db 100644
--- a/virtualizationservice/src/crosvm.rs
+++ b/virtualizationservice/src/crosvm.rs
@@ -42,6 +42,7 @@
use binder::Strong;
use android_system_virtualmachineservice::aidl::android::system::virtualmachineservice::IVirtualMachineService::IVirtualMachineService;
use tombstoned_client::{TombstonedConnection, DebuggerdDumpType};
+use rpcbinder::RpcServer;
/// external/crosvm
use base::UnixSeqpacketListener;
@@ -198,14 +199,30 @@
}
}
+/// Internal struct that holds the handles to globally unique resources of a VM.
+#[derive(Debug)]
+pub struct VmContext {
+ #[allow(dead_code)] // Keeps the global context alive
+ global_context: Strong<dyn IGlobalVmContext>,
+ #[allow(dead_code)] // Keeps the server alive
+ vm_server: RpcServer,
+}
+
+impl VmContext {
+ /// Construct new VmContext.
+ pub fn new(global_context: Strong<dyn IGlobalVmContext>, vm_server: RpcServer) -> VmContext {
+ VmContext { global_context, vm_server }
+ }
+}
+
/// Information about a particular instance of a VM which may be running.
#[derive(Debug)]
pub struct VmInstance {
/// The current state of the VM.
pub vm_state: Mutex<VmState>,
- /// Handle to global resources allocated for this VM.
- #[allow(dead_code)] // The handle is never read, we only need to hold it.
- vm_context: Strong<dyn IGlobalVmContext>,
+ /// Global resources allocated for this VM.
+ #[allow(dead_code)] // Keeps the context alive
+ vm_context: VmContext,
/// The CID assigned to the VM for vsock communication.
pub cid: Cid,
/// The name of the VM.
@@ -238,7 +255,7 @@
temporary_directory: PathBuf,
requester_uid: u32,
requester_debug_pid: i32,
- vm_context: Strong<dyn IGlobalVmContext>,
+ vm_context: VmContext,
) -> Result<VmInstance, Error> {
validate_config(&config)?;
let cid = config.cid;
diff --git a/vm_payload/Android.bp b/vm_payload/Android.bp
index dd2a937..6be6f22 100644
--- a/vm_payload/Android.bp
+++ b/vm_payload/Android.bp
@@ -17,6 +17,7 @@
"liblibc",
"liblog_rust",
"librpcbinder_rs",
+ "libvsock",
],
apex_available: [
"com.android.compos",