Merge "[rkp] Add request/response for ECDSA P256 key pair generation" into main
diff --git a/Android.bp b/Android.bp
index 1fae793..d1086ba 100644
--- a/Android.bp
+++ b/Android.bp
@@ -34,6 +34,7 @@
     config_namespace: "ANDROID",
     bool_variables: [
         "release_avf_enable_multi_tenant_microdroid_vm",
+        "release_avf_enable_vendor_modules",
     ],
     properties: [
         "cfgs",
@@ -46,5 +47,8 @@
         release_avf_enable_multi_tenant_microdroid_vm: {
             cfgs: ["payload_not_root"],
         },
+        release_avf_enable_vendor_modules: {
+            cfgs: ["vendor_modules"],
+        },
     },
 }
diff --git a/apex/product_packages.mk b/apex/product_packages.mk
index ef84551..4c03836 100644
--- a/apex/product_packages.mk
+++ b/apex/product_packages.mk
@@ -37,3 +37,10 @@
 PRODUCT_FSVERITY_GENERATE_METADATA := true
 
 PRODUCT_AVF_ENABLED := true
+
+# The cheap build flags dependency management system until there is a proper one.
+ifdef RELEASE_AVF_ENABLE_DEVICE_ASSIGNMENT
+  ifndef RELEASE_AVF_ENABLE_VENDOR_MODULES
+    $(error RELEASE_AVF_ENABLE_VENDOR_MODULES must also be enabled)
+  endif
+endif
diff --git a/apex/virtualizationservice.xml b/apex/virtualizationservice.xml
new file mode 100644
index 0000000..0ce1e10
--- /dev/null
+++ b/apex/virtualizationservice.xml
@@ -0,0 +1,7 @@
+<manifest version="1.0" type="framework">
+    <hal format="aidl">
+        <name>android.system.virtualization</name>
+        <version>3</version>
+        <fqname>IRemotelyProvisionedComponent/avf</fqname>
+    </hal>
+</manifest>
diff --git a/javalib/api/test-current.txt b/javalib/api/test-current.txt
index cf95770..bedb267 100644
--- a/javalib/api/test-current.txt
+++ b/javalib/api/test-current.txt
@@ -13,9 +13,15 @@
 
   public static final class VirtualMachineConfig.Builder {
     method @NonNull @RequiresPermission(android.system.virtualmachine.VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION) public android.system.virtualmachine.VirtualMachineConfig.Builder setPayloadConfigPath(@NonNull String);
-    method @NonNull @RequiresPermission(android.system.virtualmachine.VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION) public android.system.virtualmachine.VirtualMachineConfig.Builder setVendorDiskImage(@NonNull java.io.File);
+    method @FlaggedApi("RELEASE_AVF_ENABLE_VENDOR_MODULES") @NonNull @RequiresPermission(android.system.virtualmachine.VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION) public android.system.virtualmachine.VirtualMachineConfig.Builder setVendorDiskImage(@NonNull java.io.File);
     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";
+    field public static final String FEATURE_VENDOR_MODULES = "com.android.kvm.VENDOR_MODULES";
+  }
+
 }
 
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
index b307854..cc8f65b 100644
--- a/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
+++ b/javalib/src/android/system/virtualmachine/VirtualMachineConfig.java
@@ -21,6 +21,7 @@
 
 import static java.util.Objects.requireNonNull;
 
+import android.annotation.FlaggedApi;
 import android.annotation.IntDef;
 import android.annotation.IntRange;
 import android.annotation.NonNull;
@@ -903,6 +904,7 @@
          */
         @TestApi
         @RequiresPermission(VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION)
+        @FlaggedApi("RELEASE_AVF_ENABLE_VENDOR_MODULES")
         @NonNull
         public Builder setVendorDiskImage(@NonNull File vendorDiskImage) {
             mVendorDiskImage = vendorDiskImage;
diff --git a/javalib/src/android/system/virtualmachine/VirtualMachineManager.java b/javalib/src/android/system/virtualmachine/VirtualMachineManager.java
index b7ea22c..0a79553 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,35 @@
     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, FEATURE_VENDOR_MODULES})
