Merge "Don't start composd if VMs are not supported"
diff --git a/compos/composd_cmd/Android.bp b/compos/composd_cmd/Android.bp
index 54b0bad..77caad8 100644
--- a/compos/composd_cmd/Android.bp
+++ b/compos/composd_cmd/Android.bp
@@ -2,8 +2,8 @@
     default_applicable_licenses: ["Android-Apache-2.0"],
 }
 
-rust_binary {
-    name: "composd_cmd",
+rust_defaults {
+    name: "composd_cmd_defaults",
     srcs: ["composd_cmd.rs"],
     edition: "2021",
     rustlibs: [
@@ -12,8 +12,14 @@
         "libbinder_rs",
         "libclap",
         "libcompos_common",
+        "libhypervisor_props",
     ],
     prefer_rlib: true,
+}
+
+rust_binary {
+    name: "composd_cmd",
+    defaults: ["composd_cmd_defaults"],
     apex_available: [
         "com.android.compos",
     ],
@@ -21,15 +27,6 @@
 
 rust_test {
     name: "composd_cmd.test",
-    srcs: ["composd_cmd.rs"],
-    edition: "2021",
-    rustlibs: [
-        "android.system.composd-rust",
-        "libanyhow",
-        "libbinder_rs",
-        "libclap",
-        "libcompos_common",
-    ],
-    prefer_rlib: true,
+    defaults: ["composd_cmd_defaults"],
     test_suites: ["general-tests"],
 }
diff --git a/compos/composd_cmd/composd_cmd.rs b/compos/composd_cmd/composd_cmd.rs
index 19c3720..6d096a1 100644
--- a/compos/composd_cmd/composd_cmd.rs
+++ b/compos/composd_cmd/composd_cmd.rs
@@ -128,6 +128,12 @@
         &Strong<dyn ICompilationTaskCallback>,
     ) -> BinderResult<Strong<dyn ICompilationTask>>,
 {
+    if !hypervisor_props::is_any_vm_supported()? {
+        // Give up now, before trying to start composd, or we may end up waiting forever
+        // as it repeatedly starts and then aborts (b/254599807).
+        bail!("Device doesn't support protected or non-protected VMs")
+    }
+
     let service = wait_for_interface::<dyn IIsolatedCompilationService>("android.system.composd")
         .context("Failed to connect to composd service")?;
 
diff --git a/libs/hypervisor_props/Android.bp b/libs/hypervisor_props/Android.bp
new file mode 100644
index 0000000..af08b01
--- /dev/null
+++ b/libs/hypervisor_props/Android.bp
@@ -0,0 +1,18 @@
+package {
+    default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_library {
+    name: "libhypervisor_props",
+    crate_name: "hypervisor_props",
+    srcs: ["src/lib.rs"],
+    edition: "2021",
+    rustlibs: [
+        "libanyhow",
+        "librustutils",
+    ],
+    apex_available: [
+        "com.android.compos",
+        "com.android.virt",
+    ],
+}
diff --git a/libs/hypervisor_props/src/lib.rs b/libs/hypervisor_props/src/lib.rs
new file mode 100644
index 0000000..120a48c
--- /dev/null
+++ b/libs/hypervisor_props/src/lib.rs
@@ -0,0 +1,40 @@
+// 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.
+
+//! Access to hypervisor capabilities via system properties set by the bootloader.
+
+use anyhow::{Error, Result};
+use rustutils::system_properties;
+
+/// Returns whether there is a hypervisor present that supports non-protected VMs.
+pub fn is_vm_supported() -> Result<bool> {
+    system_properties::read_bool("ro.boot.hypervisor.vm.supported", false).map_err(Error::new)
+}
+
+/// Returns whether there is a hypervisor present that supports protected VMs.
+pub fn is_protected_vm_supported() -> Result<bool> {
+    system_properties::read_bool("ro.boot.hypervisor.protected_vm.supported", false)
+        .map_err(Error::new)
+}
+
+/// Returns whether there is a hypervisor present that supports any sort of VM, either protected
+/// or non-protected.
+pub fn is_any_vm_supported() -> Result<bool> {
+    is_vm_supported().and_then(|ok| if ok { Ok(true) } else { is_protected_vm_supported() })
+}
+
+/// Returns the version of the hypervisor, if there is one.
+pub fn version() -> Result<Option<String>> {
+    system_properties::read("ro.boot.hypervisor.version").map_err(Error::new)
+}
diff --git a/virtualizationmanager/Android.bp b/virtualizationmanager/Android.bp
index a436cea..e82797e 100644
--- a/virtualizationmanager/Android.bp
+++ b/virtualizationmanager/Android.bp
@@ -32,6 +32,7 @@
         "libclap",
         "libcommand_fds",
         "libdisk",
+        "libhypervisor_props",
         "liblazy_static",
         "liblibc",
         "liblog_rust",
diff --git a/virtualizationmanager/src/main.rs b/virtualizationmanager/src/main.rs
index dca64cb..3f0b64b 100644
--- a/virtualizationmanager/src/main.rs
+++ b/virtualizationmanager/src/main.rs
@@ -23,7 +23,7 @@
 
 use crate::aidl::{GLOBAL_SERVICE, VirtualizationService};
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualizationService::BnVirtualizationService;
-use anyhow::{bail, Context};
+use anyhow::{bail, Context, Result};
 use binder::{BinderFeatures, ProcessState};
 use lazy_static::lazy_static;
 use log::{info, Level};
@@ -33,7 +33,6 @@
 use nix::fcntl::{fcntl, F_GETFD, F_SETFD, FdFlag};
 use nix::unistd::{Pid, Uid};
 use std::os::unix::raw::{pid_t, uid_t};
-use rustutils::system_properties;
 
 const LOG_TAG: &str = "virtmgr";
 
@@ -92,9 +91,15 @@
     Ok(unsafe { OwnedFd::from_raw_fd(raw_fd) })
 }
 
-fn is_property_set(name: &str) -> bool {
-    system_properties::read_bool(name, false)
-        .unwrap_or_else(|e| panic!("Failed to read {name}: {e:?}"))
+fn check_vm_support() -> Result<()> {
+    if hypervisor_props::is_any_vm_supported()? {
+        Ok(())
+    } else {
+        // This should never happen, it indicates a misconfigured device where the virt APEX
+        // is present but VMs are not supported. If it does happen, fail fast to avoid wasting
+        // resources trying.
+        bail!("Device doesn't support protected or non-protected VMs")
+    }
 }
 
 fn main() {
@@ -105,14 +110,7 @@
             .with_log_id(android_logger::LogId::System),
     );
 
-    let non_protected_vm_supported = is_property_set("ro.boot.hypervisor.vm.supported");
-    let protected_vm_supported = is_property_set("ro.boot.hypervisor.protected_vm.supported");
-    if !non_protected_vm_supported && !protected_vm_supported {
-        // This should never happen, it indicates a misconfigured device where the virt APEX
-        // is present but VMs are not supported. If it does happen, fail fast to avoid wasting
-        // resources trying.
-        panic!("Device doesn't support protected or unprotected VMs");
-    }
+    check_vm_support().unwrap();
 
     let args = Args::parse();
 
diff --git a/vm/Android.bp b/vm/Android.bp
index e217786..50e68cc 100644
--- a/vm/Android.bp
+++ b/vm/Android.bp
@@ -15,11 +15,11 @@
         "libclap",
         "libenv_logger",
         "libglob",
+        "libhypervisor_props",
         "liblibc",
         "liblog_rust",
         "libmicrodroid_payload_config",
         "librand",
-        "librustutils",
         "libserde_json",
         "libserde",
         "libvmconfig",
diff --git a/vm/src/main.rs b/vm/src/main.rs
index e1c3413..6c08a19 100644
--- a/vm/src/main.rs
+++ b/vm/src/main.rs
@@ -23,12 +23,11 @@
     PartitionType::PartitionType, VirtualMachineAppConfig::DebugLevel::DebugLevel,
 };
 use anyhow::{Context, Error};
-use binder::ProcessState;
+use binder::{ProcessState, Strong};
 use clap::Parser;
 use create_idsig::command_create_idsig;
 use create_partition::command_create_partition;
 use run::{command_run, command_run_app, command_run_microdroid};
-use rustutils::system_properties;
 use std::path::{Path, PathBuf};
 
 #[derive(Debug)]
@@ -230,6 +229,12 @@
     }
 }
 
