blob: 129e6c307d52714a253f8ce54d61364768fdf7f1 [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;
18mod errors;
19mod sync;
20
21pub use crate::death_reason::DeathReason;
Andrew Walbranc944fae2022-08-02 16:16:28 +000022pub use crate::errors::VmWaitError;
23use crate::sync::Monitor;
Andrew Walbrand0ef4002022-05-16 16:14:10 +000024use android_system_virtualizationservice::{
25 aidl::android::system::virtualizationservice::{
26 DeathReason::DeathReason as AidlDeathReason,
27 IVirtualMachine::IVirtualMachine,
28 IVirtualMachineCallback::{BnVirtualMachineCallback, IVirtualMachineCallback},
29 IVirtualizationService::IVirtualizationService,
30 VirtualMachineConfig::VirtualMachineConfig,
31 VirtualMachineState::VirtualMachineState,
32 },
33 binder::{
Andrew Walbran1072cc02022-05-23 14:47:58 +000034 wait_for_interface, BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface,
Andrew Walbrand0ef4002022-05-16 16:14:10 +000035 ParcelFileDescriptor, Result as BinderResult, StatusCode, Strong,
36 },
37};
Andrew Walbranc944fae2022-08-02 16:16:28 +000038use binder_common::rpc_client::connect_preconnected_rpc_binder;
Andrew Walbrand0ef4002022-05-16 16:14:10 +000039use log::warn;
40use std::{
41 fmt::{self, Debug, Formatter},
42 fs::File,
Andrew Walbranc944fae2022-08-02 16:16:28 +000043 os::unix::io::IntoRawFd,
Andrew Walbrand0ef4002022-05-16 16:14:10 +000044 sync::Arc,
45 time::Duration,
46};
47
48const VIRTUALIZATION_SERVICE_BINDER_SERVICE_IDENTIFIER: &str =
49 "android.system.virtualizationservice";
50
51/// Connects to the VirtualizationService AIDL service.
52pub fn connect() -> Result<Strong<dyn IVirtualizationService>, StatusCode> {
53 wait_for_interface(VIRTUALIZATION_SERVICE_BINDER_SERVICE_IDENTIFIER)
54}
55
56/// A virtual machine which has been started by the VirtualizationService.
57pub struct VmInstance {
58 /// The `IVirtualMachine` Binder object representing the VM.
59 pub vm: Strong<dyn IVirtualMachine>,
60 cid: i32,
61 state: Arc<Monitor<VmState>>,
62 // Ensure that the DeathRecipient isn't dropped while someone might call wait_for_death, as it
63 // is removed from the Binder when it's dropped.
64 _death_recipient: DeathRecipient,
65}
66
Alan Stokes0e82b502022-08-08 14:44:48 +010067/// A trait to be implemented by clients to handle notification of significant changes to the VM
68/// state. Default implementations of all functions are provided so clients only need to handle the
69/// notifications they are interested in.
70#[allow(unused_variables)]
71pub trait VmCallback {
72 /// Called when the payload has been started within the VM. If present, `stream` is connected
73 /// to the stdin/stdout of the payload.
74 fn on_payload_started(&self, cid: i32, stream: Option<&File>) {}
75
76 /// Callend when the payload has notified Virtualization Service that it is ready to serve
77 /// clients.
78 fn on_payload_ready(&self, cid: i32) {}
79
80 /// Called when the payload has exited in the VM. `exit_code` is the exit code of the payload
81 /// process.
82 fn on_payload_finished(&self, cid: i32, exit_code: i32) {}
83
84 /// Called when an error has occurred in the VM. The `error_code` and `message` may give
85 /// further details.
86 fn on_error(&self, cid: i32, error_code: i32, message: &str) {}
87
88 /// Called when the VM has exited, all resources have been freed, and any logs have been
89 /// written. `death_reason` gives an indication why the VM exited.
90 fn on_died(&self, cid: i32, death_reason: DeathReason) {}
91}
92
Andrew Walbrand0ef4002022-05-16 16:14:10 +000093impl VmInstance {
94 /// Creates (but doesn't start) a new VM with the given configuration.
95 pub fn create(
96 service: &dyn IVirtualizationService,
97 config: &VirtualMachineConfig,
98 console: Option<File>,
99 log: Option<File>,
Alan Stokes0e82b502022-08-08 14:44:48 +0100100 callback: Option<Box<dyn VmCallback + Send + Sync>>,
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000101 ) -> BinderResult<Self> {
102 let console = console.map(ParcelFileDescriptor::new);
103 let log = log.map(ParcelFileDescriptor::new);
104
105 let vm = service.createVm(config, console.as_ref(), log.as_ref())?;
106
107 let cid = vm.getCid()?;
108
109 // Register callback before starting VM, in case it dies immediately.
110 let state = Arc::new(Monitor::new(VmState::default()));
111 let callback = BnVirtualMachineCallback::new_binder(
Alan Stokes0e82b502022-08-08 14:44:48 +0100112 VirtualMachineCallback { state: state.clone(), client_callback: callback },
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000113 BinderFeatures::default(),
114 );
115 vm.registerCallback(&callback)?;
116 let death_recipient = wait_for_binder_death(&mut vm.as_binder(), state.clone())?;
117
118 Ok(Self { vm, cid, state, _death_recipient: death_recipient })
119 }
120
121 /// Starts the VM.
122 pub fn start(&self) -> BinderResult<()> {
123 self.vm.start()
124 }
125
126 /// Returns the CID used for vsock connections to the VM.
127 pub fn cid(&self) -> i32 {
128 self.cid
129 }
130
131 /// Returns the current lifecycle state of the VM.
132 pub fn state(&self) -> BinderResult<VirtualMachineState> {
133 self.vm.getState()
134 }
135
136 /// Blocks until the VM or the VirtualizationService itself dies, and then returns the reason
137 /// why it died.
138 pub fn wait_for_death(&self) -> DeathReason {
139 self.state.wait_while(|state| state.death_reason.is_none()).unwrap().death_reason.unwrap()
140 }
141
Alan Stokes71403772022-06-21 14:56:28 +0100142 /// Blocks until the VM or the VirtualizationService itself dies, or the given timeout expires.
143 /// Returns the reason why it died if it did so.
144 pub fn wait_for_death_with_timeout(&self, timeout: Duration) -> Option<DeathReason> {
145 let (state, _timeout_result) =
146 self.state.wait_timeout_while(timeout, |state| state.death_reason.is_none()).unwrap();
147 // We don't care if it timed out - we just return the reason if there now is one
148 state.death_reason
149 }
150
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000151 /// Waits until the VM reports that it is ready.
152 ///
153 /// Returns an error if the VM dies first, or the `timeout` elapses before the VM is ready.
154 pub fn wait_until_ready(&self, timeout: Duration) -> Result<(), VmWaitError> {
155 let (state, timeout_result) = self
156 .state
157 .wait_timeout_while(timeout, |state| {
158 state.reported_state < VirtualMachineState::READY && state.death_reason.is_none()
159 })
160 .unwrap();
161 if timeout_result.timed_out() {
162 Err(VmWaitError::TimedOut)
163 } else if let Some(reason) = state.death_reason {
164 Err(VmWaitError::Died { reason })
165 } else if state.reported_state != VirtualMachineState::READY {
166 Err(VmWaitError::Finished)
167 } else {
168 Ok(())
169 }
170 }
Andrew Walbran1072cc02022-05-23 14:47:58 +0000171
172 /// Tries to connect to an RPC Binder service provided by the VM on the given vsock port.
Alan Stokes71403772022-06-21 14:56:28 +0100173 pub fn connect_service<T: FromIBinder + ?Sized>(
Andrew Walbran1072cc02022-05-23 14:47:58 +0000174 &self,
175 port: u32,
Andrew Walbranc944fae2022-08-02 16:16:28 +0000176 ) -> Result<Strong<T>, StatusCode> {
177 connect_preconnected_rpc_binder(|| {
178 match self.vm.connectVsock(port as i32) {
179 Ok(vsock) => {
180 // Ownership of the fd is transferred to binder
181 Some(vsock.into_raw_fd())
182 }
183 Err(e) => {
184 warn!("Vsock connection failed: {}", e);
185 None
186 }
187 }
188 })
Andrew Walbran1072cc02022-05-23 14:47:58 +0000189 }
Jiyong Parke558ab12022-07-07 20:18:55 +0900190
191 /// Get ramdump
192 pub fn get_ramdump(&self) -> Option<File> {
193 self.state.get_ramdump()
194 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000195}
196
197impl Debug for VmInstance {
198 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
199 f.debug_struct("VmInstance").field("cid", &self.cid).field("state", &self.state).finish()
200 }
201}
202
203/// Notify the VmState when the given Binder object dies.
204///
205/// If the returned DeathRecipient is dropped then this will no longer do anything.
206fn wait_for_binder_death(
207 binder: &mut impl IBinder,
208 state: Arc<Monitor<VmState>>,
209) -> BinderResult<DeathRecipient> {
210 let mut death_recipient = DeathRecipient::new(move || {
211 warn!("VirtualizationService unexpectedly died");
212 state.notify_death(DeathReason::VirtualizationServiceDied);
213 });
214 binder.link_to_death(&mut death_recipient)?;
215 Ok(death_recipient)
216}
217
218#[derive(Debug, Default)]
219struct VmState {
220 death_reason: Option<DeathReason>,
221 reported_state: VirtualMachineState,
Jiyong Parke558ab12022-07-07 20:18:55 +0900222 ramdump: Option<File>,
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000223}
224
225impl Monitor<VmState> {
226 fn notify_death(&self, reason: DeathReason) {
227 let state = &mut *self.state.lock().unwrap();
228 // In case this method is called more than once, ignore subsequent calls.
229 if state.death_reason.is_none() {
230 state.death_reason.replace(reason);
231 self.cv.notify_all();
232 }
233 }
234
235 fn notify_state(&self, state: VirtualMachineState) {
236 self.state.lock().unwrap().reported_state = state;
237 self.cv.notify_all();
238 }
Jiyong Parke558ab12022-07-07 20:18:55 +0900239
240 fn set_ramdump(&self, ramdump: File) {
241 self.state.lock().unwrap().ramdump = Some(ramdump);
242 }
243
244 fn get_ramdump(&self) -> Option<File> {
245 self.state.lock().unwrap().ramdump.as_ref().and_then(|f| f.try_clone().ok())
246 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000247}
248
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000249struct VirtualMachineCallback {
250 state: Arc<Monitor<VmState>>,
Alan Stokes0e82b502022-08-08 14:44:48 +0100251 client_callback: Option<Box<dyn VmCallback + Send + Sync>>,
252}
253
254impl Debug for VirtualMachineCallback {
255 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
256 fmt.debug_struct("VirtualMachineCallback")
257 .field("state", &self.state)
258 .field(
259 "client_callback",
260 &if self.client_callback.is_some() { "Some(...)" } else { "None" },
261 )
262 .finish()
263 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000264}
265
266impl Interface for VirtualMachineCallback {}
267
268impl IVirtualMachineCallback for VirtualMachineCallback {
269 fn onPayloadStarted(
270 &self,
Alan Stokes0e82b502022-08-08 14:44:48 +0100271 cid: i32,
272 stream: Option<&ParcelFileDescriptor>,
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000273 ) -> BinderResult<()> {
274 self.state.notify_state(VirtualMachineState::STARTED);
Alan Stokes0e82b502022-08-08 14:44:48 +0100275 if let Some(ref callback) = self.client_callback {
276 callback.on_payload_started(cid, stream.map(ParcelFileDescriptor::as_ref));
277 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000278 Ok(())
279 }
280
Alan Stokes0e82b502022-08-08 14:44:48 +0100281 fn onPayloadReady(&self, cid: i32) -> BinderResult<()> {
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000282 self.state.notify_state(VirtualMachineState::READY);
Alan Stokes0e82b502022-08-08 14:44:48 +0100283 if let Some(ref callback) = self.client_callback {
284 callback.on_payload_ready(cid);
285 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000286 Ok(())
287 }
288
Alan Stokes0e82b502022-08-08 14:44:48 +0100289 fn onPayloadFinished(&self, cid: i32, exit_code: i32) -> BinderResult<()> {
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000290 self.state.notify_state(VirtualMachineState::FINISHED);
Alan Stokes0e82b502022-08-08 14:44:48 +0100291 if let Some(ref callback) = self.client_callback {
292 callback.on_payload_finished(cid, exit_code);
293 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000294 Ok(())
295 }
296
Alan Stokes0e82b502022-08-08 14:44:48 +0100297 fn onError(&self, cid: i32, error_code: i32, message: &str) -> BinderResult<()> {
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000298 self.state.notify_state(VirtualMachineState::FINISHED);
Alan Stokes0e82b502022-08-08 14:44:48 +0100299 if let Some(ref callback) = self.client_callback {
300 callback.on_error(cid, error_code, message);
301 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000302 Ok(())
303 }
304
Jiyong Parke558ab12022-07-07 20:18:55 +0900305 fn onRamdump(&self, _cid: i32, ramdump: &ParcelFileDescriptor) -> BinderResult<()> {
306 let ramdump: File = ramdump.as_ref().try_clone().unwrap();
307 self.state.set_ramdump(ramdump);
308 Ok(())
309 }
310
Alan Stokes0e82b502022-08-08 14:44:48 +0100311 fn onDied(&self, cid: i32, reason: AidlDeathReason) -> BinderResult<()> {
312 let reason = reason.into();
313 self.state.notify_death(reason);
314 if let Some(ref callback) = self.client_callback {
315 callback.on_died(cid, reason);
316 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000317 Ok(())
318 }
319}