blob: b7de4796ff25e6a2c63c6ee9db1c514792770c5e [file] [log] [blame]
Alan Stokes8c840442021-11-26 15:54:30 +00001/*
2 * Copyright 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
Alan Stokesda596932021-12-15 17:48:55 +000017//! Handle running odrefresh in the VM, with an async interface to allow cancellation
18
19use crate::fd_server_helper::FdServerConfig;
Alan Stokes8c840442021-11-26 15:54:30 +000020use crate::instance_starter::CompOsInstance;
Alan Stokes8c840442021-11-26 15:54:30 +000021use android_system_composd::aidl::android::system::composd::{
22 ICompilationTask::ICompilationTask, ICompilationTaskCallback::ICompilationTaskCallback,
23};
24use android_system_composd::binder::{Interface, Result as BinderResult, Strong};
Alan Stokesa4542ec2021-12-20 09:39:33 +000025use anyhow::{bail, Context, Result};
Alan Stokes8c840442021-11-26 15:54:30 +000026use compos_aidl_interface::aidl::com::android::compos::ICompOsService::ICompOsService;
Alan Stokes46a1dff2021-12-14 10:56:05 +000027use compos_common::odrefresh::ExitCode;
Alan Stokes8c840442021-11-26 15:54:30 +000028use log::{error, warn};
Alan Stokesda596932021-12-15 17:48:55 +000029use rustutils::system_properties;
Alan Stokes35bac3c2021-12-16 14:37:24 +000030use std::fs::{remove_dir_all, File, OpenOptions};
Alan Stokesda596932021-12-15 17:48:55 +000031use std::os::unix::fs::OpenOptionsExt;
32use std::os::unix::io::AsRawFd;
33use std::path::Path;
Alan Stokes8c840442021-11-26 15:54:30 +000034use std::sync::{Arc, Mutex};
35use std::thread;
36
Alan Stokesda596932021-12-15 17:48:55 +000037const ART_APEX_DATA: &str = "/data/misc/apexdata/com.android.art";
38
Alan Stokes8c840442021-11-26 15:54:30 +000039#[derive(Clone)]
40pub struct OdrefreshTask {
41 running_task: Arc<Mutex<Option<RunningTask>>>,
42}
43
44impl Interface for OdrefreshTask {}
45
46impl ICompilationTask for OdrefreshTask {
47 fn cancel(&self) -> BinderResult<()> {
48 let task = self.take();
49 // Drop the VM, which should end compilation - and cause our thread to exit
50 drop(task);
51 Ok(())
52 }
53}
54
Alan Stokesda596932021-12-15 17:48:55 +000055struct RunningTask {
56 callback: Strong<dyn ICompilationTaskCallback>,
57 #[allow(dead_code)] // Keeps the CompOS VM alive
58 comp_os: Arc<CompOsInstance>,
59}
60
Alan Stokes8c840442021-11-26 15:54:30 +000061impl OdrefreshTask {
62 /// Return the current running task, if any, removing it from this CompilationTask.
63 /// Once removed, meaning the task has ended or been canceled, further calls will always return
64 /// None.
65 fn take(&self) -> Option<RunningTask> {
66 self.running_task.lock().unwrap().take()
67 }
68
69 pub fn start(
70 comp_os: Arc<CompOsInstance>,
Alan Stokesac9aa1a2021-12-14 11:32:13 +000071 target_dir_name: String,
Alan Stokes8c840442021-11-26 15:54:30 +000072 callback: &Strong<dyn ICompilationTaskCallback>,
73 ) -> Result<OdrefreshTask> {
74 let service = comp_os.get_service();
75 let task = RunningTask { comp_os, callback: callback.clone() };
76 let task = OdrefreshTask { running_task: Arc::new(Mutex::new(Some(task))) };
77
Alan Stokesac9aa1a2021-12-14 11:32:13 +000078 task.clone().start_thread(service, target_dir_name);
Alan Stokes8c840442021-11-26 15:54:30 +000079
80 Ok(task)
81 }
82
Alan Stokesac9aa1a2021-12-14 11:32:13 +000083 fn start_thread(self, service: Strong<dyn ICompOsService>, target_dir_name: String) {
Alan Stokes8c840442021-11-26 15:54:30 +000084 thread::spawn(move || {
Alan Stokesda596932021-12-15 17:48:55 +000085 let exit_code = run_in_vm(service, &target_dir_name);
Alan Stokes8c840442021-11-26 15:54:30 +000086
87 let task = self.take();
88 // We don't do the callback if cancel has already happened.
89 if let Some(task) = task {
90 let result = match exit_code {
Alan Stokes46a1dff2021-12-14 10:56:05 +000091 Ok(ExitCode::CompilationSuccess) => task.callback.onSuccess(),
Alan Stokes8c840442021-11-26 15:54:30 +000092 Ok(exit_code) => {
93 error!("Unexpected odrefresh result: {:?}", exit_code);
94 task.callback.onFailure()
95 }
96 Err(e) => {
97 error!("Running odrefresh failed: {:?}", e);
98 task.callback.onFailure()
99 }
100 };
101 if let Err(e) = result {
102 warn!("Failed to deliver callback: {:?}", e);
103 }
104 }
105 });
106 }
107}
108
Alan Stokesda596932021-12-15 17:48:55 +0000109fn run_in_vm(service: Strong<dyn ICompOsService>, target_dir_name: &str) -> Result<ExitCode> {
Alan Stokes35bac3c2021-12-16 14:37:24 +0000110 let output_root = Path::new(ART_APEX_DATA);
111
112 // We need to remove the target directory because odrefresh running in compos will create it
113 // (and can't see the existing one, since authfs doesn't show it existing files in an output
114 // directory).
115 let target_path = output_root.join(target_dir_name);
Alan Stokesa4542ec2021-12-20 09:39:33 +0000116 if target_path.exists() {
117 remove_dir_all(&target_path)
118 .with_context(|| format!("Failed to delete {}", target_path.display()))?;
119 }
Alan Stokes35bac3c2021-12-16 14:37:24 +0000120
Alan Stokesda596932021-12-15 17:48:55 +0000121 let staging_dir = open_dir(composd_native::palette_create_odrefresh_staging_directory()?)?;
122 let system_dir = open_dir(Path::new("/system"))?;
Alan Stokes35bac3c2021-12-16 14:37:24 +0000123 let output_dir = open_dir(output_root)?;
Alan Stokesda596932021-12-15 17:48:55 +0000124
125 // Spawn a fd_server to serve the FDs.
126 let fd_server_config = FdServerConfig {
127 ro_dir_fds: vec![system_dir.as_raw_fd()],
128 rw_dir_fds: vec![staging_dir.as_raw_fd(), output_dir.as_raw_fd()],
129 ..Default::default()
130 };
131 let fd_server_raii = fd_server_config.into_fd_server()?;
132
133 let zygote_arch = system_properties::read("ro.zygote")?;
134 let exit_code = service.odrefresh(
135 system_dir.as_raw_fd(),
136 output_dir.as_raw_fd(),
137 staging_dir.as_raw_fd(),
138 target_dir_name,
139 &zygote_arch,
140 )?;
141
142 drop(fd_server_raii);
143 if let Some(exit_code) = ExitCode::from_i32(exit_code.into()) {
144 Ok(exit_code)
145 } else {
146 bail!("odrefresh exited with {}", exit_code)
147 }
148}
149
150/// Returns an owned FD of the directory. It currently returns a `File` as a FD owner, but
151/// it's better to use `std::os::unix::io::OwnedFd` once/if it becomes standard.
152fn open_dir(path: &Path) -> Result<File> {
153 OpenOptions::new()
154 .custom_flags(libc::O_DIRECTORY)
155 .read(true) // O_DIRECTORY can only be opened with read
156 .open(path)
157 .with_context(|| format!("Failed to open {:?} directory as path fd", path))
Alan Stokes8c840442021-11-26 15:54:30 +0000158}