blob: e6f32b44beaf120a9ba339f265ce94155e04c752 [file] [log] [blame]
Andrew Walbrand0ef4002022-05-16 16:14:10 +00001// Copyright 2022, The Android Open Source Project
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Client library for VirtualizationService.
16
17mod death_reason;
Alan Stokes2bead0d2022-09-05 16:58:34 +010018mod error_code;
Andrew Walbrand0ef4002022-05-16 16:14:10 +000019mod errors;
20mod sync;
21
22pub use crate::death_reason::DeathReason;
Alan Stokes2bead0d2022-09-05 16:58:34 +010023pub use crate::error_code::ErrorCode;
Andrew Walbranc944fae2022-08-02 16:16:28 +000024pub use crate::errors::VmWaitError;
25use crate::sync::Monitor;
Alan Stokes2bead0d2022-09-05 16:58:34 +010026use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::ErrorCode::ErrorCode as AidlErrorCode;
Andrew Walbrand0ef4002022-05-16 16:14:10 +000027use android_system_virtualizationservice::{
28 aidl::android::system::virtualizationservice::{
29 DeathReason::DeathReason as AidlDeathReason,
30 IVirtualMachine::IVirtualMachine,
31 IVirtualMachineCallback::{BnVirtualMachineCallback, IVirtualMachineCallback},
32 IVirtualizationService::IVirtualizationService,
33 VirtualMachineConfig::VirtualMachineConfig,
34 VirtualMachineState::VirtualMachineState,
35 },
36 binder::{
Andrew Walbran1072cc02022-05-23 14:47:58 +000037 wait_for_interface, BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface,
Andrew Walbrand0ef4002022-05-16 16:14:10 +000038 ParcelFileDescriptor, Result as BinderResult, StatusCode, Strong,
39 },
40};
41use log::warn;
Andrew Walbran7eb5ca42022-08-08 15:33:34 +000042use rpcbinder::get_preconnected_rpc_interface;
Andrew Walbrand0ef4002022-05-16 16:14:10 +000043use std::{
44 fmt::{self, Debug, Formatter},
45 fs::File,
Andrew Walbranc944fae2022-08-02 16:16:28 +000046 os::unix::io::IntoRawFd,
Andrew Walbrand0ef4002022-05-16 16:14:10 +000047 sync::Arc,
48 time::Duration,
49};
50
51const VIRTUALIZATION_SERVICE_BINDER_SERVICE_IDENTIFIER: &str =
52 "android.system.virtualizationservice";
53
54/// Connects to the VirtualizationService AIDL service.
55pub fn connect() -> Result<Strong<dyn IVirtualizationService>, StatusCode> {
56 wait_for_interface(VIRTUALIZATION_SERVICE_BINDER_SERVICE_IDENTIFIER)
57}
58
59/// A virtual machine which has been started by the VirtualizationService.
60pub struct VmInstance {
61 /// The `IVirtualMachine` Binder object representing the VM.
62 pub vm: Strong<dyn IVirtualMachine>,
63 cid: i32,
64 state: Arc<Monitor<VmState>>,
65 // Ensure that the DeathRecipient isn't dropped while someone might call wait_for_death, as it
66 // is removed from the Binder when it's dropped.
67 _death_recipient: DeathRecipient,
68}
69
Alan Stokes0e82b502022-08-08 14:44:48 +010070/// A trait to be implemented by clients to handle notification of significant changes to the VM
71/// state. Default implementations of all functions are provided so clients only need to handle the
72/// notifications they are interested in.
73#[allow(unused_variables)]
74pub trait VmCallback {
75 /// Called when the payload has been started within the VM. If present, `stream` is connected
76 /// to the stdin/stdout of the payload.
77 fn on_payload_started(&self, cid: i32, stream: Option<&File>) {}
78
79 /// Callend when the payload has notified Virtualization Service that it is ready to serve
80 /// clients.
81 fn on_payload_ready(&self, cid: i32) {}
82
83 /// Called when the payload has exited in the VM. `exit_code` is the exit code of the payload
84 /// process.
85 fn on_payload_finished(&self, cid: i32, exit_code: i32) {}
86
87 /// Called when an error has occurred in the VM. The `error_code` and `message` may give
88 /// further details.
Alan Stokes2bead0d2022-09-05 16:58:34 +010089 fn on_error(&self, cid: i32, error_code: ErrorCode, message: &str) {}
Alan Stokes0e82b502022-08-08 14:44:48 +010090
91 /// Called when the VM has exited, all resources have been freed, and any logs have been
92 /// written. `death_reason` gives an indication why the VM exited.
93 fn on_died(&self, cid: i32, death_reason: DeathReason) {}
94}
95
Andrew Walbrand0ef4002022-05-16 16:14:10 +000096impl VmInstance {
97 /// Creates (but doesn't start) a new VM with the given configuration.
98 pub fn create(
99 service: &dyn IVirtualizationService,
100 config: &VirtualMachineConfig,
101 console: Option<File>,
102 log: Option<File>,
Alan Stokes0e82b502022-08-08 14:44:48 +0100103 callback: Option<Box<dyn VmCallback + Send + Sync>>,
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000104 ) -> BinderResult<Self> {
105 let console = console.map(ParcelFileDescriptor::new);
106 let log = log.map(ParcelFileDescriptor::new);
107
108 let vm = service.createVm(config, console.as_ref(), log.as_ref())?;
109
110 let cid = vm.getCid()?;
111
112 // Register callback before starting VM, in case it dies immediately.
113 let state = Arc::new(Monitor::new(VmState::default()));
114 let callback = BnVirtualMachineCallback::new_binder(
Alan Stokes0e82b502022-08-08 14:44:48 +0100115 VirtualMachineCallback { state: state.clone(), client_callback: callback },
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000116 BinderFeatures::default(),
117 );
118 vm.registerCallback(&callback)?;
119 let death_recipient = wait_for_binder_death(&mut vm.as_binder(), state.clone())?;
120
121 Ok(Self { vm, cid, state, _death_recipient: death_recipient })
122 }
123
124 /// Starts the VM.
125 pub fn start(&self) -> BinderResult<()> {
126 self.vm.start()
127 }
128
129 /// Returns the CID used for vsock connections to the VM.
130 pub fn cid(&self) -> i32 {
131 self.cid
132 }
133
134 /// Returns the current lifecycle state of the VM.
135 pub fn state(&self) -> BinderResult<VirtualMachineState> {
136 self.vm.getState()
137 }
138
139 /// Blocks until the VM or the VirtualizationService itself dies, and then returns the reason
140 /// why it died.
141 pub fn wait_for_death(&self) -> DeathReason {
142 self.state.wait_while(|state| state.death_reason.is_none()).unwrap().death_reason.unwrap()
143 }
144
Alan Stokes71403772022-06-21 14:56:28 +0100145 /// Blocks until the VM or the VirtualizationService itself dies, or the given timeout expires.
146 /// Returns the reason why it died if it did so.
147 pub fn wait_for_death_with_timeout(&self, timeout: Duration) -> Option<DeathReason> {
148 let (state, _timeout_result) =
149 self.state.wait_timeout_while(timeout, |state| state.death_reason.is_none()).unwrap();
150 // We don't care if it timed out - we just return the reason if there now is one
151 state.death_reason
152 }
153
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000154 /// Waits until the VM reports that it is ready.
155 ///
156 /// Returns an error if the VM dies first, or the `timeout` elapses before the VM is ready.
157 pub fn wait_until_ready(&self, timeout: Duration) -> Result<(), VmWaitError> {
158 let (state, timeout_result) = self
159 .state
160 .wait_timeout_while(timeout, |state| {
161 state.reported_state < VirtualMachineState::READY && state.death_reason.is_none()
162 })
163 .unwrap();
164 if timeout_result.timed_out() {
165 Err(VmWaitError::TimedOut)
166 } else if let Some(reason) = state.death_reason {
167 Err(VmWaitError::Died { reason })
168 } else if state.reported_state != VirtualMachineState::READY {
169 Err(VmWaitError::Finished)
170 } else {
171 Ok(())
172 }
173 }
Andrew Walbran1072cc02022-05-23 14:47:58 +0000174
175 /// Tries to connect to an RPC Binder service provided by the VM on the given vsock port.
Alan Stokes71403772022-06-21 14:56:28 +0100176 pub fn connect_service<T: FromIBinder + ?Sized>(
Andrew Walbran1072cc02022-05-23 14:47:58 +0000177 &self,
178 port: u32,
Andrew Walbranc944fae2022-08-02 16:16:28 +0000179 ) -> Result<Strong<T>, StatusCode> {
Andrew Walbran7eb5ca42022-08-08 15:33:34 +0000180 get_preconnected_rpc_interface(|| {
Andrew Walbranc944fae2022-08-02 16:16:28 +0000181 match self.vm.connectVsock(port as i32) {
182 Ok(vsock) => {
183 // Ownership of the fd is transferred to binder
184 Some(vsock.into_raw_fd())
185 }
186 Err(e) => {
187 warn!("Vsock connection failed: {}", e);
188 None
189 }
190 }
191 })
Andrew Walbran1072cc02022-05-23 14:47:58 +0000192 }
Jiyong Parke558ab12022-07-07 20:18:55 +0900193
194 /// Get ramdump
195 pub fn get_ramdump(&self) -> Option<File> {
196 self.state.get_ramdump()
197 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000198}
199
200impl Debug for VmInstance {
201 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
202 f.debug_struct("VmInstance").field("cid", &self.cid).field("state", &self.state).finish()
203 }
204}
205
206/// Notify the VmState when the given Binder object dies.
207///
208/// If the returned DeathRecipient is dropped then this will no longer do anything.
209fn wait_for_binder_death(
210 binder: &mut impl IBinder,
211 state: Arc<Monitor<VmState>>,
212) -> BinderResult<DeathRecipient> {
213 let mut death_recipient = DeathRecipient::new(move || {
214 warn!("VirtualizationService unexpectedly died");
215 state.notify_death(DeathReason::VirtualizationServiceDied);
216 });
217 binder.link_to_death(&mut death_recipient)?;
218 Ok(death_recipient)
219}
220
221#[derive(Debug, Default)]
222struct VmState {
223 death_reason: Option<DeathReason>,
224 reported_state: VirtualMachineState,
Jiyong Parke558ab12022-07-07 20:18:55 +0900225 ramdump: Option<File>,
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000226}
227
228impl Monitor<VmState> {
229 fn notify_death(&self, reason: DeathReason) {
230 let state = &mut *self.state.lock().unwrap();
231 // In case this method is called more than once, ignore subsequent calls.
232 if state.death_reason.is_none() {
233 state.death_reason.replace(reason);
234 self.cv.notify_all();
235 }
236 }
237
238 fn notify_state(&self, state: VirtualMachineState) {
239 self.state.lock().unwrap().reported_state = state;
240 self.cv.notify_all();
241 }
Jiyong Parke558ab12022-07-07 20:18:55 +0900242
243 fn set_ramdump(&self, ramdump: File) {
244 self.state.lock().unwrap().ramdump = Some(ramdump);
245 }
246
247 fn get_ramdump(&self) -> Option<File> {
248 self.state.lock().unwrap().ramdump.as_ref().and_then(|f| f.try_clone().ok())
249 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000250}
251
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000252struct VirtualMachineCallback {
253 state: Arc<Monitor<VmState>>,
Alan Stokes0e82b502022-08-08 14:44:48 +0100254 client_callback: Option<Box<dyn VmCallback + Send + Sync>>,
255}
256
257impl Debug for VirtualMachineCallback {
258 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
259 fmt.debug_struct("VirtualMachineCallback")
260 .field("state", &self.state)
261 .field(
262 "client_callback",
263 &if self.client_callback.is_some() { "Some(...)" } else { "None" },
264 )
265 .finish()
266 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000267}
268
269impl Interface for VirtualMachineCallback {}
270
271impl IVirtualMachineCallback for VirtualMachineCallback {
272 fn onPayloadStarted(
273 &self,
Alan Stokes0e82b502022-08-08 14:44:48 +0100274 cid: i32,
275 stream: Option<&ParcelFileDescriptor>,
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000276 ) -> BinderResult<()> {
277 self.state.notify_state(VirtualMachineState::STARTED);
Alan Stokes0e82b502022-08-08 14:44:48 +0100278 if let Some(ref callback) = self.client_callback {
279 callback.on_payload_started(cid, stream.map(ParcelFileDescriptor::as_ref));
280 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000281 Ok(())
282 }
283
Alan Stokes0e82b502022-08-08 14:44:48 +0100284 fn onPayloadReady(&self, cid: i32) -> BinderResult<()> {
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000285 self.state.notify_state(VirtualMachineState::READY);
Alan Stokes0e82b502022-08-08 14:44:48 +0100286 if let Some(ref callback) = self.client_callback {
287 callback.on_payload_ready(cid);
288 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000289 Ok(())
290 }
291
Alan Stokes0e82b502022-08-08 14:44:48 +0100292 fn onPayloadFinished(&self, cid: i32, exit_code: i32) -> BinderResult<()> {
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000293 self.state.notify_state(VirtualMachineState::FINISHED);
Alan Stokes0e82b502022-08-08 14:44:48 +0100294 if let Some(ref callback) = self.client_callback {
295 callback.on_payload_finished(cid, exit_code);
296 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000297 Ok(())
298 }
299
Alan Stokes2bead0d2022-09-05 16:58:34 +0100300 fn onError(&self, cid: i32, error_code: AidlErrorCode, message: &str) -> BinderResult<()> {
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000301 self.state.notify_state(VirtualMachineState::FINISHED);
Alan Stokes0e82b502022-08-08 14:44:48 +0100302 if let Some(ref callback) = self.client_callback {
Alan Stokes2bead0d2022-09-05 16:58:34 +0100303 let error_code = error_code.into();
Alan Stokes0e82b502022-08-08 14:44:48 +0100304 callback.on_error(cid, error_code, message);
305 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000306 Ok(())
307 }
308
Jiyong Parke558ab12022-07-07 20:18:55 +0900309 fn onRamdump(&self, _cid: i32, ramdump: &ParcelFileDescriptor) -> BinderResult<()> {
310 let ramdump: File = ramdump.as_ref().try_clone().unwrap();
311 self.state.set_ramdump(ramdump);
312 Ok(())
313 }
314
Alan Stokes0e82b502022-08-08 14:44:48 +0100315 fn onDied(&self, cid: i32, reason: AidlDeathReason) -> BinderResult<()> {
316 let reason = reason.into();
317 self.state.notify_death(reason);
318 if let Some(ref callback) = self.client_callback {
319 callback.on_died(cid, reason);
320 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000321 Ok(())
322 }
323}