Merge "pvmfw: README: Document config data format"
diff --git a/apex/Android.bp b/apex/Android.bp
index 579d7c7..d5f485b 100644
--- a/apex/Android.bp
+++ b/apex/Android.bp
@@ -73,6 +73,7 @@
         arm64: {
             binaries: [
                 "crosvm",
+                "virtmgr",
                 "virtualizationservice",
             ],
             filesystems: microdroid_filesystem_images,
@@ -80,6 +81,7 @@
         x86_64: {
             binaries: [
                 "crosvm",
+                "virtmgr",
                 "virtualizationservice",
             ],
             filesystems: microdroid_filesystem_images,
diff --git a/authfs/src/file.rs b/authfs/src/file.rs
index aff47c5..55c783b 100644
--- a/authfs/src/file.rs
+++ b/authfs/src/file.rs
@@ -9,7 +9,7 @@
 use crate::common::{divide_roundup, CHUNK_SIZE};
 use authfs_aidl_interface::aidl::com::android::virt::fs::IVirtFdService::IVirtFdService;
 use binder::{Status, StatusCode, Strong};
-use rpcbinder::get_vsock_rpc_interface;
+use rpcbinder::RpcSession;
 use std::convert::TryFrom;
 use std::io;
 use std::path::{Path, MAIN_SEPARATOR};
@@ -22,7 +22,7 @@
 pub const RPC_SERVICE_PORT: u32 = 3264;
 
 pub fn get_rpc_binder_service(cid: u32) -> io::Result<VirtFdService> {
-    get_vsock_rpc_interface(cid, RPC_SERVICE_PORT).map_err(|e| match e {
+    RpcSession::new().setup_vsock_client(cid, RPC_SERVICE_PORT).map_err(|e| match e {
         StatusCode::BAD_VALUE => {
             io::Error::new(io::ErrorKind::InvalidInput, "Invalid raw AIBinder")
         }
diff --git a/compos/src/compsvc.rs b/compos/src/compsvc.rs
index 40d14d8..8febd52 100644
--- a/compos/src/compsvc.rs
+++ b/compos/src/compsvc.rs
@@ -39,7 +39,7 @@
 };
 use compos_common::binder::to_binder_result;
 use compos_common::odrefresh::{is_system_property_interesting, ODREFRESH_PATH};
-use rpcbinder::get_unix_domain_rpc_interface;
+use rpcbinder::RpcSession;
 
 /// Constructs a binder object that implements ICompOsService.
 pub fn new_binder() -> Result<Strong<dyn ICompOsService>> {
@@ -130,9 +130,9 @@
 impl CompOsService {
     fn do_odrefresh(&self, args: &OdrefreshArgs) -> Result<i8> {
         log::debug!("Prepare to connect to {}", AUTHFS_SERVICE_SOCKET_NAME);
-        let authfs_service: Strong<dyn IAuthFsService> =
-            get_unix_domain_rpc_interface(AUTHFS_SERVICE_SOCKET_NAME)
-                .with_context(|| format!("Failed to connect to {}", AUTHFS_SERVICE_SOCKET_NAME))?;
+        let authfs_service: Strong<dyn IAuthFsService> = RpcSession::new()
+            .setup_unix_domain_client(AUTHFS_SERVICE_SOCKET_NAME)
+            .with_context(|| format!("Failed to connect to {}", AUTHFS_SERVICE_SOCKET_NAME))?;
         let exit_code = odrefresh(&self.odrefresh_path, args, authfs_service, |output_dir| {
             // authfs only shows us the files we created, so it's ok to just sign everything
             // under the output directory.
diff --git a/javalib/jni/android_system_virtualmachine_VirtualMachine.cpp b/javalib/jni/android_system_virtualmachine_VirtualMachine.cpp
index 7234dad..a949d46 100644
--- a/javalib/jni/android_system_virtualmachine_VirtualMachine.cpp
+++ b/javalib/jni/android_system_virtualmachine_VirtualMachine.cpp
@@ -55,7 +55,12 @@
         return ret;
     };
 
-    return AIBinder_toJavaBinder(env, RpcPreconnectedClient(requestFunc, &args));
+    auto session = ARpcSession_new();
+    auto client = ARpcSession_setupPreconnectedClient(session, requestFunc, &args);
+    auto obj = AIBinder_toJavaBinder(env, client);
+    // Free the NDK handle. The underlying RpcSession object remains alive.
+    ARpcSession_free(session);
+    return obj;
 }
 
 JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* /*reserved*/) {
diff --git a/microdroid_manager/src/main.rs b/microdroid_manager/src/main.rs
index 6a37b88..3fa7a2a 100644
--- a/microdroid_manager/src/main.rs
+++ b/microdroid_manager/src/main.rs
@@ -46,7 +46,7 @@
 use openssl::sha::Sha512;
 use payload::{get_apex_data_from_payload, load_metadata, to_metadata};
 use rand::Fill;
-use rpcbinder::get_vsock_rpc_interface;
+use rpcbinder::RpcSession;
 use rustutils::sockets::android_get_control_socket;
 use rustutils::system_properties;
 use rustutils::system_properties::PropertyWatcher;
@@ -160,7 +160,8 @@
     // The host is running a VirtualMachineService for this VM on a port equal
     // to the CID of this VM.
     let port = vsock::get_local_cid().context("Could not determine local CID")?;
-    get_vsock_rpc_interface(VMADDR_CID_HOST, port)
+    RpcSession::new()
+        .setup_vsock_client(VMADDR_CID_HOST, port)
         .context("Could not connect to IVirtualMachineService")
 }
 
diff --git a/pvmfw/src/config.rs b/pvmfw/src/config.rs
index b633745..f209784 100644
--- a/pvmfw/src/config.rs
+++ b/pvmfw/src/config.rs
@@ -20,13 +20,19 @@
 use core::ops::Range;
 use core::result;
 
+/// Configuration data header.
 #[repr(C, packed)]
 #[derive(Clone, Copy, Debug)]
 struct Header {
+    /// Magic number; must be `Header::MAGIC`.
     magic: u32,
+    /// Version of the header format.
     version: u32,
+    /// Total size of the configuration data.
     total_size: u32,
+    /// Feature flags; currently reserved and must be zero.
     flags: u32,
+    /// (offset, size) pairs used to locate individual entries appended to the header.
     entries: [HeaderEntry; Entry::COUNT],
 }
 
@@ -90,6 +96,7 @@
 
 impl Header {
     const MAGIC: u32 = u32::from_ne_bytes(*b"pvmf");
+    const VERSION_1_0: u32 = Self::version(1, 0);
     const PADDED_SIZE: usize =
         helpers::unchecked_align_up(mem::size_of::<Self>(), mem::size_of::<u64>());
 
@@ -186,7 +193,7 @@
             return Err(Error::InvalidMagic);
         }
 
-        if header.version != Header::version(1, 0) {
+        if header.version != Header::VERSION_1_0 {
             let (major, minor) = header.version_tuple();
             return Err(Error::UnsupportedVersion(major, minor));
         }
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index da56f76..d0dde42 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -5,7 +5,6 @@
 rust_defaults {
     name: "virtualizationservice_defaults",
     crate_name: "virtualizationservice",
-    srcs: ["src/main.rs"],
     edition: "2021",
     // Only build on targets which crosvm builds on.
     enabled: false,
@@ -67,11 +66,23 @@
 rust_binary {
     name: "virtualizationservice",
     defaults: ["virtualizationservice_defaults"],
+    srcs: ["src/main.rs"],
+    apex_available: ["com.android.virt"],
+}
+
+rust_binary {
+    name: "virtmgr",
+    defaults: ["virtualizationservice_defaults"],
+    srcs: ["src/virtmgr.rs"],
+    rustlibs: [
+        "libclap",
+    ],
     apex_available: ["com.android.virt"],
 }
 
 rust_test {
     name: "virtualizationservice_device_test",
+    srcs: ["src/main.rs"],
     defaults: ["virtualizationservice_defaults"],
     rustlibs: [
         "libtempfile",
diff --git a/virtualizationservice/src/aidl.rs b/virtualizationservice/src/aidl.rs
index 3e7eca1..d7c7125 100644
--- a/virtualizationservice/src/aidl.rs
+++ b/virtualizationservice/src/aidl.rs
@@ -14,6 +14,7 @@
 
 //! Implementation of the AIDL interface of the VirtualizationService.
 
+use crate::{get_calling_pid, get_calling_uid};
 use crate::atom::{
     forward_vm_booted_atom, forward_vm_creation_atom, forward_vm_exited_atom,
     write_vm_booted_stats, write_vm_creation_stats};
@@ -55,7 +56,7 @@
 use apkverify::{HashAlgorithm, V4Signature};
 use binder::{
     self, BinderFeatures, ExceptionCode, Interface, LazyServiceGuard, ParcelFileDescriptor,
-    Status, StatusCode, Strong, ThreadState,
+    Status, StatusCode, Strong,
 };
 use disk::QcowFile;
 use lazy_static::lazy_static;
@@ -82,8 +83,6 @@
 /// The unique ID of a VM used (together with a port number) for vsock communication.
 pub type Cid = u32;
 
-pub const BINDER_SERVICE_IDENTIFIER: &str = "android.system.virtualizationservice";
-
 /// Directory in which to write disk image files used while running VMs.
 pub const TEMPORARY_DIRECTORY: &str = "/data/misc/virtualizationservice";
 
@@ -550,8 +549,8 @@
         let state = &mut *self.state.lock().unwrap();
         let console_fd = console_fd.map(clone_file).transpose()?;
         let log_fd = log_fd.map(clone_file).transpose()?;
-        let requester_uid = ThreadState::get_calling_uid();
-        let requester_debug_pid = ThreadState::get_calling_pid();
+        let requester_uid = get_calling_uid();
+        let requester_debug_pid = get_calling_pid();
 
         // Counter to generate unique IDs for temporary image files.
         let mut next_temporary_image_id = 0;
@@ -877,8 +876,8 @@
 
 /// Checks whether the caller has a specific permission
 fn check_permission(perm: &str) -> binder::Result<()> {
-    let calling_pid = ThreadState::get_calling_pid();
-    let calling_uid = ThreadState::get_calling_uid();
+    let calling_pid = get_calling_pid();
+    let calling_uid = get_calling_uid();
     // Root can do anything
     if calling_uid == 0 {
         return Ok(());
diff --git a/virtualizationservice/src/atom.rs b/virtualizationservice/src/atom.rs
index 698dbca..e430c74 100644
--- a/virtualizationservice/src/atom.rs
+++ b/virtualizationservice/src/atom.rs
@@ -16,6 +16,7 @@
 
 use crate::aidl::{clone_file, GLOBAL_SERVICE};
 use crate::crosvm::VmMetric;
+use crate::get_calling_uid;
 use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::DeathReason::DeathReason;
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
     IVirtualMachine::IVirtualMachine,
@@ -29,7 +30,7 @@
     AtomVmExited::AtomVmExited,
 };
 use anyhow::{anyhow, Result};
-use binder::{ParcelFileDescriptor, ThreadState};
+use binder::ParcelFileDescriptor;
 use log::{trace, warn};
 use microdroid_payload_config::VmPayloadConfig;
 use rustutils::system_properties;
@@ -112,7 +113,7 @@
     };
 
     let atom = AtomVmCreationRequested {
-        uid: ThreadState::get_calling_uid() as i32,
+        uid: get_calling_uid() as i32,
         vmIdentifier: vm_identifier,
         isProtected: is_protected,
         creationSucceeded: creation_succeeded,
diff --git a/virtualizationservice/src/main.rs b/virtualizationservice/src/main.rs
index 714bcfd..9272e65 100644
--- a/virtualizationservice/src/main.rs
+++ b/virtualizationservice/src/main.rs
@@ -21,16 +21,27 @@
 mod payload;
 mod selinux;
 
-use crate::aidl::{VirtualizationService, BINDER_SERVICE_IDENTIFIER, TEMPORARY_DIRECTORY};
+use crate::aidl::{VirtualizationService, TEMPORARY_DIRECTORY};
 use android_logger::{Config, FilterBuilder};
 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualizationService::BnVirtualizationService;
 use anyhow::{bail, Context, Error};
-use binder::{register_lazy_service, BinderFeatures, ProcessState};
+use binder::{register_lazy_service, BinderFeatures, ProcessState, ThreadState};
 use log::{info, Level};
 use std::fs::{remove_dir_all, remove_file, read_dir};
+use std::os::unix::raw::{pid_t, uid_t};
 
 const LOG_TAG: &str = "VirtualizationService";
 
+const BINDER_SERVICE_IDENTIFIER: &str = "android.system.virtualizationservice";
+
+fn get_calling_pid() -> pid_t {
+    ThreadState::get_calling_pid()
+}
+
+fn get_calling_uid() -> uid_t {
+    ThreadState::get_calling_uid()
+}
+
 fn main() {
     android_logger::init_once(
         Config::default()
diff --git a/virtualizationservice/src/virtmgr.rs b/virtualizationservice/src/virtmgr.rs
new file mode 100644
index 0000000..1aa3df9
--- /dev/null
+++ b/virtualizationservice/src/virtmgr.rs
@@ -0,0 +1,120 @@
+// Copyright 2022, 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 Virtualization Manager
+
+mod aidl;
+mod atom;
+mod composite;
+mod crosvm;
+mod payload;
+mod selinux;
+
+use crate::aidl::VirtualizationService;
+use android_system_virtualizationservice::aidl::android::system::virtualizationservice::IVirtualizationService::BnVirtualizationService;
+use anyhow::{bail, Context};
+use binder::BinderFeatures;
+use lazy_static::lazy_static;
+use log::{info, Level};
+use rpcbinder::{FileDescriptorTransportMode, RpcServer};
+use std::os::unix::io::{FromRawFd, OwnedFd, RawFd};
+use clap::Parser;
+use nix::unistd::{Pid, Uid};
+use std::os::unix::raw::{pid_t, uid_t};
+
+const LOG_TAG: &str = "virtmgr";
+
+lazy_static! {
+    static ref PID_PARENT: Pid = Pid::parent();
+    static ref UID_CURRENT: Uid = Uid::current();
+}
+
+fn get_calling_pid() -> pid_t {
+    // The caller is the parent of this process.
+    PID_PARENT.as_raw()
+}
+
+fn get_calling_uid() -> uid_t {
+    // The caller and this process share the same UID.
+    UID_CURRENT.as_raw()
+}
+
+#[derive(Parser)]
+struct Args {
+    /// File descriptor inherited from the caller to run RpcBinder server on.
+    /// This should be one end of a socketpair() compatible with RpcBinder's
+    /// UDS bootstrap transport.
+    #[clap(long)]
+    rpc_server_fd: RawFd,
+    /// File descriptor inherited from the caller to signal RpcBinder server
+    /// readiness. This should be one end of pipe() and the caller should be
+    /// waiting for HUP on the other end.
+    #[clap(long)]
+    ready_fd: RawFd,
+}
+
+fn take_fd_ownership(raw_fd: RawFd, owned_fds: &mut Vec<RawFd>) -> Result<OwnedFd, anyhow::Error> {
+    // Basic check that the integer value does correspond to a file descriptor.
+    nix::fcntl::fcntl(raw_fd, nix::fcntl::F_GETFD)
+        .with_context(|| format!("Invalid file descriptor {raw_fd}"))?;
+
+    // Creating OwnedFd for stdio FDs is not safe.
+    if [libc::STDIN_FILENO, libc::STDOUT_FILENO, libc::STDERR_FILENO].contains(&raw_fd) {
+        bail!("File descriptor {raw_fd} is standard I/O descriptor");
+    }
+
+    // Reject RawFds that already have a corresponding OwnedFd.
+    if owned_fds.contains(&raw_fd) {
+        bail!("File descriptor {raw_fd} already owned");
+    }
+    owned_fds.push(raw_fd);
+
+    // SAFETY - Initializing OwnedFd for a RawFd provided in cmdline arguments.
+    // We checked that the integer value corresponds to a valid FD and that this
+    // is the first argument to claim its ownership.
+    Ok(unsafe { OwnedFd::from_raw_fd(raw_fd) })
+}
+
+fn main() {
+    android_logger::init_once(
+        android_logger::Config::default()
+            .with_tag(LOG_TAG)
+            .with_min_level(Level::Info)
+            .with_log_id(android_logger::LogId::System),
+    );
+
+    let args = Args::parse();
+
+    let mut owned_fds = vec![];
+    let rpc_server_fd = take_fd_ownership(args.rpc_server_fd, &mut owned_fds)
+        .expect("Failed to take ownership of rpc_server_fd");
+    let ready_fd = take_fd_ownership(args.ready_fd, &mut owned_fds)
+        .expect("Failed to take ownership of ready_fd");
+
+    let service = VirtualizationService::init();
+    let service =
+        BnVirtualizationService::new_binder(service, BinderFeatures::default()).as_binder();
+
+    let server = RpcServer::new_unix_domain_bootstrap(service, rpc_server_fd)
+        .expect("Failed to start RpcServer");
+    server.set_supported_file_descriptor_transport_modes(&[FileDescriptorTransportMode::Unix]);
+
+    info!("Started VirtualizationService RpcServer. Ready to accept connections");
+
+    // Signal readiness to the caller by closing our end of the pipe.
+    drop(ready_fd);
+
+    server.join();
+    info!("Shutting down VirtualizationService RpcServer");
+}
diff --git a/vm_payload/src/api.rs b/vm_payload/src/api.rs
index a9b3abb..4b565e0 100644
--- a/vm_payload/src/api.rs
+++ b/vm_payload/src/api.rs
@@ -23,7 +23,7 @@
 use binder::{Strong, unstable_api::{AIBinder, new_spibinder}};
 use lazy_static::lazy_static;
 use log::{error, info, Level};
-use rpcbinder::{get_unix_domain_rpc_interface, RpcServer};
+use rpcbinder::{RpcSession, RpcServer};
 use std::convert::Infallible;
 use std::ffi::CString;
 use std::fmt::Debug;
@@ -49,10 +49,9 @@
     if let Some(strong) = &*connection {
         Ok(strong.clone())
     } else {
-        let new_connection: Strong<dyn IVmPayloadService> = get_unix_domain_rpc_interface(
-            VM_PAYLOAD_SERVICE_SOCKET_NAME,
-        )
-        .context(format!("Failed to connect to service: {}", VM_PAYLOAD_SERVICE_SOCKET_NAME))?;
+        let new_connection: Strong<dyn IVmPayloadService> = RpcSession::new()
+            .setup_unix_domain_client(VM_PAYLOAD_SERVICE_SOCKET_NAME)
+            .context(format!("Failed to connect to service: {}", VM_PAYLOAD_SERVICE_SOCKET_NAME))?;
         *connection = Some(new_connection.clone());
         Ok(new_connection)
     }
diff --git a/vmclient/src/lib.rs b/vmclient/src/lib.rs
index 2f22316..a9c2619 100644
--- a/vmclient/src/lib.rs
+++ b/vmclient/src/lib.rs
@@ -40,7 +40,7 @@
     },
 };
 use log::warn;
-use rpcbinder::get_preconnected_rpc_interface;
+use rpcbinder::RpcSession;
 use std::{
     fmt::{self, Debug, Formatter},
     fs::File,
@@ -178,7 +178,7 @@
         &self,
         port: u32,
     ) -> Result<Strong<T>, StatusCode> {
-        get_preconnected_rpc_interface(|| {
+        RpcSession::new().setup_preconnected_client(|| {
             match self.vm.connectVsock(port as i32) {
                 Ok(vsock) => {
                     // Ownership of the fd is transferred to binder