Add `vm` tool for talking to Virt Manager.

For now it just has a single command, to run a specified VM.

Bug: 181869875
Test: Started a VM on a VIM3L
Change-Id: Ifdaaccb3be92e774ee4d7672914a3014220af489
diff --git a/apex/Android.bp b/apex/Android.bp
index 0985577..50c17f6 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -29,6 +29,7 @@
     binaries: [
         "assemble_cvd",
         "virtmanager",
+        "vm",
     ],
     filesystems: ["microdroid"],
 }
diff --git a/vm/Android.bp b/vm/Android.bp
new file mode 100644
index 0000000..8fe7ae9
--- /dev/null
+++ b/vm/Android.bp
@@ -0,0 +1,16 @@
+rust_binary {
+    name: "vm",
+    crate_name: "vm",
+    srcs: ["src/main.rs"],
+    edition: "2018",
+    rustlibs: [
+        "android.system.virtmanager-rust",
+        "libanyhow",
+        "libbinder_rs",
+        "libenv_logger",
+        "liblog_rust",
+    ],
+    apex_available: [
+        "com.android.virt",
+    ],
+}
diff --git a/vm/src/main.rs b/vm/src/main.rs
new file mode 100644
index 0000000..1e642cb
--- /dev/null
+++ b/vm/src/main.rs
@@ -0,0 +1,78 @@
+// Copyright 2021, 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.
+
+//! Android VM control tool.
+
+mod sync;
+
+use android_system_virtmanager::aidl::android::system::virtmanager::IVirtManager::IVirtManager;
+use android_system_virtmanager::binder::{get_interface, ProcessState, Strong};
+use anyhow::{bail, Context, Error};
+// TODO: Import these via android_system_virtmanager::binder once https://r.android.com/1619403 is
+// submitted.
+use binder::{DeathRecipient, IBinder};
+use std::env;
+use std::process::exit;
+use sync::AtomicFlag;
+
+const VIRT_MANAGER_BINDER_SERVICE_IDENTIFIER: &str = "android.system.virtmanager";
+
+fn main() -> Result<(), Error> {
+    env_logger::init();
+
+    let args: Vec<_> = env::args().collect();
+    if args.len() < 2 {
+        eprintln!("Usage:");
+        eprintln!("  {} run <vm_config.json>", args[0]);
+        exit(1);
+    }
+
+    // We need to start the thread pool for Binder to work properly, especially link_to_death.
+    ProcessState::start_thread_pool();
+
+    match args[1].as_ref() {
+        "run" if args.len() == 3 => command_run(&args[2]),
+        command => bail!("Invalid command '{}' or wrong number of arguments", command),
+    }
+}
+
+/// Run a VM from the given configuration file.
+fn command_run(config_filename: &str) -> Result<(), Error> {
+    let virt_manager: Strong<dyn IVirtManager> =
+        get_interface(VIRT_MANAGER_BINDER_SERVICE_IDENTIFIER)
+            .with_context(|| "Failed to find Virt Manager service")?;
+    let vm = virt_manager.startVm(config_filename).with_context(|| "Failed to start VM")?;
+    let cid = vm.getCid().with_context(|| "Failed to get CID")?;
+    println!("Started VM from {} with CID {}.", config_filename, cid);
+
+    // Wait until the VM dies. If we just returned immediately then the IVirtualMachine Binder
+    // object would be dropped and the VM would be killed.
+    wait_for_death(&mut vm.as_binder())?;
+    println!("VM died");
+    Ok(())
+}
+
+/// Block until the given Binder object dies.
+fn wait_for_death(binder: &mut impl IBinder) -> Result<(), Error> {
+    let dead = AtomicFlag::default();
+    let mut death_recipient = {
+        let dead = dead.clone();
+        DeathRecipient::new(move || {
+            dead.raise();
+        })
+    };
+    binder.link_to_death(&mut death_recipient)?;
+    dead.wait();
+    Ok(())
+}
diff --git a/vm/src/sync.rs b/vm/src/sync.rs
new file mode 100644
index 0000000..82839b3
--- /dev/null
+++ b/vm/src/sync.rs
@@ -0,0 +1,46 @@
+// Copyright 2021, 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.
+
+//! Synchronisation utilities.
+
+use std::sync::{Arc, Condvar, Mutex};
+
+/// A flag which one thread can use to notify other threads when a condition becomes true. This is
+/// something like a single-use binary semaphore.
+#[derive(Clone, Debug)]
+pub struct AtomicFlag {
+    state: Arc<(Mutex<bool>, Condvar)>,
+}
+
+impl Default for AtomicFlag {
+    #[allow(clippy::mutex_atomic)]
+    fn default() -> Self {
+        Self { state: Arc::new((Mutex::new(false), Condvar::new())) }
+    }
+}
+
+#[allow(clippy::mutex_atomic)]
+impl AtomicFlag {
+    /// Wait until the flag is set.
+    pub fn wait(&self) {
+        let _flag = self.state.1.wait_while(self.state.0.lock().unwrap(), |flag| !*flag).unwrap();
+    }
+
+    /// Set the flag, and notify all waiting threads.
+    pub fn raise(&self) {
+        let mut flag = self.state.0.lock().unwrap();
+        *flag = true;
+        self.state.1.notify_all();
+    }
+}