Allow vendor clients to use AVF API

* virtmgr now checks both clients and origins of FDs
* Early VMs are extended to vendors, regarding early_vms.xsd
* libavf now exports headers to vendor modules

Bug: 369588412
Bug: 383969737
Test: create a vendor binary using libavf and run a vm
Change-Id: Id212f4b9b86504f2081faa4e99baee22b860f481
diff --git a/android/virtmgr/src/aidl.rs b/android/virtmgr/src/aidl.rs
index cbae56b..daa755e 100644
--- a/android/virtmgr/src/aidl.rs
+++ b/android/virtmgr/src/aidl.rs
@@ -137,6 +137,21 @@
 static SUPPORTED_OS_NAMES: LazyLock<HashSet<String>> =
     LazyLock::new(|| get_supported_os_names().expect("Failed to get list of supported os names"));
 
+static CALLING_EXE_PATH: LazyLock<Option<PathBuf>> = LazyLock::new(|| {
+    let calling_exe_link = format!("/proc/{}/exe", get_calling_pid());
+    match fs::read_link(&calling_exe_link) {
+        Ok(calling_exe_path) => Some(calling_exe_path),
+        Err(e) => {
+            // virtmgr forked from apps fails to read /proc probably due to hidepid=2. As we
+            // discourage vendor apps, regarding such cases as system is safer.
+            // TODO(b/383969737): determine if this is okay. Or find a way how to track origins of
+            // apps.
+            warn!("can't read_link '{calling_exe_link}': {e:?}; regarding as system");
+            None
+        }
+    }
+});
+
 fn create_or_update_idsig_file(
     input_fd: &ParcelFileDescriptor,
     idsig_fd: &ParcelFileDescriptor,
@@ -406,7 +421,11 @@
     }
 }
 
