Extract and rename InstanceFiles.

It seems to be complex enough to merit its own file and not have
everything effectively public.

I renamed it to InstanceStarter, since what it does is start an
instance (possibly creating/re-creating it first). And did a little
related renaming.

This is intended as a pure refactoring; no functionality changes.

Bug: 186126194
Test: adb shell apex/com.android.compos/bin/composd_cmd
Change-Id: Ib42e1c23594d436b845c28bcfe38803e119d1106
diff --git a/compos/composd/src/composd_main.rs b/compos/composd/src/composd_main.rs
index 3bd4121..3f28a81 100644
--- a/compos/composd/src/composd_main.rs
+++ b/compos/composd/src/composd_main.rs
@@ -19,6 +19,7 @@
 //! them, and orchestrating trusted compilation.
 
 mod instance_manager;
+mod instance_starter;
 mod odrefresh;
 mod service;
 
diff --git a/compos/composd/src/instance_manager.rs b/compos/composd/src/instance_manager.rs
index ad9c28a..6b36ed8 100644
--- a/compos/composd/src/instance_manager.rs
+++ b/compos/composd/src/instance_manager.rs
@@ -14,29 +14,17 @@
  * limitations under the License.
  */
 
-//! Starts and manages instances of the CompOS VM. At most one instance should be running at
-//! a time.
+//! Manages running instances of the CompOS VM. At most one instance should be running at
+//! a time, started on demand.
 
-use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
-    IVirtualizationService::IVirtualizationService, PartitionType::PartitionType,
-};
+use crate::instance_starter::{CompOsInstance, InstanceStarter};
+use android_system_virtualizationservice::aidl::android::system::virtualizationservice;
 use anyhow::{bail, Context, Result};
 use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
-use compos_aidl_interface::binder::{ParcelFileDescriptor, Strong};
-use compos_common::compos_client::VmInstance;
-use compos_common::{
-    COMPOS_DATA_ROOT, CURRENT_DIR, INSTANCE_IMAGE_FILE, PRIVATE_KEY_BLOB_FILE, PUBLIC_KEY_FILE,
-};
-use log::{info, warn};
-use std::fs;
-use std::path::{Path, PathBuf};
+use compos_aidl_interface::binder::Strong;
+use compos_common::CURRENT_DIR;
 use std::sync::{Arc, Mutex, Weak};
-
-pub struct CompOsInstance {
-    #[allow(dead_code)] // Keeps VirtualizationService & the VM alive
-    vm_instance: VmInstance,
-    service: Strong<dyn ICompOsService>,
-}
+use virtualizationservice::IVirtualizationService::IVirtualizationService;
 
 pub struct InstanceManager {
     service: Strong<dyn IVirtualizationService>,
@@ -51,7 +39,7 @@
     pub fn get_running_service(&self) -> Result<Strong<dyn ICompOsService>> {
         let mut state = self.state.lock().unwrap();
         let instance = state.get_running_instance().context("No running instance")?;
-        Ok(instance.service.clone())
+        Ok(instance.get_service())
     }
 
     pub fn start_current_instance(&self) -> Result<Arc<CompOsInstance>> {
@@ -72,137 +60,13 @@
     }
 
     fn try_start_current_instance(&self) -> Result<Arc<CompOsInstance>> {
-        let instance_files = InstanceFiles::new(CURRENT_DIR);
-
-        let compos_instance = instance_files.create_or_start_instance(&*self.service)?;
+        let instance_starter = InstanceStarter::new(CURRENT_DIR);
+        let compos_instance = instance_starter.create_or_start_instance(&*self.service)?;
 
         Ok(Arc::new(compos_instance))
     }
 }
 