+fn get_service() -> Result<Strong<dyn IVirtualizationService>, Error> {
+    let virtmgr =
+        vmclient::VirtualizationService::new().context("Failed to spawn VirtualizationService")?;
+    virtmgr.connect().context("Failed to connect to VirtualizationService")
+}
+
 fn main() -> Result<(), Error> {
     env_logger::init();
     let opt = Opt::parse();
@@ -237,10 +242,6 @@
     // We need to start the thread pool for Binder to work properly, especially link_to_death.
     ProcessState::start_thread_pool();
 
-    let virtmgr =
-        vmclient::VirtualizationService::new().context("Failed to spawn VirtualizationService")?;
-    let service = virtmgr.connect().context("Failed to connect to VirtualizationService")?;
-
     match opt {
         Opt::RunApp {
             name,
@@ -261,7 +262,7 @@
             extra_idsigs,
         } => command_run_app(
             name,
-            service.as_ref(),
+            get_service()?.as_ref(),
             &apk,
             &idsig,
             &instance,
@@ -292,7 +293,7 @@
             task_profiles,
         } => command_run_microdroid(
             name,
-            service.as_ref(),
+            get_service()?.as_ref(),
             work_dir,
             storage.as_deref(),
             storage_size,
@@ -307,7 +308,7 @@
         Opt::Run { name, config, cpu_topology, task_profiles, console, log } => {
             command_run(
                 name,
-                service.as_ref(),
+                get_service()?.as_ref(),
                 &config,
                 console.as_deref(),
                 log.as_deref(),
@@ -316,12 +317,14 @@
                 task_profiles,
             )
         }
-        Opt::List => command_list(service.as_ref()),
+        Opt::List => command_list(get_service()?.as_ref()),
         Opt::Info => command_info(),
         Opt::CreatePartition { path, size, partition_type } => {
-            command_create_partition(service.as_ref(), &path, size, partition_type)
+            command_create_partition(get_service()?.as_ref(), &path, size, partition_type)
         }
-        Opt::CreateIdsig { apk, path } => command_create_idsig(service.as_ref(), &apk, &path),
+        Opt::CreateIdsig { apk, path } => {
+            command_create_idsig(get_service()?.as_ref(), &apk, &path)
+        }
     }
 }
 
@@ -334,10 +337,8 @@
 
 /// Print information about supported VM types.
 fn command_info() -> Result<(), Error> {
-    let non_protected_vm_supported =
-        system_properties::read_bool("ro.boot.hypervisor.vm.supported", false)?;
-    let protected_vm_supported =
-        system_properties::read_bool("ro.boot.hypervisor.protected_vm.supported", false)?;
+    let non_protected_vm_supported = hypervisor_props::is_vm_supported()?;
+    let protected_vm_supported = hypervisor_props::is_protected_vm_supported()?;
     match (non_protected_vm_supported, protected_vm_supported) {
         (false, false) => println!("VMs are not supported."),
         (false, true) => println!("Only protected VMs are supported."),
@@ -345,7 +346,7 @@
         (true, true) => println!("Both protected and non-protected VMs are supported."),
     }
 
-    if let Some(version) = system_properties::read("ro.boot.hypervisor.version")? {
+    if let Some(version) = hypervisor_props::version()? {
         println!("Hypervisor version: {}", version);
     } else {
         println!("Hypervisor version not set.");