+    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;
+
+    /**
+     * Feature to run payload as non-root user.
+     *
+     * @hide
+     */
+    @TestApi
+    public static final String FEATURE_VENDOR_MODULES =
+            IVirtualizationService.FEATURE_VENDOR_MODULES;
+
+    /**
      * Returns a set of flags indicating what this implementation of virtualization is capable of.
      *
      * @see #CAPABILITY_PROTECTED_VM
@@ -277,4 +310,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..473a560 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,10 +1534,10 @@
     }
 
     @Test
-    @Ignore // Figure out how to run this conditionally
     @CddTest(requirements = {"9.17/C-1-1"})
     public void payloadIsNotRoot() throws Exception {
         assumeSupportedDevice();
+        assumeFeatureEnabled(VirtualMachineManager.FEATURE_PAYLOAD_NOT_ROOT);
 
         VirtualMachineConfig config =
                 newVmConfigBuilder()
@@ -2084,6 +2094,7 @@
     @Test
     public void configuringVendorDiskImageRequiresCustomPermission() throws Exception {
         assumeSupportedDevice();
+        assumeFeatureEnabled(VirtualMachineManager.FEATURE_VENDOR_MODULES);
 
         File vendorDiskImage =
                 new File("/data/local/tmp/cts/microdroid/test_microdroid_vendor_image.img");
@@ -2108,6 +2119,7 @@
     @Test
     public void bootsWithVendorPartition() throws Exception {
         assumeSupportedDevice();
+        assumeFeatureEnabled(VirtualMachineManager.FEATURE_VENDOR_MODULES);
 
         grantPermission(VirtualMachine.USE_CUSTOM_VIRTUAL_MACHINE_PERMISSION);
 
@@ -2235,4 +2247,9 @@
                 .that(KERNEL_VERSION)
                 .isNotEqualTo("5.4");
     }
+
+    private void assumeFeatureEnabled(String featureName) throws Exception {
+        VirtualMachineManager vmm = getVirtualMachineManager();
+        assumeTrue(featureName + " not enabled", vmm.isFeatureEnabled(featureName));
+    }
 }
diff --git a/virtualizationmanager/src/aidl.rs b/virtualizationmanager/src/aidl.rs
index f5f2718..781d83f 100644
--- a/virtualizationmanager/src/aidl.rs
+++ b/virtualizationmanager/src/aidl.rs
@@ -34,6 +34,8 @@
     IVirtualMachine::{BnVirtualMachine, IVirtualMachine},
     IVirtualMachineCallback::IVirtualMachineCallback,
     IVirtualizationService::IVirtualizationService,
+    IVirtualizationService::FEATURE_PAYLOAD_NON_ROOT,
+    IVirtualizationService::FEATURE_VENDOR_MODULES,
     MemoryTrimLevel::MemoryTrimLevel,
     Partition::Partition,
     PartitionType::PartitionType,
@@ -264,6 +266,22 @@
         // 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)),
+            FEATURE_VENDOR_MODULES => Ok(cfg!(vendor_modules)),
+            _ => {
+                warn!("unknown feature {}", feature);
+                Ok(false)
+            }
+        }
+    }
 }
 
 impl VirtualizationService {
@@ -310,6 +328,8 @@
         let requester_uid = get_calling_uid();
         let requester_debug_pid = get_calling_pid();
 
+        check_config_features(config)?;
+
         // Allocating VM context checks the MANAGE_VIRTUAL_MACHINE permission.
         let (vm_context, cid, temporary_directory) = self.create_vm_context(requester_debug_pid)?;
 
@@ -1089,6 +1109,24 @@
     }
 }
 
+fn check_no_vendor_modules(config: &VirtualMachineConfig) -> binder::Result<()> {
+    let VirtualMachineConfig::AppConfig(config) = config else { return Ok(()) };
+    if let Some(custom_config) = &config.customConfig {
+        if custom_config.vendorImage.is_some() || custom_config.customKernelImage.is_some() {
+            return Err(anyhow!("vendor modules feature is disabled"))
+                .or_binder_exception(ExceptionCode::UNSUPPORTED_OPERATION);
+        }
+    }
+    Ok(())
+}
+
+fn check_config_features(config: &VirtualMachineConfig) -> binder::Result<()> {
+    if !cfg!(vendor_modules) {
+        check_no_vendor_modules(config)?;
+    }
+    Ok(())
+}
+
 fn clone_or_prepare_logger_fd(
     debug_config: &DebugConfig,
     fd: Option<&ParcelFileDescriptor>,
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index 74f88c5..ac9c1df 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -21,6 +21,7 @@
     },
     prefer_rlib: true,
     rustlibs: [
+        "android.hardware.security.rkp-V3-rust",
         "android.system.virtualizationcommon-rust",
         "android.system.virtualizationservice-rust",
         "android.system.virtualizationservice_internal-rust",
diff --git a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
index df72e49..fa50d54 100644
--- a/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
+++ b/virtualizationservice/aidl/android/system/virtualizationservice/IVirtualizationService.aidl
@@ -22,6 +22,9 @@
 import android.system.virtualizationservice.VirtualMachineDebugInfo;
 
 interface IVirtualizationService {
+    const String FEATURE_PAYLOAD_NON_ROOT = "com.android.kvm.PAYLOAD_NON_ROOT";
+    const String FEATURE_VENDOR_MODULES = "com.android.kvm.VENDOR_MODULES";
+
     /**
      * 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 +64,7 @@
      * Get a list of assignable device types.
      */
     AssignableDevice[] getAssignableDevices();