-struct InstanceFiles {
-    instance_name: String,
-    instance_root: PathBuf,
-    instance_image: PathBuf,
-    key_blob: PathBuf,
-    public_key: PathBuf,
-}
-
-impl InstanceFiles {
-    fn new(instance_name: &str) -> Self {
-        let instance_root = Path::new(COMPOS_DATA_ROOT).join(instance_name);
-        let instant_root_path = instance_root.as_path();
-        let instance_image = instant_root_path.join(INSTANCE_IMAGE_FILE);
-        let key_blob = instant_root_path.join(PRIVATE_KEY_BLOB_FILE);
-        let public_key = instant_root_path.join(PUBLIC_KEY_FILE);
-        Self {
-            instance_name: instance_name.to_owned(),
-            instance_root,
-            instance_image,
-            key_blob,
-            public_key,
-        }
-    }
-
-    fn create_or_start_instance(
-        &self,
-        service: &dyn IVirtualizationService,
-    ) -> Result<CompOsInstance> {
-        let compos_instance = self.start_instance();
-        match compos_instance {
-            Ok(_) => return compos_instance,
-            Err(e) => warn!("Failed to start {}: {}", self.instance_name, e),
-        }
-
-        self.start_new_instance(service)
-    }
-
-    fn start_instance(&self) -> Result<CompOsInstance> {
-        // No point even trying if the files we need aren't there.
-        self.check_files_exist()?;
-
-        let key_blob = fs::read(&self.key_blob).context("Reading private key blob")?;
-        let public_key = fs::read(&self.public_key).context("Reading public key")?;
-
-        let vm_instance = VmInstance::start(&self.instance_image).context("Starting VM")?;
-        let service = vm_instance.get_service().context("Connecting to CompOS")?;
-
-        if !service.verifySigningKey(&key_blob, &public_key).context("Verifying key pair")? {
-            bail!("Key pair invalid");
-        }
-
-        // If we get this far then the instance image is valid in the current context (e.g. the
-        // current set of APEXes) and the key blob can be successfully decrypted by the VM. So the
-        // files have not been tampered with and we're good to go.
-
-        service.initializeSigningKey(&key_blob).context("Loading signing key")?;
-
-        Ok(CompOsInstance { vm_instance, service })
-    }
-
-    fn start_new_instance(
-        &self,
-        virtualization_service: &dyn IVirtualizationService,
-    ) -> Result<CompOsInstance> {
-        info!("Creating {} CompOs instance", self.instance_name);
-
-        // Ignore failure here - the directory may already exist.
-        let _ = fs::create_dir(&self.instance_root);
-
-        self.create_instance_image(virtualization_service)?;
-
-        let vm_instance = VmInstance::start(&self.instance_image).context("Starting VM")?;
-        let service = vm_instance.get_service().context("Connecting to CompOS")?;
-
-        let key_data = service.generateSigningKey().context("Generating signing key")?;
-        fs::write(&self.key_blob, &key_data.keyBlob).context("Writing key blob")?;
-        // TODO: Extract public key from cert
-        fs::write(&self.public_key, &key_data.certificate).context("Writing public key")?;
-
-        // We don't need to verify the key, since we just generated it and have it in memory.
-
-        service.initializeSigningKey(&key_data.keyBlob).context("Loading signing key")?;
-
-        Ok(CompOsInstance { vm_instance, service })
-    }
-
-    fn create_instance_image(
-        &self,
-        virtualization_service: &dyn IVirtualizationService,
-    ) -> Result<()> {
-        let instance_image = fs::OpenOptions::new()
-            .create(true)
-            .read(true)
-            .write(true)
-            .open(&self.instance_image)
-            .context("Creating instance image file")?;
-        let instance_image = ParcelFileDescriptor::new(instance_image);
-        // TODO: Where does this number come from?
-        let size = 10 * 1024 * 1024;
-        virtualization_service
-            .initializeWritablePartition(&instance_image, size, PartitionType::ANDROID_VM_INSTANCE)
-            .context("Writing instance image file")?;
-        Ok(())
-    }
-
-    fn check_files_exist(&self) -> Result<()> {
-        if !self.instance_root.is_dir() {
-            bail!("Directory {} not found", self.instance_root.display())
-        };
-        Self::check_file_exists(&self.instance_image)?;
-        Self::check_file_exists(&self.key_blob)?;
-        Self::check_file_exists(&self.public_key)?;
-        Ok(())
-    }
-
-    fn check_file_exists(file: &Path) -> Result<()> {
-        if !file.is_file() {
-            bail!("File {} not found", file.display())
-        };
-        Ok(())
-    }
-}
-
 // Ensures we only run one instance at a time.
 // Valid states:
 // Starting: is_starting is true, running_instance is None.
