1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 //! Responsible for validating and starting an existing instance of the CompOS VM, or creating and 18 //! starting a new instance if necessary. 19 20 use android_system_virtualizationservice::aidl::android::system::virtualizationservice::{ 21 IVirtualizationService::IVirtualizationService, PartitionType::PartitionType, 22 }; 23 use anyhow::{Context, Result}; 24 use binder::{LazyServiceGuard, ParcelFileDescriptor, Strong}; 25 use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService; 26 use compos_common::compos_client::{ComposClient, VmParameters}; 27 use compos_common::{ 28 COMPOS_DATA_ROOT, IDSIG_FILE, IDSIG_MANIFEST_APK_FILE, IDSIG_MANIFEST_EXT_APK_FILE, 29 INSTANCE_IMAGE_FILE, 30 }; 31 use log::info; 32 use std::fs; 33 use std::path::{Path, PathBuf}; 34 use std::sync::Arc; 35 36 pub struct CompOsInstance { 37 service: Strong<dyn ICompOsService>, 38 #[allow(dead_code)] // Keeps VirtualizationService & the VM alive 39 vm_instance: ComposClient, 40 #[allow(dead_code)] // Keeps composd process alive 41 lazy_service_guard: LazyServiceGuard, 42 // Keep this alive as long as we are 43 instance_tracker: Arc<()>, 44 } 45 46 impl CompOsInstance { get_service(&self) -> Strong<dyn ICompOsService>47 pub fn get_service(&self) -> Strong<dyn ICompOsService> { 48 self.service.clone() 49 } 50 51 /// Returns an Arc that this instance holds a strong reference to as long as it exists. This 52 /// can be used to determine when the instance has been dropped. get_instance_tracker(&self) -> &Arc<()>53 pub fn get_instance_tracker(&self) -> &Arc<()> { 54 &self.instance_tracker 55 } 56 57 /// Attempt to shut down the VM cleanly, giving time for any relevant logs to be written. shutdown(self) -> LazyServiceGuard58 pub fn shutdown(self) -> LazyServiceGuard { 59 self.vm_instance.shutdown(self.service); 60 // Return the guard to the caller, since we might be terminated at any point after it is 61 // dropped, and there might still be things to do. 62 self.lazy_service_guard 63 } 64 } 65 66 pub struct InstanceStarter { 67 instance_name: String, 68 instance_root: PathBuf, 69 instance_image: PathBuf, 70 idsig: PathBuf, 71 idsig_manifest_apk: PathBuf, 72 idsig_manifest_ext_apk: PathBuf, 73 vm_parameters: VmParameters, 74 } 75 76 impl InstanceStarter { new(instance_name: &str, vm_parameters: VmParameters) -> Self77 pub fn new(instance_name: &str, vm_parameters: VmParameters) -> Self { 78 let instance_root = Path::new(COMPOS_DATA_ROOT).join(instance_name); 79 let instance_root_path = instance_root.as_path(); 80 let instance_image = instance_root_path.join(INSTANCE_IMAGE_FILE); 81 let idsig = instance_root_path.join(IDSIG_FILE); 82 let idsig_manifest_apk = instance_root_path.join(IDSIG_MANIFEST_APK_FILE); 83 let idsig_manifest_ext_apk = instance_root_path.join(IDSIG_MANIFEST_EXT_APK_FILE); 84 Self { 85 instance_name: instance_name.to_owned(), 86 instance_root, 87 instance_image, 88 idsig, 89 idsig_manifest_apk, 90 idsig_manifest_ext_apk, 91 vm_parameters, 92 } 93 } 94 start_new_instance( &self, virtualization_service: &dyn IVirtualizationService, ) -> Result<CompOsInstance>95 pub fn start_new_instance( 96 &self, 97 virtualization_service: &dyn IVirtualizationService, 98 ) -> Result<CompOsInstance> { 99 info!("Creating {} CompOs instance", self.instance_name); 100 101 // Ignore failure here - the directory may already exist. 102 let _ = fs::create_dir(&self.instance_root); 103 104 // Overwrite any existing instance - it's unlikely to be valid with the current set 105 // of APEXes, and finding out it isn't is much more expensive than creating a new one. 106 self.create_instance_image(virtualization_service)?; 107 108 // Delete existing idsig files. Ignore error in case idsig doesn't exist. 109 let _ = fs::remove_file(&self.idsig); 110 let _ = fs::remove_file(&self.idsig_manifest_apk); 111 let _ = fs::remove_file(&self.idsig_manifest_ext_apk); 112 113 let instance = self.start_vm(virtualization_service)?; 114 115 // Retrieve the VM's attestation chain as a BCC and save it in the instance directory. 116 let bcc = instance.service.getAttestationChain().context("Getting attestation chain")?; 117 fs::write(self.instance_root.join("bcc"), bcc).context("Writing BCC")?; 118 119 Ok(instance) 120 } 121 start_vm( &self, virtualization_service: &dyn IVirtualizationService, ) -> Result<CompOsInstance>122 fn start_vm( 123 &self, 124 virtualization_service: &dyn IVirtualizationService, 125 ) -> Result<CompOsInstance> { 126 let instance_image = fs::OpenOptions::new() 127 .read(true) 128 .write(true) 129 .open(&self.instance_image) 130 .context("Failed to open instance image")?; 131 let vm_instance = ComposClient::start( 132 virtualization_service, 133 instance_image, 134 &self.idsig, 135 &self.idsig_manifest_apk, 136 &self.idsig_manifest_ext_apk, 137 &self.vm_parameters, 138 ) 139 .context("Starting VM")?; 140 let service = vm_instance.connect_service().context("Connecting to CompOS")?; 141 Ok(CompOsInstance { 142 vm_instance, 143 service, 144 lazy_service_guard: Default::default(), 145 instance_tracker: Default::default(), 146 }) 147 } 148 create_instance_image( &self, virtualization_service: &dyn IVirtualizationService, ) -> Result<()>149 fn create_instance_image( 150 &self, 151 virtualization_service: &dyn IVirtualizationService, 152 ) -> Result<()> { 153 let instance_image = fs::OpenOptions::new() 154 .create(true) 155 .truncate(true) 156 .read(true) 157 .write(true) 158 .open(&self.instance_image) 159 .context("Creating instance image file")?; 160 let instance_image = ParcelFileDescriptor::new(instance_image); 161 // TODO: Where does this number come from? 162 let size = 10 * 1024 * 1024; 163 virtualization_service 164 .initializeWritablePartition(&instance_image, size, PartitionType::ANDROID_VM_INSTANCE) 165 .context("Writing instance image file")?; 166 Ok(()) 167 } 168 } 169