Merge "Add support for checking whether feature is enabled on device" into main
diff --git a/javalib/api/test-current.txt b/javalib/api/test-current.txt
index cf95770..51c2223 100644
--- a/javalib/api/test-current.txt
+++ b/javalib/api/test-current.txt
@@ -17,5 +17,10 @@
method @NonNull public android.system.virtualmachine.VirtualMachineConfig.Builder setVmConsoleInputSupported(boolean);
}
+ public class VirtualMachineManager {
+ method @RequiresPermission(android.system.virtualmachine.VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION) public boolean isFeatureEnabled(String) throws android.system.virtualmachine.VirtualMachineException;
+ field public static final String FEATURE_PAYLOAD_NOT_ROOT = "com.android.kvm.PAYLOAD_NON_ROOT";
+ }
+
}
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineManager.java b/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
index b7ea22c..c4096da 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
@@ -23,11 +23,15 @@
import android.annotation.Nullable;
import android.annotation.RequiresFeature;
import android.annotation.RequiresPermission;
+import android.annotation.StringDef;
import android.annotation.SystemApi;
+import android.annotation.TestApi;
import android.annotation.WorkerThread;
import android.content.Context;
import android.content.pm.PackageManager;
+import android.os.RemoteException;
import android.sysprop.HypervisorProperties;
+import android.system.virtualizationservice.IVirtualizationService;
import android.util.ArrayMap;
import com.android.internal.annotations.GuardedBy;
@@ -96,6 +100,26 @@
public static final int CAPABILITY_NON_PROTECTED_VM = 2;
/**
+ * Features provided by {@link VirtualMachineManager}.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @StringDef(
+ prefix = "FEATURE_",
+ value = {FEATURE_PAYLOAD_NOT_ROOT})
+ public @interface Features {}
+
+ /**
+ * Feature to run payload as non-root user.
+ *
+ * @hide
+ */
+ @TestApi
+ public static final String FEATURE_PAYLOAD_NOT_ROOT =
+ IVirtualizationService.FEATURE_PAYLOAD_NON_ROOT;
+
+ /**
* Returns a set of flags indicating what this implementation of virtualization is capable of.
*
* @see #CAPABILITY_PROTECTED_VM
@@ -277,4 +301,22 @@
}
return null;
}
+
+ /**
+ * Returns {@code true} if given {@code featureName} is enabled.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION)
+ public boolean isFeatureEnabled(@Features String featureName) throws VirtualMachineException {
+ synchronized (sCreateLock) {
+ VirtualizationService service = VirtualizationService.getInstance();
+ try {
+ return service.getBinder().isFeatureEnabled(featureName);
+ } catch (RemoteException e) {
+ throw e.rethrowAsRuntimeException();
+ }
+ }
+ }
}
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 a928dcf..d6183cf 100644
--- a/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
+++ b/tests/testapk/src/java/com/android/microdroid/test/MicrodroidTests.java
@@ -72,7 +72,6 @@
import org.junit.After;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.function.ThrowingRunnable;
@@ -1130,6 +1129,17 @@
assertThrows(Exception.class, () -> launchVmAndGetCdis("test_vm"));
}
+ @Test
+ public void isFeatureEnabled_requiresManagePermission() throws Exception {
+ revokePermission(VirtualMachine.MANAGE_VIRTUAL_MACHINE_PERMISSION);
+
+ VirtualMachineManager vmm = getVirtualMachineManager();
+ SecurityException e =
+ assertThrows(SecurityException.class, () -> vmm.isFeatureEnabled("whatever"));
+ assertThat(e)
+ .hasMessageThat()
+ .contains("android.permission.MANAGE_VIRTUAL_MACHINE permission");
+ }
private static final UUID MICRODROID_PARTITION_UUID =
UUID.fromString("cf9afe9a-0662-11ec-a329-c32663a09d75");
@@ -1524,11 +1534,15 @@
}
@Test
- @Ignore // Figure out how to run this conditionally
@CddTest(requirements = {"9.17/C-1-1"})
public void payloadIsNotRoot() throws Exception {
assumeSupportedDevice();
+ VirtualMachineManager vmm = getVirtualMachineManager();
+ assumeTrue(
+ VirtualMachineManager.FEATURE_PAYLOAD_NOT_ROOT + " not enabled",
+ vmm.isFeatureEnabled(VirtualMachineManager.FEATURE_PAYLOAD_NOT_ROOT));
+
VirtualMachineConfig config =
newVmConfigBuilder()
.setPayloadBinaryName("MicrodroidTestNativeLib.so")
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index f5f2718..164977c 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -34,6 +34,7 @@
IVirtualMachine::{BnVirtualMachine, IVirtualMachine},
IVirtualMachineCallback::IVirtualMachineCallback,
IVirtualizationService::IVirtualizationService,
+ IVirtualizationService::FEATURE_PAYLOAD_NON_ROOT,
MemoryTrimLevel::MemoryTrimLevel,
Partition::Partition,
PartitionType::PartitionType,
@@ -264,6 +265,21 @@
// Delegate to the global service, including checking the permission.
GLOBAL_SERVICE.getAssignableDevices()
}
+
+ /// Returns whether given feature is enabled
+ fn isFeatureEnabled(&self, feature: &str) -> binder::Result<bool> {
+ check_manage_access()?;
+
+ // This approach is quite cumbersome, but will do the work for the short term.
+ // TODO(b/298012279): make this scalable.
+ match feature {
+ FEATURE_PAYLOAD_NON_ROOT => Ok(cfg!(payload_not_root)),
+ _ => {
+ warn!("unknown feature {}", feature);
+ Ok(false)
+ }
+ }
+ }
}
impl VirtualizationService {
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
index df72e49..0ee958d 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
@@ -22,6 +22,8 @@
import android.system.virtualizationservice.VirtualMachineDebugInfo;
interface IVirtualizationService {
+ const String FEATURE_PAYLOAD_NON_ROOT = "com.android.kvm.PAYLOAD_NON_ROOT";
+
/**
* Create the VM with the given config file, and return a handle to it ready to start it. If
* `consoleOutFd` is provided then console output from the VM will be sent to it. If
@@ -61,4 +63,7 @@
* Get a list of assignable device types.
*/
AssignableDevice[] getAssignableDevices();
+
+ /** Returns whether given feature is enabled. */
+ boolean isFeatureEnabled(in String feature);
}