[service-vm] Extract service-vm module in a separate library

This allows us to test the library with rialto_test and to remove
the duplicate code in the tests.

Test: m MicrodroidHostTests
Bug: 299089107
Change-Id: Idb9c6bf7c96a334d1f0982d8118e0d5b39915003
diff --git a/virtualizationservice/Android.bp b/virtualizationservice/Android.bp
index 56be95b..c00445d 100644
--- a/virtualizationservice/Android.bp
+++ b/virtualizationservice/Android.bp
@@ -31,7 +31,6 @@
         "libanyhow",
         "libavflog",
         "libbinder_rs",
-        "libciborium",
         "liblibc",
         "liblog_rust",
         "libnix",
@@ -43,6 +42,7 @@
         "libserde",
         "libserde_xml_rs",
         "libservice_vm_comm",
+        "libservice_vm_manager",
     ],
     apex_available: ["com.android.virt"],
 }
diff --git a/virtualizationservice/src/main.rs b/virtualizationservice/src/main.rs
index cdb3ac9..9778599 100644
--- a/virtualizationservice/src/main.rs
+++ b/virtualizationservice/src/main.rs
@@ -18,7 +18,6 @@
 mod atom;
 mod remote_provisioning;
 mod rkpvm;
-mod service_vm;
 
 use crate::aidl::{
     remove_temporary_dir, BINDER_SERVICE_IDENTIFIER, TEMPORARY_DIRECTORY,
diff --git a/virtualizationservice/src/rkpvm.rs b/virtualizationservice/src/rkpvm.rs
index f638503..2c9230b 100644
--- a/virtualizationservice/src/rkpvm.rs
+++ b/virtualizationservice/src/rkpvm.rs
@@ -16,9 +16,9 @@
 //! The RKP VM will be recognized and attested by the RKP server periodically and
 //! serves as a trusted platform to attest a client VM.
 
-use crate::service_vm::ServiceVm;
 use anyhow::{bail, Context, Result};
 use service_vm_comm::{Request, Response};
+use service_vm_manager::ServiceVm;
 
 pub(crate) fn request_certificate(csr: &[u8]) -> Result<Vec<u8>> {
     let mut vm = ServiceVm::start()?;
diff --git a/virtualizationservice/src/service_vm.rs b/virtualizationservice/src/service_vm.rs
deleted file mode 100644
index 23eae91..0000000
--- a/virtualizationservice/src/service_vm.rs
+++ /dev/null
@@ -1,169 +0,0 @@
-// 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.
-
-//! Service VM.
-
-use android_system_virtualizationservice::{
-    aidl::android::system::virtualizationservice::{
-        CpuTopology::CpuTopology, DiskImage::DiskImage,
-        IVirtualizationService::IVirtualizationService, Partition::Partition,
-        PartitionType::PartitionType, VirtualMachineConfig::VirtualMachineConfig,
-        VirtualMachineRawConfig::VirtualMachineRawConfig,
-    },
-    binder::ParcelFileDescriptor,
-};
-use anyhow::{ensure, Context, Result};
-use log::{info, warn};
-use service_vm_comm::{Request, Response, VmType};
-use std::fs::{File, OpenOptions};
-use std::io::{BufWriter, Write};
-use std::path::Path;
-use std::time::Duration;
-use vmclient::VmInstance;
-use vsock::{VsockListener, VsockStream, VMADDR_CID_HOST};
-
-const VIRT_DATA_DIR: &str = "/data/misc/apexdata/com.android.virt";
-const RIALTO_PATH: &str = "/apex/com.android.virt/etc/rialto.bin";
-const INSTANCE_IMG_NAME: &str = "service_vm_instance.img";
-const INSTANCE_IMG_SIZE_BYTES: i64 = 1 << 20; // 1MB
-const MEMORY_MB: i32 = 300;
-const WRITE_BUFFER_CAPACITY: usize = 512;
-const READ_TIMEOUT: Duration = Duration::from_secs(10);
-const WRITE_TIMEOUT: Duration = Duration::from_secs(10);
-
-/// Service VM.
-pub struct ServiceVm {
-    vsock_stream: VsockStream,
-    /// VmInstance will be dropped when ServiceVm goes out of scope, which will kill the VM.
-    vm: VmInstance,
-}
-
-impl ServiceVm {
-    /// Starts the service VM and returns its instance.
-    /// The same instance image is used for different VMs.
-    /// TODO(b/278858244): Allow only one service VM running at each time.
-    pub fn start() -> Result<Self> {
-        // Sets up the vsock server on the host.
-        let vsock_listener =
-            VsockListener::bind_with_cid_port(VMADDR_CID_HOST, VmType::ProtectedVm.port())?;
-
-        // Starts the service VM.
-        let virtmgr = vmclient::VirtualizationService::new().context("Failed to spawn VirtMgr")?;
-        let service = virtmgr.connect().context("Failed to connect to VirtMgr")?;
-        info!("Connected to VirtMgr for service VM");
-
-        let vm = vm_instance(service.as_ref())?;
-        vm.start().context("Failed to start service VM")?;
-        info!("Service VM started");
-
-        // Accepts the connection from the service VM.
-        // TODO(b/299427101): Introduce a timeout for the accept.
-        let (vsock_stream, peer_addr) = vsock_listener.accept().context("Failed to accept")?;
-        info!("Accepted connection {:?}", vsock_stream);
-        ensure!(
-            peer_addr.cid() == u32::try_from(vm.cid()).unwrap(),
-            "The CID of the peer address {} doesn't match the service VM CID {}",
-            peer_addr,
-            vm.cid()
-        );
-        vsock_stream.set_read_timeout(Some(READ_TIMEOUT))?;
-        vsock_stream.set_write_timeout(Some(WRITE_TIMEOUT))?;
-
-        Ok(Self { vsock_stream, vm })
-    }
-
-    /// Processes the request in the service VM.
-    pub fn process_request(&mut self, request: &Request) -> Result<Response> {
-        self.write_request(request)?;
-        self.read_response()
-    }
-
-    /// Sends the request to the service VM.
-    fn write_request(&mut self, request: &Request) -> Result<()> {
-        let mut buffer = BufWriter::with_capacity(WRITE_BUFFER_CAPACITY, &mut self.vsock_stream);
-        ciborium::into_writer(request, &mut buffer)?;
-        buffer.flush().context("Failed to flush the buffer")?;
-        info!("Sent request to the service VM.");
-        Ok(())
-    }
-
-    /// Reads the response from the service VM.
-    fn read_response(&mut self) -> Result<Response> {
-        let response: Response = ciborium::from_reader(&mut self.vsock_stream)
-            .context("Failed to read the response from the service VM")?;
-        info!("Received response from the service VM.");
-        Ok(response)
-    }
-}
-
-impl Drop for ServiceVm {
-    fn drop(&mut self) {
-        // Wait till the service VM finishes releasing all the resources.
-        match self.vm.wait_for_death_with_timeout(Duration::from_secs(10)) {
-            Some(e) => info!("Exit the service VM: {e:?}"),
-            None => warn!("Timed out waiting for service VM exit"),
-        }
-    }
-}
-
-fn vm_instance(service: &dyn IVirtualizationService) -> Result<VmInstance> {
-    let instance_img = instance_img(service)?;
-    let writable_partitions = vec![Partition {
-        label: "vm-instance".to_owned(),
-        image: Some(instance_img),
-        writable: true,
-    }];
-    let rialto = File::open(RIALTO_PATH).context("Failed to open Rialto kernel binary")?;
-    let config = VirtualMachineConfig::RawConfig(VirtualMachineRawConfig {
-        name: String::from("Service VM"),
-        bootloader: Some(ParcelFileDescriptor::new(rialto)),
-        disks: vec![DiskImage { image: None, partitions: writable_partitions, writable: true }],
-        protectedVm: true,
-        memoryMib: MEMORY_MB,
-        cpuTopology: CpuTopology::ONE_CPU,
-        platformVersion: "~1.0".to_string(),
-        gdbPort: 0, // No gdb
-        ..Default::default()
-    });
-    let console_out = None;
-    let console_in = None;
-    let log = None;
-    let callback = None;
-    VmInstance::create(service, &config, console_out, console_in, log, callback)
-        .context("Failed to create service VM")
-}
-
-fn instance_img(service: &dyn IVirtualizationService) -> Result<ParcelFileDescriptor> {
-    let instance_img_path = Path::new(VIRT_DATA_DIR).join(INSTANCE_IMG_NAME);
-    if instance_img_path.exists() {
-        // TODO(b/298174584): Try to recover if the service VM is triggered by rkpd.
-        return Ok(OpenOptions::new()
-            .read(true)
-            .write(true)
-            .open(instance_img_path)
-            .map(ParcelFileDescriptor::new)?);
-    }
-    let instance_img = OpenOptions::new()
-        .create(true)
-        .read(true)
-        .write(true)
-        .open(instance_img_path)
-        .map(ParcelFileDescriptor::new)?;
-    service.initializeWritablePartition(
-        &instance_img,
-        INSTANCE_IMG_SIZE_BYTES,
-        PartitionType::ANDROID_VM_INSTANCE,
-    )?;
-    Ok(instance_img)
-}