Merge "Let fd_server return errno as service specific error"
diff --git a/TEST_MAPPING b/TEST_MAPPING
index b805d03..69f5518 100644
--- a/TEST_MAPPING
+++ b/TEST_MAPPING
@@ -10,6 +10,11 @@
"name": "VirtualizationTestCases"
}
],
+ "postsubmit": [
+ {
+ "name": "MicrodroidTestApp"
+ }
+ ],
"imports": [
{
"path": "packages/modules/Virtualization/apkdmverity"
diff --git a/compos/common/compos_client.rs b/compos/common/compos_client.rs
index af504a1..508423b 100644
--- a/compos/common/compos_client.rs
+++ b/compos/common/compos_client.rs
@@ -85,12 +85,15 @@
.context("Failed to open config APK idsig file")?;
let idsig_fd = ParcelFileDescriptor::new(idsig_fd);
- // Console output and the system log output from the VM are redirected to this file.
- // TODO: Send this to stdout instead? Or specify None?
- let log_fd = File::create(data_dir.join("vm.log")).context("Failed to create log file")?;
- let log_fd = ParcelFileDescriptor::new(log_fd);
-
- let debug_level = if parameters.debug_mode { DebugLevel::FULL } else { DebugLevel::NONE };
+ let (log_fd, debug_level) = if parameters.debug_mode {
+ // Console output and the system log output from the VM are redirected to this file.
+ let log_fd =
+ File::create(data_dir.join("vm.log")).context("Failed to create log file")?;
+ let log_fd = ParcelFileDescriptor::new(log_fd);
+ (Some(log_fd), DebugLevel::FULL)
+ } else {
+ (None, DebugLevel::NONE)
+ };
let config = VirtualMachineConfig::AppConfig(VirtualMachineAppConfig {
apk: Some(apk_fd),
@@ -102,7 +105,7 @@
});
let vm = service
- .createVm(&config, Some(&log_fd), Some(&log_fd))
+ .createVm(&config, log_fd.as_ref(), log_fd.as_ref())
.context("Failed to create VM")?;
let vm_state = Arc::new(VmStateMonitor::default());
diff --git a/compos/compos_key_cmd/compos_key_cmd.cpp b/compos/compos_key_cmd/compos_key_cmd.cpp
index 2735f2e..3f431da 100644
--- a/compos/compos_key_cmd/compos_key_cmd.cpp
+++ b/compos/compos_key_cmd/compos_key_cmd.cpp
@@ -236,7 +236,7 @@
appConfig.idsig = std::move(idsigFd);
appConfig.instanceImage = std::move(instanceFd);
appConfig.configPath = kConfigFilePath;
- appConfig.debugLevel = VirtualMachineAppConfig::DebugLevel::NONE;
+ appConfig.debugLevel = VirtualMachineAppConfig::DebugLevel::FULL;
appConfig.memoryMib = 0; // Use default
LOG(INFO) << "Starting VM";
diff --git a/compos/composd/src/instance_starter.rs b/compos/composd/src/instance_starter.rs
index 3959859..4b3ac1b 100644
--- a/compos/composd/src/instance_starter.rs
+++ b/compos/composd/src/instance_starter.rs
@@ -179,6 +179,7 @@
) -> Result<()> {
let instance_image = fs::OpenOptions::new()
.create(true)
+ .truncate(true)
.read(true)
.write(true)
.open(&self.instance_image)
diff --git a/tests/testapk/Android.bp b/tests/testapk/Android.bp
index 493fc93..32c47dd 100644
--- a/tests/testapk/Android.bp
+++ b/tests/testapk/Android.bp
@@ -6,7 +6,10 @@
name: "MicrodroidTestApp",
test_suites: ["device-tests"],
srcs: ["src/java/**/*.java"],
- static_libs: ["androidx.test.runner"],
+ static_libs: [
+ "androidx.test.runner",
+ "androidx.test.ext.junit",
+ ],
libs: ["android.system.virtualmachine"],
jni_libs: ["MicrodroidTestNativeLib"],
platform_apis: true,
diff --git a/tests/testapk/AndroidManifest.xml b/tests/testapk/AndroidManifest.xml
index 21abeb5..bc955d2 100644
--- a/tests/testapk/AndroidManifest.xml
+++ b/tests/testapk/AndroidManifest.xml
@@ -15,8 +15,9 @@
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.microdroid.test">
+ <uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
<application>
- <uses-library android:name="android.system.virtualmachine" android:required="true" />
+ <uses-library android:name="android.system.virtualmachine" android:required="false" />
</application>
<instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.microdroid.test"
diff --git a/tests/testapk/AndroidTest.xml b/tests/testapk/AndroidTest.xml
index 25b1001..c7097db 100644
--- a/tests/testapk/AndroidTest.xml
+++ b/tests/testapk/AndroidTest.xml
@@ -17,8 +17,15 @@
<target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
<option name="test-file-name" value="MicrodroidTestApp.apk" />
</target_preparer>
+ <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
+ <option
+ name="run-command"
+ value="pm grant com.android.microdroid.test android.permission.MANAGE_VIRTUAL_MACHINE" />
+ </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.microdroid.test" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="shell-timeout" value="300000" />
+ <option name="test-timeout" value="300000" />
</test>
</configuration>
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 5e465d5..8ff2127 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -16,15 +16,164 @@
package com.android.microdroid.test;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeNoException;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.ParcelFileDescriptor;
+import android.system.virtualmachine.VirtualMachine;
+import android.system.virtualmachine.VirtualMachineCallback;
+import android.system.virtualmachine.VirtualMachineConfig;
+import android.system.virtualmachine.VirtualMachineException;
+import android.system.virtualmachine.VirtualMachineManager;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
import org.junit.Test;
+import org.junit.rules.Timeout;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class MicrodroidTests {
+ @Rule public Timeout globalTimeout = Timeout.seconds(300);
+
+ private static class Inner {
+ public Context mContext;
+ public VirtualMachineManager mVmm;
+ public VirtualMachine mVm;
+ }
+
+ private boolean mPkvmSupported = false;
+ private Inner mInner;
+
+ @Before
+ public void setup() {
+ // In case when the virt APEX doesn't exist on the device, classes in the
+ // android.system.virtualmachine package can't be loaded. Therefore, before using the
+ // classes, check the existence of a class in the package and skip this test if not exist.
+ try {
+ Class.forName("android.system.virtualmachine.VirtualMachineManager");
+ mPkvmSupported = true;
+ } catch (ClassNotFoundException e) {
+ assumeNoException(e);
+ return;
+ }
+ mInner = new Inner();
+ mInner.mContext = ApplicationProvider.getApplicationContext();
+ mInner.mVmm = VirtualMachineManager.getInstance(mInner.mContext);
+ }
+
+ @After
+ public void cleanup() throws VirtualMachineException {
+ if (!mPkvmSupported) {
+ return;
+ }
+ if (mInner.mVm == null) {
+ return;
+ }
+ mInner.mVm.stop();
+ mInner.mVm.delete();
+ }
+
+ private abstract static class VmEventListener implements VirtualMachineCallback {
+ private final Handler mHandler;
+
+ VmEventListener() {
+ Looper.prepare();
+ mHandler = new Handler(Looper.myLooper());
+ }
+
+ void runToFinish(VirtualMachine vm) throws VirtualMachineException {
+ vm.setCallback(mCallback);
+ vm.run();
+ Looper.loop();
+ }
+
+ void forceStop(VirtualMachine vm) {
+ try {
+ vm.stop();
+ } catch (VirtualMachineException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ // This is the actual listener that is registered. Since the listener is executed in another
+ // thread, post a runnable to the current thread to call the corresponding mHandler method
+ // in the current thread.
+ private final VirtualMachineCallback mCallback =
+ new VirtualMachineCallback() {
+ @Override
+ public void onPayloadStarted(VirtualMachine vm, ParcelFileDescriptor stream) {
+ mHandler.post(() -> VmEventListener.this.onPayloadStarted(vm, stream));
+ }
+
+ @Override
+ public void onPayloadReady(VirtualMachine vm) {
+ mHandler.post(() -> VmEventListener.this.onPayloadReady(vm));
+ }
+
+ @Override
+ public void onPayloadFinished(VirtualMachine vm, int exitCode) {
+ mHandler.post(() -> VmEventListener.this.onPayloadFinished(vm, exitCode));
+ }
+
+ @Override
+ public void onDied(VirtualMachine vm) {
+ mHandler.post(
+ () -> {
+ VmEventListener.this.onDied(vm);
+ Looper.myLooper().quitSafely();
+ });
+ }
+ };
+
+ @Override
+ public void onPayloadStarted(VirtualMachine vm, ParcelFileDescriptor stream) {}
+
+ @Override
+ public void onPayloadReady(VirtualMachine vm) {}
+
+ @Override
+ public void onPayloadFinished(VirtualMachine vm, int exitCode) {}
+
+ @Override
+ public void onDied(VirtualMachine vm) {}
+ }
+
@Test
- public void testNothing() {
- assertTrue(true);
+ public void startAndStop() throws VirtualMachineException, InterruptedException {
+ VirtualMachineConfig.Builder builder =
+ new VirtualMachineConfig.Builder(mInner.mContext, "assets/vm_config.json");
+ VirtualMachineConfig config = builder.build();
+
+ mInner.mVm = mInner.mVmm.getOrCreate("test_vm", config);
+ VmEventListener listener =
+ new VmEventListener() {
+ private boolean mPayloadReadyCalled = false;
+ private boolean mPayloadStartedCalled = false;
+
+ @Override
+ public void onPayloadReady(VirtualMachine vm) {
+ mPayloadReadyCalled = true;
+ }
+
+ @Override
+ public void onPayloadStarted(VirtualMachine vm, ParcelFileDescriptor stream) {
+ mPayloadStartedCalled = true;
+ forceStop(vm);
+ }
+
+ @Override
+ public void onDied(VirtualMachine vm) {
+ assertTrue(mPayloadReadyCalled);
+ assertTrue(mPayloadStartedCalled);
+ }
+ };
+ listener.runToFinish(mInner.mVm);
}
}
diff --git a/virtualizationservice/src/payload.rs b/virtualizationservice/src/payload.rs
index a59afd5..bc184ec 100644
--- a/virtualizationservice/src/payload.rs
+++ b/virtualizationservice/src/payload.rs
@@ -132,7 +132,11 @@
let staged_apex_info = pm.getStagedApexInfo(&apex_info.name)?;
if let Some(staged_apex_info) = staged_apex_info {
apex_info.path = PathBuf::from(staged_apex_info.diskImagePath);
- // TODO(b/201788989) copy bootclasspath/systemserverclasspath
+ apex_info.boot_classpath = staged_apex_info.hasBootClassPathJars;
+ apex_info.systemserver_classpath =
+ staged_apex_info.hasSystemServerClassPathJars;
+ apex_info.dex2oatboot_classpath =
+ staged_apex_info.hasDex2OatBootClassPathJars;
}
}
}