diff --git a/compos/composd/src/instance_starter.rs b/compos/composd/src/instance_starter.rs
new file mode 100644
index 0000000..5352250
--- /dev/null
+++ b/compos/composd/src/instance_starter.rs
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 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.
+ */
+
+//! Responsible for validating and starting an existing instance of the CompOS VM, or creating and
+//! starting a new instance if necessary.
+
+use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{
+    IVirtualizationService::IVirtualizationService, PartitionType::PartitionType,
+};
+use anyhow::{bail, Context, Result};
+use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
+use compos_aidl_interface::binder::{ParcelFileDescriptor, Strong};
+use compos_common::compos_client::VmInstance;
+use compos_common::{
+    COMPOS_DATA_ROOT, INSTANCE_IMAGE_FILE, PRIVATE_KEY_BLOB_FILE, PUBLIC_KEY_FILE,
+};
+use log::{info, warn};
+use std::fs;
+use std::path::{Path, PathBuf};
+
+pub struct CompOsInstance {
+    #[allow(dead_code)] // Keeps VirtualizationService & the VM alive
+    vm_instance: VmInstance,
+    service: Strong<dyn ICompOsService>,
+}
+
+impl CompOsInstance {
+    pub fn get_service(&self) -> Strong<dyn ICompOsService> {
+        self.service.clone()
+    }
+}
+
+pub struct InstanceStarter {
+    instance_name: String,
+    instance_root: PathBuf,
+    instance_image: PathBuf,
+    key_blob: PathBuf,
+    public_key: PathBuf,
+}
+
+impl InstanceStarter {
+    pub fn new(instance_name: &str) -> Self {
+        let instance_root = Path::new(COMPOS_DATA_ROOT).join(instance_name);
+        let instant_root_path = instance_root.as_path();
+        let instance_image = instant_root_path.join(INSTANCE_IMAGE_FILE);
+        let key_blob = instant_root_path.join(PRIVATE_KEY_BLOB_FILE);
+        let public_key = instant_root_path.join(PUBLIC_KEY_FILE);
+        Self {
+            instance_name: instance_name.to_owned(),
+            instance_root,
+            instance_image,
+            key_blob,
+            public_key,
+        }
+    }
+
+    pub fn create_or_start_instance(
+        &self,
+        service: &dyn IVirtualizationService,
+    ) -> Result<CompOsInstance> {
+        let compos_instance = self.start_existing_instance();
+        match compos_instance {
+            Ok(_) => return compos_instance,
+            Err(e) => warn!("Failed to start {}: {}", self.instance_name, e),
+        }
+
+        self.start_new_instance(service)
+    }
+
+    fn start_existing_instance(&self) -> Result<CompOsInstance> {
+        // No point even trying if the files we need aren't there.
+        self.check_files_exist()?;
+
+        let key_blob = fs::read(&self.key_blob).context("Reading private key blob")?;
+        let public_key = fs::read(&self.public_key).context("Reading public key")?;
+
+        let vm_instance = VmInstance::start(&self.instance_image).context("Starting VM")?;
+        let service = vm_instance.get_service().context("Connecting to CompOS")?;
+
+        if !service.verifySigningKey(&key_blob, &public_key).context("Verifying key pair")? {
+            bail!("Key pair invalid");
+        }
+
+        // If we get this far then the instance image is valid in the current context (e.g. the
+        // current set of APEXes) and the key blob can be successfully decrypted by the VM. So the
+        // files have not been tampered with and we're good to go.
+
+        service.initializeSigningKey(&key_blob).context("Loading signing key")?;
+
+        Ok(CompOsInstance { vm_instance, service })
+    }
+
+    fn start_new_instance(
+        &self,
+        virtualization_service: &dyn IVirtualizationService,
+    ) -> Result<CompOsInstance> {
+        info!("Creating {} CompOs instance", self.instance_name);
+
+        // Ignore failure here - the directory may already exist.
+        let _ = fs::create_dir(&self.instance_root);
+
+        self.create_instance_image(virtualization_service)?;
+
+        let vm_instance = VmInstance::start(&self.instance_image).context("Starting VM")?;
+        let service = vm_instance.get_service().context("Connecting to CompOS")?;
+
+        let key_data = service.generateSigningKey().context("Generating signing key")?;
+        fs::write(&self.key_blob, &key_data.keyBlob).context("Writing key blob")?;
+        // TODO: Extract public key from cert
+        fs::write(&self.public_key, &key_data.certificate).context("Writing public key")?;
+
+        // We don't need to verify the key, since we just generated it and have it in memory.
+
+        service.initializeSigningKey(&key_data.keyBlob).context("Loading signing key")?;
+
+        Ok(CompOsInstance { vm_instance, service })
+    }
+
+    fn create_instance_image(
+        &self,
+        virtualization_service: &dyn IVirtualizationService,
+    ) -> Result<()> {
+        let instance_image = fs::OpenOptions::new()
+            .create(true)
+            .read(true)
+            .write(true)
+            .open(&self.instance_image)
+            .context("Creating instance image file")?;
+        let instance_image = ParcelFileDescriptor::new(instance_image);
+        // TODO: Where does this number come from?
+        let size = 10 * 1024 * 1024;
+        virtualization_service
+            .initializeWritablePartition(&instance_image, size, PartitionType::ANDROID_VM_INSTANCE)
+            .context("Writing instance image file")?;
+        Ok(())
+    }
+
+    fn check_files_exist(&self) -> Result<()> {
+        if !self.instance_root.is_dir() {
+            bail!("Directory {} not found", self.instance_root.display())
+        };
+        Self::check_file_exists(&self.instance_image)?;
+        Self::check_file_exists(&self.key_blob)?;
+        Self::check_file_exists(&self.public_key)?;
+        Ok(())
+    }
+
+    fn check_file_exists(file: &Path) -> Result<()> {
+        if !file.is_file() {
+            bail!("File {} not found", file.display())
+        };
+        Ok(())
+    }
+}