-fn find_partition(path: &Path) -> binder::Result<String> {
+fn find_partition(path: Option<&Path>) -> binder::Result<String> {
+    let path = match path {
+        Some(path) => path,
+        None => return Ok("system".to_owned()),
+    };
     let mut components = path.components();
     match components.nth(1) {
         Some(std::path::Component::Normal(partition)) => {
@@ -448,24 +467,27 @@
     fn create_early_vm_context(
         &self,
         config: &VirtualMachineConfig,
+        calling_exe_path: Option<&Path>,
     ) -> binder::Result<(VmContext, Cid, PathBuf)> {
-        let calling_exe_path = format!("/proc/{}/exe", get_calling_pid());
-        let link = fs::read_link(&calling_exe_path)
-            .context(format!("can't read_link '{calling_exe_path}'"))
-            .or_service_specific_exception(-1)?;
-        let partition = find_partition(&link)?;
-
         let name = match config {
             VirtualMachineConfig::RawConfig(config) => &config.name,
             VirtualMachineConfig::AppConfig(config) => &config.name,
         };
-        let early_vm =
-            find_early_vm_for_partition(&partition, name).or_service_specific_exception(-1)?;
-        if Path::new(&early_vm.path) != link {
+        let calling_partition = find_partition(calling_exe_path)?;
+        let early_vm = find_early_vm_for_partition(&calling_partition, name)
+            .or_service_specific_exception(-1)?;
+        let calling_exe_path = match calling_exe_path {
+            Some(path) => path,
+            None => {
+                return Err(anyhow!("Can't verify the path of PID {}", get_calling_pid()))
+                    .or_service_specific_exception(-1)
+            }
+        };
+        if Path::new(&early_vm.path) != calling_exe_path {
             return Err(anyhow!(
-                "VM '{name}' in partition '{partition}' must be created with '{}', not '{}'",
+                "VM '{name}' in partition '{calling_partition}' must be created with '{}', not '{}'",
                 &early_vm.path,
-                link.display()
+                calling_exe_path.display()
             ))
             .or_service_specific_exception(-1);
         }
@@ -549,7 +571,7 @@
 
         // Allocating VM context checks the MANAGE_VIRTUAL_MACHINE permission.
         let (vm_context, cid, temporary_directory) = if cfg!(early) {
-            self.create_early_vm_context(config)?
+            self.create_early_vm_context(config, CALLING_EXE_PATH.as_deref())?
         } else {
             self.create_vm_context(requester_debug_pid, config)?
         };
@@ -649,7 +671,8 @@
         // Check if files for payloads and bases are NOT coming from /vendor and /odm, as they may
         // have unstable interfaces.
         // TODO(b/316431494): remove once Treble interfaces are stabilized.
-        check_partitions_for_files(config).or_service_specific_exception(-1)?;
+        check_partitions_for_files(config, &find_partition(CALLING_EXE_PATH.as_deref())?)
+            .or_service_specific_exception(-1)?;
 
         let zero_filler_path = temporary_directory.join("zero.img");
         write_zero_filler(&zero_filler_path)
@@ -1285,7 +1308,7 @@
     Ok(vm_config)
 }
 
-fn check_partition_for_file(fd: &ParcelFileDescriptor) -> Result<()> {
+fn check_partition_for_file(fd: &ParcelFileDescriptor, calling_partition: &str) -> Result<()> {
     let path = format!("/proc/self/fd/{}", fd.as_raw_fd());
     let link = fs::read_link(&path).context(format!("can't read_link {path}"))?;
 
@@ -1295,24 +1318,39 @@
         return Ok(());
     }
 
-    if link.starts_with("/vendor") || link.starts_with("/odm") {
-        bail!("vendor or odm file {} can't be used for VM", link.display());
+    let is_fd_vendor = link.starts_with("/vendor") || link.starts_with("/odm");
+    let is_caller_vendor = calling_partition == "vendor" || calling_partition == "odm";
+
+    if is_fd_vendor != is_caller_vendor {
+        bail!("{} can't be used for VM client in {calling_partition}", link.display());
     }
 
     Ok(())
 }
 
-fn check_partitions_for_files(config: &VirtualMachineRawConfig) -> Result<()> {
+fn check_partitions_for_files(
+    config: &VirtualMachineRawConfig,
+    calling_partition: &str,
+) -> Result<()> {
     config
         .disks
         .iter()
         .flat_map(|disk| disk.partitions.iter())
         .filter_map(|partition| partition.image.as_ref())
-        .try_for_each(check_partition_for_file)?;
+        .try_for_each(|fd| check_partition_for_file(fd, calling_partition))?;
 
-    config.kernel.as_ref().map_or(Ok(()), check_partition_for_file)?;
-    config.initrd.as_ref().map_or(Ok(()), check_partition_for_file)?;
-    config.bootloader.as_ref().map_or(Ok(()), check_partition_for_file)?;
+    config
+        .disks
+        .iter()
+        .filter_map(|disk| disk.image.as_ref())
+        .try_for_each(|fd| check_partition_for_file(fd, calling_partition))?;
+
+    config.kernel.as_ref().map_or(Ok(()), |fd| check_partition_for_file(fd, calling_partition))?;
+    config.initrd.as_ref().map_or(Ok(()), |fd| check_partition_for_file(fd, calling_partition))?;
+    config
+        .bootloader
+        .as_ref()
+        .map_or(Ok(()), |fd| check_partition_for_file(fd, calling_partition))?;
 
     Ok(())
 }
@@ -2163,6 +2201,7 @@
     match partition {
         "system" => Ok(100..200),
         "system_ext" | "product" => Ok(200..300),
+        "vendor" | "odm" => Ok(300..400),
         _ => Err(anyhow!("Early VMs are not supported for {partition}")),
     }
 }
diff --git a/libs/libavf/Android.bp b/libs/libavf/Android.bp
index e143709..079f4ae 100644
--- a/libs/libavf/Android.bp
+++ b/libs/libavf/Android.bp
@@ -34,7 +34,6 @@
 rust_ffi_static {
     name: "libavf_impl",
     defaults: ["libavf.default"],
-    export_include_dirs: ["include"],
 }
 
 cc_library {
@@ -49,7 +48,7 @@
         "libbinder_rpc_unstable",
         "liblog",
     ],
-    export_static_lib_headers: ["libavf_impl"],
+    export_include_dirs: ["include"],
     apex_available: ["com.android.virt"],
     version_script: "libavf.map.txt",
     stubs: {