+
+    /** Returns whether given feature is enabled. */
+    boolean isFeatureEnabled(in String feature);
 }
diff --git a/virtualizationservice/src/main.rs b/virtualizationservice/src/main.rs
index 3af0d42..cdb3ac9 100644
--- a/virtualizationservice/src/main.rs
+++ b/virtualizationservice/src/main.rs
@@ -16,6 +16,7 @@
 
 mod aidl;
 mod atom;
+mod remote_provisioning;
 mod rkpvm;
 mod service_vm;
 
@@ -32,6 +33,8 @@
 use std::os::unix::raw::{pid_t, uid_t};
 
 const LOG_TAG: &str = "VirtualizationService";
+const _REMOTELY_PROVISIONED_COMPONENT_SERVICE_NAME: &str =
+    "android.system.virtualization.IRemotelyProvisionedComponent/avf";
 
 fn get_calling_pid() -> pid_t {
     ThreadState::get_calling_pid()
@@ -61,7 +64,13 @@
     let service = VirtualizationServiceInternal::init();
     let service = BnVirtualizationServiceInternal::new_binder(service, BinderFeatures::default());
     register_lazy_service(BINDER_SERVICE_IDENTIFIER, service.as_binder()).unwrap();
-    info!("Registered Binder service, joining threadpool.");
+    info!("Registered Binder service {}.", BINDER_SERVICE_IDENTIFIER);
+
+    // The IRemotelyProvisionedComponent service is only supposed to be triggered by rkpd for
+    // RKP VM attestation.
+    let _remote_provisioning_service = remote_provisioning::new_binder();
+    // TODO(b/274881098): Register the RKP service when the implementation is ready.
+
     ProcessState::join_thread_pool();
 }
 
