Config file requires custom VM permission
Modify VS to require the USE_CUSTOM_VIRTUAL_MACHINE permission if a
config file is specified.
Modify the tests to grant the necessary permissions at runtime rather
than via AndroidTest.xml. Make use of a config file explicit, and only
do so (and grant the custom VM permission) for the tests that need it.
Moved the existing permission test to a device test and added a new
one for the custom VM permission. That failed unexpectedly, so I fixed
the way we were reporting the exception.
Other minor tidying up. Renamed MicrodroidTestCase to
MicrodroidHostTests because it kept confusing me.
Bug: 243513572
Test: atest MicrodroidTests MicrodroidHostTests
Change-Id: Ie67e7ed214dc9c95e453ca1fcc38a1b18d4f5f88
diff --git a/tests/benchmark/AndroidManifest.xml b/tests/benchmark/AndroidManifest.xml
index ff18130..c39b91c 100644
--- a/tests/benchmark/AndroidManifest.xml
+++ b/tests/benchmark/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.microdroid.benchmark">
<uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
+ <uses-permission android:name="android.permission.USE_CUSTOM_VIRTUAL_MACHINE" />
<application>
<uses-library android:name="android.system.virtualmachine" android:required="false" />
</application>
diff --git a/tests/benchmark/AndroidTest.xml b/tests/benchmark/AndroidTest.xml
index 4949d22..0214cd9 100644
--- a/tests/benchmark/AndroidTest.xml
+++ b/tests/benchmark/AndroidTest.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<configuration description="Runs sample instrumentation test.">
+<configuration description="Runs Microdroid benchmarks.">
<option name="config-descriptor:metadata" key="component" value="security" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
<option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
@@ -25,11 +25,6 @@
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
<option name="force-root" value="true" />
</target_preparer>
- <target_preparer class="com.android.tradefed.targetprep.RunCommandTargetPreparer">
- <option
- name="run-command"
- value="pm grant com.android.microdroid.benchmark android.permission.MANAGE_VIRTUAL_MACHINE" />
- </target_preparer>
<test class="com.android.tradefed.testtype.AndroidJUnitTest" >
<option name="package" value="com.android.microdroid.benchmark" />
<option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
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 b1a1160..23d546d 100644
--- a/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
+++ b/tests/benchmark/src/java/com/android/microdroid/benchmark/MicrodroidBenchmarks.java
@@ -80,20 +80,23 @@
@Before
public void setup() {
+ grantPermission(VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION);
+ grantPermission(VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION);
prepareTestSetup(mProtectedVm);
mInstrumentation = getInstrumentation();
}
private boolean canBootMicrodroidWithMemory(int mem)
throws VirtualMachineException, InterruptedException, IOException {
- final int trialCount = 5;
+ VirtualMachineConfig normalConfig = mInner.newVmConfigBuilder()
+ .setPayloadBinaryPath("MicrodroidTestNativeLib.so")
+ .setDebugLevel(DEBUG_LEVEL_NONE)
+ .setMemoryMib(mem)
+ .build();
// returns true if succeeded at least once.
+ final int trialCount = 5;
for (int i = 0; i < trialCount; i++) {
- VirtualMachineConfig.Builder builder =
- mInner.newVmConfigBuilder("assets/vm_config.json");
- VirtualMachineConfig normalConfig =
- 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;
@@ -144,7 +147,8 @@
for (int i = 0; i < trialCount; i++) {
// To grab boot events from log, set debug mode to FULL
- VirtualMachineConfig normalConfig = mInner.newVmConfigBuilder("assets/vm_config.json")
+ VirtualMachineConfig normalConfig = mInner.newVmConfigBuilder()
+ .setPayloadBinaryPath("MicrodroidTestNativeLib.so")
.setDebugLevel(DEBUG_LEVEL_FULL)
.setMemoryMib(256)
.build();
@@ -191,7 +195,8 @@
@Test
public void testVsockTransferFromHostToVM() throws Exception {
- VirtualMachineConfig config = mInner.newVmConfigBuilder("assets/vm_config_io.json")
+ VirtualMachineConfig config = mInner.newVmConfigBuilder()
+ .setPayloadConfigPath("assets/vm_config_io.json")
.setDebugLevel(DEBUG_LEVEL_FULL)
.build();
List<Double> transferRates = new ArrayList<>(IO_TEST_TRIAL_COUNT);
@@ -217,7 +222,8 @@
}
private void testVirtioBlkReadRate(boolean isRand) throws Exception {
- VirtualMachineConfig config = mInner.newVmConfigBuilder("assets/vm_config_io.json")
+ VirtualMachineConfig config = mInner.newVmConfigBuilder()
+ .setPayloadConfigPath("assets/vm_config_io.json")
.setDebugLevel(DEBUG_LEVEL_FULL)
.build();
List<Double> readRates = new ArrayList<>(IO_TEST_TRIAL_COUNT);
@@ -282,11 +288,11 @@
@Test
public void testMemoryUsage() throws Exception {
final String vmName = "test_vm_mem_usage";
- VirtualMachineConfig config =
- mInner.newVmConfigBuilder("assets/vm_config_io.json")
- .setDebugLevel(DEBUG_LEVEL_NONE)
- .setMemoryMib(256)
- .build();
+ VirtualMachineConfig config = mInner.newVmConfigBuilder()
+ .setPayloadConfigPath("assets/vm_config_io.json")
+ .setDebugLevel(DEBUG_LEVEL_NONE)
+ .setMemoryMib(256)
+ .build();
mInner.forceCreateNewVirtualMachine(vmName, config);
VirtualMachine vm = mInner.getVirtualMachineManager().get(vmName);
MemoryUsageListener listener = new MemoryUsageListener(this::executeCommand);
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 07705ba..66cd211 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
@@ -19,6 +19,7 @@
import static org.junit.Assume.assumeNoException;
+import android.app.Instrumentation;
import android.app.UiAutomation;
import android.content.Context;
import android.os.ParcelFileDescriptor;
@@ -32,6 +33,7 @@
import androidx.annotation.CallSuper;
import androidx.test.core.app.ApplicationProvider;
+import androidx.test.platform.app.InstrumentationRegistry;
import com.android.microdroid.test.common.MetricsProcessor;
import com.android.virt.VirtualizationTestHelper;
@@ -58,6 +60,20 @@
SystemProperties.get("debug.hypervisor.metrics_tag"));
}
+ protected final void grantPermission(String permission) {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ UiAutomation uiAutomation = instrumentation.getUiAutomation();
+ uiAutomation.grantRuntimePermission(instrumentation.getContext().getPackageName(),
+ permission);
+ }
+
+ protected final void revokePermission(String permission) {
+ Instrumentation instrumentation = InstrumentationRegistry.getInstrumentation();
+ UiAutomation uiAutomation = instrumentation.getUiAutomation();
+ uiAutomation.revokeRuntimePermission(instrumentation.getContext().getPackageName(),
+ permission);
+ }
+
// TODO(b/220920264): remove Inner class; this is a hack to hide virt APEX types
protected static class Inner {
private final boolean mProtectedVm;
@@ -82,10 +98,6 @@
return new VirtualMachineConfig.Builder(mContext).setProtectedVm(mProtectedVm);
}
- public VirtualMachineConfig.Builder newVmConfigBuilder(String payloadConfigPath) {
- return newVmConfigBuilder().setPayloadConfigPath(payloadConfigPath);
- }
-
/**
* Creates a new virtual machine, potentially removing an existing virtual machine with
* given name.
diff --git a/tests/hostside/AndroidTest.xml b/tests/hostside/AndroidTest.xml
index 5c3e5d1..18728ad 100644
--- a/tests/hostside/AndroidTest.xml
+++ b/tests/hostside/AndroidTest.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<configuration description="Tests for microdroid">
+<configuration description="Host driven tests for Microdroid">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="security" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
diff --git a/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
similarity index 94%
rename from tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
rename to tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
index 190c524..c9df624 100644
--- a/tests/hostside/java/com/android/microdroid/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/com/android/microdroid/test/MicrodroidHostTests.java
@@ -38,11 +38,8 @@
import com.android.os.AtomsProto;
import com.android.os.StatsLog;
import com.android.tradefed.device.DeviceNotAvailableException;
-import com.android.tradefed.result.TestDescription;
-import com.android.tradefed.result.TestResult;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner;
import com.android.tradefed.testtype.DeviceJUnit4ClassRunner.TestMetrics;
-import com.android.tradefed.testtype.junit4.DeviceTestRunOptions;
import com.android.tradefed.util.CommandResult;
import com.android.tradefed.util.FileUtil;
import com.android.tradefed.util.RunUtil;
@@ -73,7 +70,7 @@
import java.util.regex.Pattern;
@RunWith(DeviceJUnit4ClassRunner.class)
-public class MicrodroidTestCase extends MicrodroidHostTestCaseBase {
+public class MicrodroidHostTests extends MicrodroidHostTestCaseBase {
private static final String APK_NAME = "MicrodroidTestApp.apk";
private static final String PACKAGE_NAME = "com.android.microdroid.test";
private static final String SHELL_PACKAGE_NAME = "com.android.shell";
@@ -106,29 +103,6 @@
runOnMicrodroidForResult("watch -e \"getprop dev.bootcomplete | grep '^0$'\"");
}
- @Test
- @CddTest(requirements = {"9.17/C-1-1", "9.17/C-1-2", "9.17/C-1-4"})
- public void testCreateVmRequiresPermission() throws Exception {
- // Revoke the MANAGE_VIRTUAL_MACHINE permission for the test app
- CommandRunner android = new CommandRunner(getDevice());
- android.run("pm", "revoke", PACKAGE_NAME, "android.permission.MANAGE_VIRTUAL_MACHINE");
-
- // Run MicrodroidTests#connectToVmService test, which should fail
- final DeviceTestRunOptions options =
- new DeviceTestRunOptions(PACKAGE_NAME)
- .setTestClassName(PACKAGE_NAME + ".MicrodroidTests")
- .setTestMethodName("connectToVmService[protectedVm=false]")
- .setCheckResults(false);
- assertThat(runDeviceTests(options)).isFalse();
-
- Map<TestDescription, TestResult> results = getLastDeviceRunResults().getTestResults();
- assertThat(results).hasSize(1);
- TestResult result = results.values().toArray(new TestResult[0])[0];
- assertWithMessage("The test should fail with a permission error")
- .that(result.getStackTrace())
- .contains("android.permission.MANAGE_VIRTUAL_MACHINE permission");
- }
-
private static JSONObject newPartition(String label, String path) {
return new JSONObject(Map.of("label", label, "path", path));
}
@@ -364,13 +338,10 @@
final String initrdPath = TEST_ROOT + "etc/microdroid_initrd_full_debuggable.img";
config.put("initrd", initrdPath);
// Add instance image as a partition in disks[1]
- disks.put(
- new JSONObject()
+ disks.put(new JSONObject()
.put("writable", true)
- .put(
- "partitions",
- new JSONArray()
- .put(newPartition("vm-instance", instanceImgPath))));
+ .put("partitions",
+ new JSONArray().put(newPartition("vm-instance", instanceImgPath))));
// Add payload image disk with partitions:
// - payload-metadata
// - apexes: com.android.os.statsd, com.android.adbd, [sharedlib apex](optional)
diff --git a/tests/testapk/AndroidManifest.xml b/tests/testapk/AndroidManifest.xml
index 9c8b2d5..ab22546 100644
--- a/tests/testapk/AndroidManifest.xml
+++ b/tests/testapk/AndroidManifest.xml
@@ -16,6 +16,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.microdroid.test">
<uses-permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE" />
+ <uses-permission android:name="android.permission.USE_CUSTOM_VIRTUAL_MACHINE" />
<uses-sdk android:minSdkVersion="33" android:targetSdkVersion="33" />
<application>
<uses-library android:name="android.system.virtualmachine" android:required="false" />
diff --git a/tests/testapk/AndroidTest.xml b/tests/testapk/AndroidTest.xml
index e8bb1aa..787ebd4 100644
--- a/tests/testapk/AndroidTest.xml
+++ b/tests/testapk/AndroidTest.xml
@@ -13,7 +13,7 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
-<configuration description="Runs sample instrumentation test.">
+<configuration description="Runs Microdroid device-side tests.">
<option name="test-suite-tag" value="cts" />
<option name="config-descriptor:metadata" key="component" value="security" />
<option name="config-descriptor:metadata" key="parameter" value="not_instant_app" />
@@ -22,11 +22,6 @@
<target_preparer class="com.android.tradefed.targetprep.suite.SuiteApkInstaller">
<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" />
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 466acc7..297341b 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -21,6 +21,8 @@
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.TruthJUnit.assume;
+import static org.junit.Assert.assertThrows;
+
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import android.os.Build;
@@ -35,6 +37,7 @@
import com.android.microdroid.test.device.MicrodroidDeviceTestBase;
import com.android.microdroid.testservice.ITestService;
+import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
@@ -76,9 +79,16 @@
@Before
public void setup() {
+ grantPermission(VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION);
prepareTestSetup(mProtectedVm);
}
+ @After
+ public void tearDown() {
+ revokePermission(VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION);
+ revokePermission(VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION);
+ }
+
private static final int MIN_MEM_ARM64 = 150;
private static final int MIN_MEM_X86_64 = 196;
@@ -106,12 +116,59 @@
@Test
@CddTest(requirements = {
"9.17/C-1-1",
+ "9.17/C-1-2",
+ "9.17/C-1-4",
+ })
+ public void createVmRequiresPermission() throws Exception {
+ assumeSupportedKernel();
+
+ revokePermission(VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION);
+
+ VirtualMachineConfig config = mInner.newVmConfigBuilder()
+ .setPayloadBinaryPath("MicrodroidTestNativeLib.so")
+ .setMemoryMib(minMemoryRequired())
+ .build();
+
+ SecurityException e = assertThrows(SecurityException.class,
+ () -> mInner.forceCreateNewVirtualMachine("test_vm_requires_permission", config));
+ assertThat(e).hasMessageThat()
+ .contains("android.permission.MANAGE_VIRTUAL_MACHINE permission");
+ }
+
+ @Test
+ @CddTest(requirements = {
+ "9.17/C-1-1",
+ "9.17/C-1-2",
+ "9.17/C-1-4",
+ })
+ public void createVmWithConfigRequiresPermission() throws Exception {
+ assumeSupportedKernel();
+
+ VirtualMachineConfig config = mInner.newVmConfigBuilder()
+ .setPayloadConfigPath("assets/vm_config.json")
+ .setMemoryMib(minMemoryRequired())
+ .build();
+
+ VirtualMachine vm = mInner.forceCreateNewVirtualMachine(
+ "test_vm_config_requires_permission", config);
+
+ SecurityException e = assertThrows(SecurityException.class, () -> runVmTestService(vm));
+ assertThat(e).hasMessageThat()
+ .contains("android.permission.USE_CUSTOM_VIRTUAL_MACHINE permission");
+ }
+
+
+ @Test
+ @CddTest(requirements = {
+ "9.17/C-1-1",
"9.17/C-2-1"
})
public void extraApk() throws Exception {
assumeSupportedKernel();
- VirtualMachineConfig config = mInner.newVmConfigBuilder("assets/vm_config_extra_apk.json")
+ grantPermission(VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION);
+ VirtualMachineConfig config = mInner.newVmConfigBuilder()
+ .setPayloadConfigPath("assets/vm_config_extra_apk.json")
.setMemoryMib(minMemoryRequired())
.build();
VirtualMachine vm = mInner.forceCreateNewVirtualMachine("test_vm_extra_apk", config);
@@ -406,9 +463,11 @@
@Test
public void bootFailsWhenConfigIsInvalid() throws Exception {
- VirtualMachineConfig.Builder builder =
- mInner.newVmConfigBuilder("assets/vm_config_no_task.json");
- VirtualMachineConfig normalConfig = builder.setDebugLevel(DEBUG_LEVEL_FULL).build();
+ grantPermission(VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION);
+ VirtualMachineConfig normalConfig = mInner.newVmConfigBuilder()
+ .setPayloadConfigPath("assets/vm_config_no_task.json")
+ .setDebugLevel(DEBUG_LEVEL_FULL)
+ .build();
mInner.forceCreateNewVirtualMachine("test_vm_invalid_config", normalConfig);
BootResult bootResult = tryBootVm(TAG, "test_vm_invalid_config");