/* * 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, #[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 { 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 { 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 { 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(()) } }