Add USE_CUSTOM_VIRTUAL_MACHINE permission

A package should be granted USE_CUSTOM_VIRTUAL_MACHINE permission to
create a protected VM with a raw config.

Bug: 207769805
Test: atest MicrodroidHostTestCases
Change-Id: I222b110f0634c0e4658ed192099d4af6c5b3debe
diff --git a/javalib/AndroidManifest.xml b/javalib/AndroidManifest.xml
index 2a0b903..e68b5a4 100644
--- a/javalib/AndroidManifest.xml
+++ b/javalib/AndroidManifest.xml
@@ -20,6 +20,9 @@
   <permission android:name="android.permission.MANAGE_VIRTUAL_MACHINE"
       android:protectionLevel="signature|development" />
 
+  <permission android:name="android.permission.USE_CUSTOM_VIRTUAL_MACHINE"
+      android:protectionLevel="signature|development" />
+
   <permission android:name="android.permission.DEBUG_VIRTUAL_MACHINE"
       android:protectionLevel="signature" />
 
diff --git a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
index 7126399..a59419d 100644
--- a/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
+++ b/tests/hostside/java/android/virt/test/MicrodroidTestCase.java
@@ -66,6 +66,7 @@
 public class MicrodroidTestCase extends VirtualizationTestCaseBase {
     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";
 
     private static final int MIN_MEM_ARM64 = 145;
     private static final int MIN_MEM_X86_64 = 196;
@@ -470,6 +471,40 @@
         shutdownMicrodroid(getDevice(), cid);
     }
 
+    @Test
+    public void testCustomVirtualMachinePermission()
+            throws DeviceNotAvailableException, IOException, JSONException {
+        CommandRunner android = new CommandRunner(getDevice());
+
+        // Pull etc/microdroid.json
+        File virtApexDir = FileUtil.createTempDir("virt_apex");
+        File microdroidConfigFile = new File(virtApexDir, "microdroid.json");
+        assertTrue(getDevice().pullFile(VIRT_APEX + "etc/microdroid.json", microdroidConfigFile));
+        JSONObject config = new JSONObject(FileUtil.readStringFromFile(microdroidConfigFile));
+
+        // USE_CUSTOM_VIRTUAL_MACHINE is enforced only on protected mode
+        config.put("protected", true);
+
+        // Write updated config
+        final String configPath = TEST_ROOT + "raw_config.json";
+        getDevice().pushString(config.toString(), configPath);
+
+        // temporarily revoke the permission
+        android.run(
+                "pm",
+                "revoke",
+                SHELL_PACKAGE_NAME,
+                "android.permission.USE_CUSTOM_VIRTUAL_MACHINE");
+        final String ret =
+                android.runForResult(VIRT_APEX + "bin/vm run", configPath).getStderr().trim();
+
+        assertTrue(
+                "The test should fail with a permission error",
+                ret.contains(
+                        "does not have the android.permission.USE_CUSTOM_VIRTUAL_MACHINE"
+                            + " permission"));
+    }
+
     @Before
     public void setUp() throws Exception {
         testIfDeviceIsCapable(getDevice());
@@ -490,5 +525,9 @@
                 "vm.log-" + mTestName.getMethodName());
 
         getDevice().uninstallPackage(PACKAGE_NAME);
+
+        // testCustomVirtualMachinePermission revokes this permission. Grant it again as cleanup
+        new CommandRunner(getDevice()).tryRun(
+                "pm", "grant", SHELL_PACKAGE_NAME, "android.permission.USE_CUSTOM_VIRTUAL_MACHINE");
     }
 }
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 41cc4a5..23febed 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -362,6 +362,13 @@
         is_protected: &mut bool,
     ) -> binder::Result<Strong<dyn IVirtualMachine>> {
         check_manage_access()?;
+
+        if let VirtualMachineConfig::RawConfig(config) = config {
+            if config.protectedVm {
+                check_use_custom_virtual_machine()?;
+            }
+        }
+
         let state = &mut *self.state.lock().unwrap();
         let console_fd = console_fd.map(clone_file).transpose()?;
         let log_fd = log_fd.map(clone_file).transpose()?;
@@ -729,6 +736,11 @@
     check_permission("android.permission.MANAGE_VIRTUAL_MACHINE")
 }
 
+/// Check whether the caller of the current Binder method is allowed to create custom VMs
+fn check_use_custom_virtual_machine() -> binder::Result<()> {
+    check_permission("android.permission.USE_CUSTOM_VIRTUAL_MACHINE")
+}
+
 /// Check if a partition has selinux labels that are not allowed
 fn check_label_for_partition(partition: &Partition) -> Result<()> {
     let ctx = getfilecon(partition.image.as_ref().unwrap().as_ref())?;