[service-vm] Start a bare-metal service VM from a client app

This cl mainly sets up the general pipeline to trigger the
bare-metal VM from a client app. The real implementation of the
API will be adjusted in the future.

Test: Runs the RkpvmClientApp in VM
Bug: 241428822
Change-Id: I92cef7033db9a2d8cf4ad1fec22fee8c93b1cef6
diff --git a/vm_payload/Android.bp b/vm_payload/Android.bp
index 967d1cf..9d55879 100644
--- a/vm_payload/Android.bp
+++ b/vm_payload/Android.bp
@@ -36,7 +36,10 @@
     crate_name: "vm_payload_bindgen",
     source_stem: "bindings",
     apex_available: ["com.android.compos"],
-    visibility: ["//packages/modules/Virtualization/compos"],
+    visibility: [
+        "//packages/modules/Virtualization/compos",
+        "//packages/modules/Virtualization/service_vm/client_apk",
+    ],
     shared_libs: [
         "libvm_payload#current",
     ],
diff --git a/vm_payload/include-restricted/vm_payload_restricted.h b/vm_payload/include-restricted/vm_payload_restricted.h
index 7f17cde..1e0c3cc 100644
--- a/vm_payload/include-restricted/vm_payload_restricted.h
+++ b/vm_payload/include-restricted/vm_payload_restricted.h
@@ -22,6 +22,10 @@
 
 #include "vm_payload.h"
 
+#if !defined(__INTRODUCED_IN)
+#define __INTRODUCED_IN(__api_level) /* nothing */
+#endif
+
 // The functions declared here are restricted to VMs created with a config file;
 // they will fail if called in other VMs. The ability to create such VMs
 // requires the android.permission.USE_CUSTOM_VIRTUAL_MACHINE permission, and is
@@ -51,4 +55,18 @@
  */
 size_t AVmPayload_getDiceAttestationCdi(void* _Nullable data, size_t size);
 
+/**
+ * Requests a certificate using the provided certificate signing request (CSR).
+ *
+ * \param csr A pointer to the CSR buffer.
+ * \param csr_size The size of the CSR buffer.
+ * \param buffer A pointer to the certificate buffer.
+ * \param size number of bytes that can be written to the certificate buffer.
+ *
+ * \return the total size of the certificate
+ */
+size_t AVmPayload_requestCertificate(const void* _Nonnull csr, size_t csr_size,
+                                     void* _Nullable buffer, size_t size)
+        __INTRODUCED_IN(__ANDROID_API_V__);
+
 __END_DECLS
diff --git a/vm_payload/libvm_payload.map.txt b/vm_payload/libvm_payload.map.txt
index a2402d1..f0d867e 100644
--- a/vm_payload/libvm_payload.map.txt
+++ b/vm_payload/libvm_payload.map.txt
@@ -7,6 +7,7 @@
     AVmPayload_getDiceAttestationCdi;    # systemapi
     AVmPayload_getApkContentsPath;       # systemapi
     AVmPayload_getEncryptedStoragePath;  # systemapi
+    AVmPayload_requestCertificate;       # systemapi introduced=35
   local:
     *;
 };
diff --git a/vm_payload/src/api.rs b/vm_payload/src/api.rs
index 4b565e0..6ca473a 100644
--- a/vm_payload/src/api.rs
+++ b/vm_payload/src/api.rs
@@ -256,6 +256,52 @@
     get_vm_payload_service()?.getDiceAttestationCdi().context("Cannot get attestation CDI")
 }
 
+/// Requests a certificate using the provided certificate signing request (CSR).
+/// Panics on failure.
+///
+/// # Safety
+///
+/// Behavior is undefined if any of the following conditions are violated:
+///
+/// * `csr` must be [valid] for reads of `csr_size` bytes.
+/// * `buffer` must be [valid] for writes of `size` bytes. `buffer` can be null if `size` is 0.
+///
+/// [valid]: ptr#safety
+#[no_mangle]
+pub unsafe extern "C" fn AVmPayload_requestCertificate(
+    csr: *const u8,
+    csr_size: usize,
+    buffer: *mut u8,
+    size: usize,
+) -> usize {
+    initialize_logging();
+
+    // SAFETY: See the requirements on `csr` above.
+    let csr = unsafe { std::slice::from_raw_parts(csr, csr_size) };
+    let certificate = unwrap_or_abort(try_request_certificate(csr));
+
+    if size != 0 || buffer.is_null() {
+        // SAFETY: See the requirements on `buffer` above. The number of bytes copied doesn't exceed
+        // the length of either buffer, and `certificate` cannot overlap `buffer` because we just
+        // allocated it.
+        unsafe {
+            ptr::copy_nonoverlapping(
+                certificate.as_ptr(),
+                buffer,
+                std::cmp::min(certificate.len(), size),
+            );
+        }
+    }
+    certificate.len()
+}
+
+fn try_request_certificate(csr: &[u8]) -> Result<Vec<u8>> {
+    let certificate = get_vm_payload_service()?
+        .requestCertificate(csr)
+        .context("Failed to request certificate")?;
+    Ok(certificate)
+}
+
 /// Gets the path to the APK contents.
 #[no_mangle]
 pub extern "C" fn AVmPayload_getApkContentsPath() -> *const c_char {
diff --git a/vm_payload/src/lib.rs b/vm_payload/src/lib.rs
index 5c3ee31..4d059d1 100644
--- a/vm_payload/src/lib.rs
+++ b/vm_payload/src/lib.rs
@@ -17,6 +17,7 @@
 mod api;
 
 pub use api::{
-    AVmPayload_getDiceAttestationCdi, AVmPayload_getDiceAttestationChain,
-    AVmPayload_getVmInstanceSecret, AVmPayload_notifyPayloadReady,
+    AVmPayload_getCertificate, AVmPayload_getDiceAttestationCdi,
+    AVmPayload_getDiceAttestationChain, AVmPayload_getVmInstanceSecret,
+    AVmPayload_notifyPayloadReady,
 };