Move VsockFactory into VM client library.

Test: ComposHostTestCases compos_key_tests
Change-Id: I0500c9ad46df4c004f033161ade53b94d3d2afea
diff --git a/vmclient/Android.bp b/vmclient/Android.bp
index 3310ec6..8ad5adf 100644
--- a/vmclient/Android.bp
+++ b/vmclient/Android.bp
@@ -9,9 +9,14 @@
     edition: "2021",
     rustlibs: [
         "android.system.virtualizationservice-rust",
+        "libbinder_rpc_unstable_bindgen",
+        "libbinder_rs",
         "liblog_rust",
         "libthiserror",
     ],
+    shared_libs: [
+        "libbinder_rpc_unstable",
+    ],
     apex_available: [
         "com.android.compos",
         "com.android.virt",
diff --git a/vmclient/src/errors.rs b/vmclient/src/errors.rs
index b9de868..532706d 100644
--- a/vmclient/src/errors.rs
+++ b/vmclient/src/errors.rs
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 use super::DeathReason;
+use android_system_virtualizationservice::binder::StatusCode;
 use thiserror::Error;
 
 /// An error while waiting for a VM to do something.
@@ -31,3 +32,14 @@
     #[error("VM payload finished.")]
     Finished,
 }
+
+/// An error connection to a VM RPC Binder service.
+#[derive(Clone, Debug, Error)]
+pub enum GetServiceError {
+    /// The RPC binder connection failed.
+    #[error("Vsock connection to RPC binder failed.")]
+    ConnectionFailed,
+    /// The AIDL service type didn't match.
+    #[error("Service type didn't match ({0}).")]
+    WrongServiceType(StatusCode),
+}
diff --git a/vmclient/src/lib.rs b/vmclient/src/lib.rs
index 888092f..d182b60 100644
--- a/vmclient/src/lib.rs
+++ b/vmclient/src/lib.rs
@@ -16,11 +16,12 @@
 
 mod death_reason;
 mod errors;
+mod rpc_binder;
 mod sync;
 
 pub use crate::death_reason::DeathReason;
-pub use crate::errors::VmWaitError;
-use crate::sync::Monitor;
+pub use crate::errors::{GetServiceError, VmWaitError};
+use crate::{rpc_binder::VsockFactory, sync::Monitor};
 use android_system_virtualizationservice::{
     aidl::android::system::virtualizationservice::{
         DeathReason::DeathReason as AidlDeathReason,
@@ -31,7 +32,7 @@
         VirtualMachineState::VirtualMachineState,
     },
     binder::{
-        wait_for_interface, BinderFeatures, DeathRecipient, IBinder, Interface,
+        wait_for_interface, BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface,
         ParcelFileDescriptor, Result as BinderResult, StatusCode, Strong,
     },
 };
@@ -130,6 +131,18 @@
             Ok(())
         }
     }
+
+    /// Tries to connect to an RPC Binder service provided by the VM on the given vsock port.
+    pub fn get_service<T: FromIBinder + ?Sized>(
+        &self,
+        port: u32,
+    ) -> Result<Strong<T>, GetServiceError> {
+        let mut vsock_factory = VsockFactory::new(&*self.vm, port);
+
+        let ibinder = vsock_factory.connect_rpc_client()?;
+
+        FromIBinder::try_from(ibinder).map_err(GetServiceError::WrongServiceType)
+    }
 }
 
 impl Debug for VmInstance {
diff --git a/vmclient/src/rpc_binder.rs b/vmclient/src/rpc_binder.rs
new file mode 100644
index 0000000..fee643f
--- /dev/null
+++ b/vmclient/src/rpc_binder.rs
@@ -0,0 +1,72 @@
+// 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.
+
+use crate::errors::GetServiceError;
+use android_system_virtualizationservice::{
+    aidl::android::system::virtualizationservice::IVirtualMachine::IVirtualMachine,
+};
+use binder::unstable_api::{new_spibinder, AIBinder};
+use log::warn;
+use std::os::{raw, unix::io::IntoRawFd};
+
+pub struct VsockFactory<'a> {
+    vm: &'a dyn IVirtualMachine,
+    port: u32,
+}
+
+impl<'a> VsockFactory<'a> {
+    pub fn new(vm: &'a dyn IVirtualMachine, port: u32) -> Self {
+        Self { vm, port }
+    }
+
+    pub fn connect_rpc_client(&mut self) -> Result<binder::SpIBinder, GetServiceError> {
+        let param = self.as_void_ptr();
+
+        unsafe {
+            // SAFETY: AIBinder returned by RpcPreconnectedClient has correct reference count, and
+            // the ownership can be safely taken by new_spibinder.
+            // RpcPreconnectedClient does not take ownership of param, only passing it to
+            // request_fd.
+            let binder =
+                binder_rpc_unstable_bindgen::RpcPreconnectedClient(Some(Self::request_fd), param)
+                    as *mut AIBinder;
+            new_spibinder(binder).ok_or(GetServiceError::ConnectionFailed)
+        }
+    }
+
+    fn as_void_ptr(&mut self) -> *mut raw::c_void {
+        self as *mut _ as *mut raw::c_void
+    }
+
+    fn new_vsock_fd(&self) -> i32 {
+        match self.vm.connectVsock(self.port as i32) {
+            Ok(vsock) => {
+                // Ownership of the fd is transferred to binder
+                vsock.into_raw_fd()
+            }
+            Err(e) => {
+                warn!("Vsock connection failed: {}", e);
+                -1
+            }
+        }
+    }
+
+    unsafe extern "C" fn request_fd(param: *mut raw::c_void) -> raw::c_int {
+        // SAFETY: This is only ever called by RpcPreconnectedClient, within the lifetime of the
+        // VsockFactory, with param taking the value returned by as_void_ptr (so a properly aligned
+        // non-null pointer to an initialized instance).
+        let vsock_factory = param as *mut Self;
+        vsock_factory.as_ref().unwrap().new_vsock_fd()
+    }
+}