blob: a9c26194759ad0783523b5a633bcbe4e8cdf672f [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;
David Brazdil49f96f52022-12-16 21:29:13 +000026use android_system_virtualizationcommon::aidl::android::system::virtualizationcommon::{
27 DeathReason::DeathReason as AidlDeathReason, ErrorCode::ErrorCode as AidlErrorCode,
28};
Andrew Walbrand0ef4002022-05-16 16:14:10 +000029use android_system_virtualizationservice::{
30 aidl::android::system::virtualizationservice::{
Andrew Walbrand0ef4002022-05-16 16:14:10 +000031 IVirtualMachine::IVirtualMachine,
32 IVirtualMachineCallback::{BnVirtualMachineCallback, IVirtualMachineCallback},
33 IVirtualizationService::IVirtualizationService,
34 VirtualMachineConfig::VirtualMachineConfig,
35 VirtualMachineState::VirtualMachineState,
36 },
37 binder::{
Andrew Walbran1072cc02022-05-23 14:47:58 +000038 wait_for_interface, BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface,
Andrew Walbrand0ef4002022-05-16 16:14:10 +000039 ParcelFileDescriptor, Result as BinderResult, StatusCode, Strong,
40 },
41};
42use log::warn;
David Brazdila2125dd2022-12-14 16:37:44 +000043use rpcbinder::RpcSession;
Andrew Walbrand0ef4002022-05-16 16:14:10 +000044use std::{
45 fmt::{self, Debug, Formatter},
46 fs::File,
Andrew Walbranc944fae2022-08-02 16:16:28 +000047 os::unix::io::IntoRawFd,
Andrew Walbrand0ef4002022-05-16 16:14:10 +000048 sync::Arc,
49 time::Duration,
50};
51
52const VIRTUALIZATION_SERVICE_BINDER_SERVICE_IDENTIFIER: &str =
53 "android.system.virtualizationservice";
54
55/// Connects to the VirtualizationService AIDL service.
56pub fn connect() -> Result<Strong<dyn IVirtualizationService>, StatusCode> {
57 wait_for_interface(VIRTUALIZATION_SERVICE_BINDER_SERVICE_IDENTIFIER)
58}
59
60/// A virtual machine which has been started by the VirtualizationService.
61pub struct VmInstance {
62 /// The `IVirtualMachine` Binder object representing the VM.
63 pub vm: Strong<dyn IVirtualMachine>,
64 cid: i32,
65 state: Arc<Monitor<VmState>>,
66 // Ensure that the DeathRecipient isn't dropped while someone might call wait_for_death, as it
67 // is removed from the Binder when it's dropped.
68 _death_recipient: DeathRecipient,
69}
70
Alan Stokes0e82b502022-08-08 14:44:48 +010071/// A trait to be implemented by clients to handle notification of significant changes to the VM
72/// state. Default implementations of all functions are provided so clients only need to handle the
73/// notifications they are interested in.
74#[allow(unused_variables)]
75pub trait VmCallback {
76 /// Called when the payload has been started within the VM. If present, `stream` is connected
77 /// to the stdin/stdout of the payload.
David Brazdil451cc962022-10-14 14:08:12 +010078 fn on_payload_started(&self, cid: i32) {}
Alan Stokes0e82b502022-08-08 14:44:48 +010079
80 /// Callend when the payload has notified Virtualization Service that it is ready to serve
81 /// clients.
82 fn on_payload_ready(&self, cid: i32) {}
83
84 /// Called when the payload has exited in the VM. `exit_code` is the exit code of the payload
85 /// process.
86 fn on_payload_finished(&self, cid: i32, exit_code: i32) {}
87
88 /// Called when an error has occurred in the VM. The `error_code` and `message` may give
89 /// further details.
Alan Stokes2bead0d2022-09-05 16:58:34 +010090 fn on_error(&self, cid: i32, error_code: ErrorCode, message: &str) {}
Alan Stokes0e82b502022-08-08 14:44:48 +010091
92 /// Called when the VM has exited, all resources have been freed, and any logs have been
93 /// written. `death_reason` gives an indication why the VM exited.
94 fn on_died(&self, cid: i32, death_reason: DeathReason) {}
95}
96
Andrew Walbrand0ef4002022-05-16 16:14:10 +000097impl VmInstance {
98 /// Creates (but doesn't start) a new VM with the given configuration.
99 pub fn create(
100 service: &dyn IVirtualizationService,
101 config: &VirtualMachineConfig,
102 console: Option<File>,
103 log: Option<File>,
Alan Stokes0e82b502022-08-08 14:44:48 +0100104 callback: Option<Box<dyn VmCallback + Send + Sync>>,
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000105 ) -> BinderResult<Self> {
106 let console = console.map(ParcelFileDescriptor::new);
107 let log = log.map(ParcelFileDescriptor::new);
108
109 let vm = service.createVm(config, console.as_ref(), log.as_ref())?;
110
111 let cid = vm.getCid()?;
112
113 // Register callback before starting VM, in case it dies immediately.
114 let state = Arc::new(Monitor::new(VmState::default()));
115 let callback = BnVirtualMachineCallback::new_binder(
Alan Stokes0e82b502022-08-08 14:44:48 +0100116 VirtualMachineCallback { state: state.clone(), client_callback: callback },
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000117 BinderFeatures::default(),
118 );
119 vm.registerCallback(&callback)?;
120 let death_recipient = wait_for_binder_death(&mut vm.as_binder(), state.clone())?;
121
122 Ok(Self { vm, cid, state, _death_recipient: death_recipient })
123 }
124
125 /// Starts the VM.
126 pub fn start(&self) -> BinderResult<()> {
127 self.vm.start()
128 }
129
130 /// Returns the CID used for vsock connections to the VM.
131 pub fn cid(&self) -> i32 {
132 self.cid
133 }
134
135 /// Returns the current lifecycle state of the VM.
136 pub fn state(&self) -> BinderResult<VirtualMachineState> {
137 self.vm.getState()
138 }
139
140 /// Blocks until the VM or the VirtualizationService itself dies, and then returns the reason
141 /// why it died.
142 pub fn wait_for_death(&self) -> DeathReason {
143 self.state.wait_while(|state| state.death_reason.is_none()).unwrap().death_reason.unwrap()
144 }
145
Alan Stokes71403772022-06-21 14:56:28 +0100146 /// Blocks until the VM or the VirtualizationService itself dies, or the given timeout expires.
147 /// Returns the reason why it died if it did so.
148 pub fn wait_for_death_with_timeout(&self, timeout: Duration) -> Option<DeathReason> {
149 let (state, _timeout_result) =
150 self.state.wait_timeout_while(timeout, |state| state.death_reason.is_none()).unwrap();
151 // We don't care if it timed out - we just return the reason if there now is one
152 state.death_reason
153 }
154
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000155 /// Waits until the VM reports that it is ready.
156 ///
157 /// Returns an error if the VM dies first, or the `timeout` elapses before the VM is ready.
158 pub fn wait_until_ready(&self, timeout: Duration) -> Result<(), VmWaitError> {
159 let (state, timeout_result) = self
160 .state
161 .wait_timeout_while(timeout, |state| {
162 state.reported_state < VirtualMachineState::READY && state.death_reason.is_none()
163 })
164 .unwrap();
165 if timeout_result.timed_out() {
166 Err(VmWaitError::TimedOut)
167 } else if let Some(reason) = state.death_reason {
168 Err(VmWaitError::Died { reason })
169 } else if state.reported_state != VirtualMachineState::READY {
170 Err(VmWaitError::Finished)
171 } else {
172 Ok(())
173 }
174 }
Andrew Walbran1072cc02022-05-23 14:47:58 +0000175
176 /// Tries to connect to an RPC Binder service provided by the VM on the given vsock port.
Alan Stokes71403772022-06-21 14:56:28 +0100177 pub fn connect_service<T: FromIBinder + ?Sized>(
Andrew Walbran1072cc02022-05-23 14:47:58 +0000178 &self,
179 port: u32,
Andrew Walbranc944fae2022-08-02 16:16:28 +0000180 ) -> Result<Strong<T>, StatusCode> {
David Brazdila2125dd2022-12-14 16:37:44 +0000181 RpcSession::new().setup_preconnected_client(|| {
Andrew Walbranc944fae2022-08-02 16:16:28 +0000182 match self.vm.connectVsock(port as i32) {
183 Ok(vsock) => {
184 // Ownership of the fd is transferred to binder
185 Some(vsock.into_raw_fd())
186 }
187 Err(e) => {
188 warn!("Vsock connection failed: {}", e);
189 None
190 }
191 }
192 })
Andrew Walbran1072cc02022-05-23 14:47:58 +0000193 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000194}
195
196impl Debug for VmInstance {
197 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
198 f.debug_struct("VmInstance").field("cid", &self.cid).field("state", &self.state).finish()
199 }
200}
201
202/// Notify the VmState when the given Binder object dies.
203///
204/// If the returned DeathRecipient is dropped then this will no longer do anything.
205fn wait_for_binder_death(
206 binder: &mut impl IBinder,
207 state: Arc<Monitor<VmState>>,
208) -> BinderResult<DeathRecipient> {
209 let mut death_recipient = DeathRecipient::new(move || {
210 warn!("VirtualizationService unexpectedly died");
211 state.notify_death(DeathReason::VirtualizationServiceDied);
212 });
213 binder.link_to_death(&mut death_recipient)?;
214 Ok(death_recipient)
215}
216
217#[derive(Debug, Default)]
218struct VmState {
219 death_reason: Option<DeathReason>,
220 reported_state: VirtualMachineState,
221}
222
223impl Monitor<VmState> {
224 fn notify_death(&self, reason: DeathReason) {
225 let state = &mut *self.state.lock().unwrap();
226 // In case this method is called more than once, ignore subsequent calls.
227 if state.death_reason.is_none() {
228 state.death_reason.replace(reason);
229 self.cv.notify_all();
230 }
231 }
232
233 fn notify_state(&self, state: VirtualMachineState) {
234 self.state.lock().unwrap().reported_state = state;
235 self.cv.notify_all();
236 }
237}
238
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000239struct VirtualMachineCallback {
240 state: Arc<Monitor<VmState>>,
Alan Stokes0e82b502022-08-08 14:44:48 +0100241 client_callback: Option<Box<dyn VmCallback + Send + Sync>>,
242}
243
244impl Debug for VirtualMachineCallback {
245 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
246 fmt.debug_struct("VirtualMachineCallback")
247 .field("state", &self.state)
248 .field(
249 "client_callback",
250 &if self.client_callback.is_some() { "Some(...)" } else { "None" },
251 )
252 .finish()
253 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000254}
255
256impl Interface for VirtualMachineCallback {}
257
258impl IVirtualMachineCallback for VirtualMachineCallback {
David Brazdil451cc962022-10-14 14:08:12 +0100259 fn onPayloadStarted(&self, cid: i32) -> BinderResult<()> {
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000260 self.state.notify_state(VirtualMachineState::STARTED);
Alan Stokes0e82b502022-08-08 14:44:48 +0100261 if let Some(ref callback) = self.client_callback {
David Brazdil451cc962022-10-14 14:08:12 +0100262 callback.on_payload_started(cid);
263 }
264 Ok(())
265 }
266
Alan Stokes0e82b502022-08-08 14:44:48 +0100267 fn onPayloadReady(&self, cid: i32) -> BinderResult<()> {
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000268 self.state.notify_state(VirtualMachineState::READY);
Alan Stokes0e82b502022-08-08 14:44:48 +0100269 if let Some(ref callback) = self.client_callback {
270 callback.on_payload_ready(cid);
271 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000272 Ok(())
273 }
274
Alan Stokes0e82b502022-08-08 14:44:48 +0100275 fn onPayloadFinished(&self, cid: i32, exit_code: i32) -> BinderResult<()> {
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000276 self.state.notify_state(VirtualMachineState::FINISHED);
Alan Stokes0e82b502022-08-08 14:44:48 +0100277 if let Some(ref callback) = self.client_callback {
278 callback.on_payload_finished(cid, exit_code);
279 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000280 Ok(())
281 }
282
Alan Stokes2bead0d2022-09-05 16:58:34 +0100283 fn onError(&self, cid: i32, error_code: AidlErrorCode, message: &str) -> BinderResult<()> {
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000284 self.state.notify_state(VirtualMachineState::FINISHED);
Alan Stokes0e82b502022-08-08 14:44:48 +0100285 if let Some(ref callback) = self.client_callback {
Alan Stokes2bead0d2022-09-05 16:58:34 +0100286 let error_code = error_code.into();
Alan Stokes0e82b502022-08-08 14:44:48 +0100287 callback.on_error(cid, error_code, message);
288 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000289 Ok(())
290 }
291
Alan Stokes0e82b502022-08-08 14:44:48 +0100292 fn onDied(&self, cid: i32, reason: AidlDeathReason) -> BinderResult<()> {
293 let reason = reason.into();
294 self.state.notify_death(reason);
295 if let Some(ref callback) = self.client_callback {
296 callback.on_died(cid, reason);
297 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000298 Ok(())
299 }
300}