• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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