| /* |
| * 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::{anyhow, Context, Result}; |
| use binder::{LazyServiceGuard, ParcelFileDescriptor, Strong}; |
| use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService; |
| use compos_common::compos_client::{ComposClient, VmParameters}; |
| use compos_common::{ |
| COMPOS_DATA_ROOT, IDSIG_FILE, IDSIG_MANIFEST_APK_FILE, IDSIG_MANIFEST_EXT_APK_FILE, |
| INSTANCE_ID_FILE, INSTANCE_IMAGE_FILE, |
| }; |
| use log::info; |
| use std::fs; |
| use std::path::{Path, PathBuf}; |
| use std::sync::Arc; |
| |
| pub struct CompOsInstance { |
| service: Strong<dyn ICompOsService>, |
| #[allow(dead_code)] // Keeps VirtualizationService & the VM alive |
| vm_instance: ComposClient, |
| #[allow(dead_code)] // Keeps composd process alive |
| lazy_service_guard: LazyServiceGuard, |
| // Keep this alive as long as we are |
| instance_tracker: Arc<()>, |
| } |
| |
| impl CompOsInstance { |
| pub fn get_service(&self) -> Strong<dyn ICompOsService> { |
| self.service.clone() |
| } |
| |
| /// Returns an Arc that this instance holds a strong reference to as long as it exists. This |
| /// can be used to determine when the instance has been dropped. |
| pub fn get_instance_tracker(&self) -> &Arc<()> { |
| &self.instance_tracker |
| } |
| |
| /// Attempt to shut down the VM cleanly, giving time for any relevant logs to be written. |
| pub fn shutdown(self) -> LazyServiceGuard { |
| self.vm_instance.shutdown(self.service); |
| // Return the guard to the caller, since we might be terminated at any point after it is |
| // dropped, and there might still be things to do. |
| self.lazy_service_guard |
| } |
| } |
| |
| pub struct InstanceStarter { |
| instance_name: String, |
| instance_root: PathBuf, |
| instance_id_file: PathBuf, |
| instance_image: PathBuf, |
| idsig: PathBuf, |
| idsig_manifest_apk: PathBuf, |
| idsig_manifest_ext_apk: PathBuf, |
| vm_parameters: VmParameters, |
| } |
| |
| impl InstanceStarter { |
| pub fn new(instance_name: &str, vm_parameters: VmParameters) -> Self { |
| let instance_root = Path::new(COMPOS_DATA_ROOT).join(instance_name); |
| let instance_root_path = instance_root.as_path(); |
| let instance_id_file = instance_root_path.join(INSTANCE_ID_FILE); |
| let instance_image = instance_root_path.join(INSTANCE_IMAGE_FILE); |
| let idsig = instance_root_path.join(IDSIG_FILE); |
| let idsig_manifest_apk = instance_root_path.join(IDSIG_MANIFEST_APK_FILE); |
| let idsig_manifest_ext_apk = instance_root_path.join(IDSIG_MANIFEST_EXT_APK_FILE); |
| Self { |
| instance_name: instance_name.to_owned(), |
| instance_root, |
| instance_id_file, |
| instance_image, |
| idsig, |
| idsig_manifest_apk, |
| idsig_manifest_ext_apk, |
| vm_parameters, |
| } |
| } |
| |
| pub fn start_new_instance( |
| &self, |
| virtualization_service: &dyn IVirtualizationService, |
| ) -> Result<CompOsInstance> { |
| info!("Creating {} CompOs instance", self.instance_name); |
| |
| fs::create_dir_all(&self.instance_root)?; |
| |
| // Overwrite any existing instance - it's unlikely to be valid with the current set |
| // of APEXes, and finding out it isn't is much more expensive than creating a new one. |
| self.create_instance_image(virtualization_service)?; |
| // TODO(b/294177871): Ping VS to delete the old instance's secret. |
| if cfg!(llpvm_changes) { |
| self.allocate_instance_id(virtualization_service)?; |
| } |
| // Delete existing idsig files. Ignore error in case idsig doesn't exist. |
| let _ignored1 = fs::remove_file(&self.idsig); |
| let _ignored2 = fs::remove_file(&self.idsig_manifest_apk); |
| let _ignored3 = fs::remove_file(&self.idsig_manifest_ext_apk); |
| |
| let instance = self.start_vm(virtualization_service)?; |
| |
| // Retrieve the VM's attestation chain as a BCC and save it in the instance directory. |
| let bcc = instance.service.getAttestationChain().context("Getting attestation chain")?; |
| fs::write(self.instance_root.join("bcc"), bcc).context("Writing BCC")?; |
| |
| Ok(instance) |
| } |
| |
| fn start_vm( |
| &self, |
| virtualization_service: &dyn IVirtualizationService, |
| ) -> Result<CompOsInstance> { |
| let instance_id: [u8; 64] = if cfg!(llpvm_changes) { |
| fs::read(&self.instance_id_file)? |
| .try_into() |
| .map_err(|_| anyhow!("Failed to get instance_id"))? |
| } else { |
| [0u8; 64] |
| }; |
| |
| let instance_image = fs::OpenOptions::new() |
| .read(true) |
| .write(true) |
| .open(&self.instance_image) |
| .context("Failed to open instance image")?; |
| let vm_instance = ComposClient::start( |
| virtualization_service, |
| instance_id, |
| instance_image, |
| &self.idsig, |
| &self.idsig_manifest_apk, |
| &self.idsig_manifest_ext_apk, |
| &self.vm_parameters, |
| ) |
| .context("Starting VM")?; |
| let service = vm_instance.connect_service().context("Connecting to CompOS")?; |
| Ok(CompOsInstance { |
| vm_instance, |
| service, |
| lazy_service_guard: Default::default(), |
| instance_tracker: Default::default(), |
| }) |
| } |
| |
| fn create_instance_image( |
| &self, |
| virtualization_service: &dyn IVirtualizationService, |
| ) -> Result<()> { |
| let instance_image = fs::OpenOptions::new() |
| .create(true) |
| .truncate(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 allocate_instance_id( |
| &self, |
| virtualization_service: &dyn IVirtualizationService, |
| ) -> Result<()> { |
| let id = virtualization_service.allocateInstanceId().context("Allocating Instance Id")?; |
| fs::write(&self.instance_id_file, id)?; |
| Ok(()) |
| } |
| } |