diff --git a/virtualizationservice/src/remote_provisioning.rs b/virtualizationservice/src/remote_provisioning.rs
new file mode 100644
index 0000000..1acbcee
--- /dev/null
+++ b/virtualizationservice/src/remote_provisioning.rs
@@ -0,0 +1,86 @@
+// Copyright 2023, 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.
+
+//! IRemotelyProvisionedComponent HAL implementation.
+
+use android_hardware_security_rkp::aidl::android::hardware::security::keymint::{
+    DeviceInfo::DeviceInfo,
+    IRemotelyProvisionedComponent::{
+        BnRemotelyProvisionedComponent, IRemotelyProvisionedComponent, STATUS_REMOVED,
+    },
+    MacedPublicKey::MacedPublicKey,
+    ProtectedData::ProtectedData,
+    RpcHardwareInfo::{RpcHardwareInfo, CURVE_NONE, MIN_SUPPORTED_NUM_KEYS_IN_CSR},
+};
+use avflog::LogResult;
+use binder::{BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Status, Strong};
+
+/// Constructs a binder object that implements `IRemotelyProvisionedComponent`.
+pub(crate) fn new_binder() -> Strong<dyn IRemotelyProvisionedComponent> {
+    BnRemotelyProvisionedComponent::new_binder(
+        AvfRemotelyProvisionedComponent {},
+        BinderFeatures::default(),
+    )
+}
+
+struct AvfRemotelyProvisionedComponent {}
+
+impl Interface for AvfRemotelyProvisionedComponent {}
+
+#[allow(non_snake_case)]
+impl IRemotelyProvisionedComponent for AvfRemotelyProvisionedComponent {
+    fn getHardwareInfo(&self) -> BinderResult<RpcHardwareInfo> {
+        Ok(RpcHardwareInfo {
+            versionNumber: 3,
+            rpcAuthorName: String::from("Android Virtualization Framework"),
+            supportedEekCurve: CURVE_NONE,
+            uniqueId: Some(String::from("Android Virtualization Framework 1")),
+            supportedNumKeysInCsr: MIN_SUPPORTED_NUM_KEYS_IN_CSR,
+        })
+    }
+
+    fn generateEcdsaP256KeyPair(
+        &self,
+        _testMode: bool,
+        _macedPublicKey: &mut MacedPublicKey,
+    ) -> BinderResult<Vec<u8>> {
+        // TODO(b/274881098): Implement this.
+        Err(Status::new_exception(ExceptionCode::UNSUPPORTED_OPERATION, None)).with_log()
+    }
+
+    fn generateCertificateRequest(
+        &self,
+        _testMode: bool,
+        _keysToSign: &[MacedPublicKey],
+        _endpointEncryptionCertChain: &[u8],
+        _challenge: &[u8],
+        _deviceInfo: &mut DeviceInfo,
+        _protectedData: &mut ProtectedData,
+    ) -> BinderResult<Vec<u8>> {
+        Err(Status::new_service_specific_error_str(
+            STATUS_REMOVED,
+            Some("This method was deprecated in v3 of the interface."),
+        ))
+        .with_log()
+    }
+
+    fn generateCertificateRequestV2(
+        &self,
+        _keysToSign: &[MacedPublicKey],
+        _challenge: &[u8],
+    ) -> BinderResult<Vec<u8>> {
+        // TODO(b/274881098): Implement this.
+        Err(Status::new_exception(ExceptionCode::UNSUPPORTED_OPERATION, None)).with_log()
+    }
+}
diff --git a/vm/src/main.rs b/vm/src/main.rs
index 4c44496..e133b8b 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -98,10 +98,12 @@
     storage_size: Option<u64>,
 
     /// Path to custom kernel image to use when booting Microdroid.
+    #[cfg(vendor_modules)]
     #[arg(long)]
     kernel: Option<PathBuf>,
 
     /// Path to disk image containing vendor-specific modules.
+    #[cfg(vendor_modules)]
     #[arg(long)]
     vendor: Option<PathBuf>,
 
@@ -110,6 +112,29 @@
     devices: Vec<PathBuf>,
 }
 
+impl MicrodroidConfig {
+    #[cfg(vendor_modules)]
+    fn kernel(&self) -> &Option<PathBuf> {
+        &self.kernel
+    }
+
+    #[cfg(not(vendor_modules))]
+    fn kernel(&self) -> Option<PathBuf> {
+        None
+    }
+
+    #[cfg(vendor_modules)]
+    fn vendor(&self) -> &Option<PathBuf> {
+        &self.vendor
+    }
+
+    #[cfg(not(vendor_modules))]
+    #[inline(always)]
+    fn vendor(&self) -> Option<PathBuf> {
+        None
+    }
+}
+
 #[derive(Args)]
 /// Flags for the run_app subcommand
 pub struct RunAppConfig {
diff --git a/vm/src/run.rs b/vm/src/run.rs
index fc8d7e0..1ba9dec 100644
--- a/vm/src/run.rs
+++ b/vm/src/run.rs
@@ -84,25 +84,25 @@
         )?;
     }
 
-    let storage = if let Some(path) = config.microdroid.storage {
+    let storage = if let Some(ref path) = config.microdroid.storage {
         if !path.exists() {
             command_create_partition(
                 service.as_ref(),
-                &path,
+                path,
                 config.microdroid.storage_size.unwrap_or(10 * 1024 * 1024),
                 PartitionType::ENCRYPTEDSTORE,
             )?;
         }
-        Some(open_parcel_file(&path, true)?)
+        Some(open_parcel_file(path, true)?)
     } else {
         None
     };
 
     let kernel =
-        config.microdroid.kernel.as_ref().map(|p| open_parcel_file(p, false)).transpose()?;
+        config.microdroid.kernel().as_ref().map(|p| open_parcel_file(p, false)).transpose()?;
 
     let vendor =
-        config.microdroid.vendor.as_ref().map(|p| open_parcel_file(p, false)).transpose()?;
+        config.microdroid.vendor().as_ref().map(|p| open_parcel_file(p, false)).transpose()?;
 
     let extra_idsig_files: Result<Vec<File>, _> =
         config.extra_idsigs.iter().map(File::open).collect();