Merge "Remove CAP_IPC_LOCK from crosvm, use CAP_SYS_RESOURCE on VS"
diff --git a/apex/Android.bp b/apex/Android.bp
index 52f4384..e0ca9bf 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -123,6 +123,8 @@
// deapexer
"deapexer",
"debugfs_static",
+ "blkid",
+ "fsck.erofs",
// sign_virt_apex
"avbtool",
diff --git a/apex/sign_virt_apex_test.sh b/apex/sign_virt_apex_test.sh
index 640a3d4..03a56ca 100644
--- a/apex/sign_virt_apex_test.sh
+++ b/apex/sign_virt_apex_test.sh
@@ -23,8 +23,11 @@
# To access host tools
PATH=$TEST_DIR:$PATH
DEBUGFS=$TEST_DIR/debugfs_static
+BLKID=$TEST_DIR/blkid
+FSCKEROFS=$TEST_DIR/fsck.erofs
-deapexer --debugfs_path $DEBUGFS extract $TEST_DIR/com.android.virt.apex $TMP_ROOT
+deapexer --debugfs_path $DEBUGFS --blkid_path $BLKID --fsckerofs_path $FSCKEROFS \
+ extract $TEST_DIR/com.android.virt.apex $TMP_ROOT
if [ "$(ls -A $TMP_ROOT/etc/fs/)" ]; then
sign_virt_apex $TEST_DIR/test.com.android.virt.pem $TMP_ROOT
diff --git a/compos/tests/AndroidTest.xml b/compos/tests/AndroidTest.xml
index 2a84291..f9e6837 100644
--- a/compos/tests/AndroidTest.xml
+++ b/compos/tests/AndroidTest.xml
@@ -14,6 +14,8 @@
limitations under the License.
-->
<configuration description="Tests for CompOS">
+ <option name="config-descriptor:metadata" key="mainline-param" value="com.google.android.art.apex" />
+
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
<option name="force-root" value="true" />
</target_preparer>
diff --git a/javalib/src/android/system/virtualmachine/ParcelVirtualMachine.java b/javalib/src/android/system/virtualmachine/ParcelVirtualMachine.java
new file mode 100644
index 0000000..808f30a
--- /dev/null
+++ b/javalib/src/android/system/virtualmachine/ParcelVirtualMachine.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.system.virtualmachine;
+
+import static java.util.Objects.requireNonNull;
+
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * A parcelable that captures the state of a Virtual Machine.
+ *
+ * <p>You can capture the current state of VM by creating an instance of this class with {@link
+ * VirtualMachine#toParcelVirtualMachine()}, optionally pass it to another App, and then build an
+ * identical VM with the parcel received.
+ *
+ * @hide
+ */
+public final class ParcelVirtualMachine implements Parcelable {
+ private final @NonNull ParcelFileDescriptor mConfigFd;
+ private final @NonNull ParcelFileDescriptor mInstanceImgFd;
+ // TODO(b/243129654): Add trusted storage fd once it is available.
+
+ @Override
+ public int describeContents() {
+ return CONTENTS_FILE_DESCRIPTOR;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ mConfigFd.writeToParcel(out, flags);
+ mInstanceImgFd.writeToParcel(out, flags);
+ }
+
+ public static final Parcelable.Creator<ParcelVirtualMachine> CREATOR =
+ new Parcelable.Creator<ParcelVirtualMachine>() {
+ public ParcelVirtualMachine createFromParcel(Parcel in) {
+ return new ParcelVirtualMachine(in);
+ }
+
+ public ParcelVirtualMachine[] newArray(int size) {
+ return new ParcelVirtualMachine[size];
+ }
+ };
+
+ /**
+ * @return File descriptor of the VM configuration file config.xml.
+ * @hide
+ */
+ @VisibleForTesting
+ public @NonNull ParcelFileDescriptor getConfigFd() {
+ return mConfigFd;
+ }
+
+ /**
+ * @return File descriptor of the instance.img of the VM.
+ * @hide
+ */
+ @VisibleForTesting
+ public @NonNull ParcelFileDescriptor getInstanceImgFd() {
+ return mInstanceImgFd;
+ }
+
+ ParcelVirtualMachine(
+ @NonNull ParcelFileDescriptor configFd, @NonNull ParcelFileDescriptor instanceImgFd) {
+ mConfigFd = configFd;
+ mInstanceImgFd = instanceImgFd;
+ }
+
+ private ParcelVirtualMachine(Parcel in) {
+ mConfigFd = requireNonNull(in.readFileDescriptor());
+ mInstanceImgFd = requireNonNull(in.readFileDescriptor());
+ }
+}
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachine.java b/javalib/src/android/system/virtualmachine/VirtualMachine.java
index b026fe3..e750ae9 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachine.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachine.java
@@ -901,6 +901,28 @@
}
}
+ /**
+ * Captures the current state of the VM in a {@link ParcelVirtualMachine} instance.
+ * The VM needs to be stopped to avoid inconsistency in its state representation.
+ *
+ * @return a {@link ParcelVirtualMachine} instance that represents the VM's state.
+ * @throws VirtualMachineException if the virtual machine is not stopped, or the state could not
+ * be captured.
+ */
+ @NonNull
+ public ParcelVirtualMachine toParcelVirtualMachine() throws VirtualMachineException {
+ synchronized (mLock) {
+ checkStopped();
+ }
+ try {
+ return new ParcelVirtualMachine(
+ ParcelFileDescriptor.open(mConfigFilePath, MODE_READ_ONLY),
+ ParcelFileDescriptor.open(mInstanceFilePath, MODE_READ_ONLY));
+ } catch (IOException e) {
+ throw new VirtualMachineException(e);
+ }
+ }
+
@VirtualMachineCallback.ErrorCode
private int getTranslatedError(int reason) {
switch (reason) {
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 42c0e64..dd01867 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -25,9 +25,11 @@
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
+import android.content.Context;
import android.os.Build;
import android.os.ParcelFileDescriptor;
import android.os.SystemProperties;
+import android.system.virtualmachine.ParcelVirtualMachine;
import android.system.virtualmachine.VirtualMachine;
import android.system.virtualmachine.VirtualMachineCallback;
import android.system.virtualmachine.VirtualMachineConfig;
@@ -35,6 +37,8 @@
import android.system.virtualmachine.VirtualMachineManager;
import android.util.Log;
+import androidx.test.core.app.ApplicationProvider;
+
import com.android.compatibility.common.util.CddTest;
import com.android.microdroid.test.device.MicrodroidDeviceTestBase;
import com.android.microdroid.testservice.ITestService;
@@ -54,6 +58,8 @@
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
import java.util.List;
import java.util.OptionalLong;
import java.util.UUID;
@@ -289,8 +295,7 @@
// Try to run the VM again with the previous instance.img
// We need to make sure that no changes on config don't invalidate the identity, to compare
// the result with the below "different debug level" test.
- File vmRoot = new File(getContext().getDataDir(), "vm");
- File vmInstance = new File(new File(vmRoot, "test_vm"), "instance.img");
+ File vmInstance = getVmFile("test_vm", "instance.img");
File vmInstanceBackup = File.createTempFile("instance", ".img");
Files.copy(vmInstance.toPath(), vmInstanceBackup.toPath(), REPLACE_EXISTING);
mInner.forceCreateNewVirtualMachine("test_vm", normalConfig);
@@ -476,10 +481,7 @@
mInner.forceCreateNewVirtualMachine(vmName, config);
assertThat(tryBootVm(TAG, vmName).payloadStarted).isTrue();
-
- File vmRoot = new File(getContext().getDataDir(), "vm");
- File vmDir = new File(vmRoot, vmName);
- File instanceImgPath = new File(vmDir, "instance.img");
+ File instanceImgPath = getVmFile(vmName, "instance.img");
return new RandomAccessFile(instanceImgPath, "rw");
}
@@ -575,6 +577,41 @@
assertThat(vm).isNotEqualTo(newVm);
}
+ @Test
+ public void vmConvertsToValidParcelVm() throws Exception {
+ // Arrange
+ VirtualMachineConfig config =
+ mInner.newVmConfigBuilder()
+ .setPayloadBinaryPath("MicrodroidTestNativeLib.so")
+ .setDebugLevel(DEBUG_LEVEL_NONE)
+ .build();
+ String vmName = "test_vm";
+ VirtualMachine vm = mInner.forceCreateNewVirtualMachine(vmName, config);
+
+ // Action
+ ParcelVirtualMachine parcelVm = vm.toParcelVirtualMachine();
+
+ // Asserts
+ assertFileContentsAreEqual(parcelVm.getConfigFd(), vmName, "config.xml");
+ assertFileContentsAreEqual(parcelVm.getInstanceImgFd(), vmName, "instance.img");
+ }
+
+ private void assertFileContentsAreEqual(
+ ParcelFileDescriptor parcelFd, String vmName, String fileName) throws IOException {
+ File file = getVmFile(vmName, fileName);
+ // Use try-with-resources to close the files automatically after assert.
+ try (FileInputStream input1 = new FileInputStream(parcelFd.getFileDescriptor());
+ FileInputStream input2 = new FileInputStream(file)) {
+ assertThat(input1.readAllBytes()).isEqualTo(input2.readAllBytes());
+ }
+ }
+
+ private File getVmFile(String vmName, String fileName) {
+ Context context = ApplicationProvider.getApplicationContext();
+ Path filePath = Paths.get(context.getDataDir().getPath(), "vm", vmName, fileName);
+ return filePath.toFile();
+ }
+
private int minMemoryRequired() {
if (Build.SUPPORTED_ABIS.length > 0) {
String primaryAbi = Build.SUPPORTED_ABIS[0];