blob: 9b5b8ddce8bc0047e36253e34d9ff88d3f9d0398 [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;
Andrew Walbran1072cc02022-05-23 14:47:58 +000019mod rpc_binder;
Andrew Walbrand0ef4002022-05-16 16:14:10 +000020mod sync;
21
22pub use crate::death_reason::DeathReason;
Alan Stokes71403772022-06-21 14:56:28 +010023pub use crate::errors::{ConnectServiceError, VmWaitError};
Andrew Walbran1072cc02022-05-23 14:47:58 +000024use crate::{rpc_binder::VsockFactory, sync::Monitor};
Andrew Walbrand0ef4002022-05-16 16:14:10 +000025use android_system_virtualizationservice::{
26 aidl::android::system::virtualizationservice::{
27 DeathReason::DeathReason as AidlDeathReason,
28 IVirtualMachine::IVirtualMachine,
29 IVirtualMachineCallback::{BnVirtualMachineCallback, IVirtualMachineCallback},
30 IVirtualizationService::IVirtualizationService,
31 VirtualMachineConfig::VirtualMachineConfig,
32 VirtualMachineState::VirtualMachineState,
33 },
34 binder::{
Andrew Walbran1072cc02022-05-23 14:47:58 +000035 wait_for_interface, BinderFeatures, DeathRecipient, FromIBinder, IBinder, Interface,
Andrew Walbrand0ef4002022-05-16 16:14:10 +000036 ParcelFileDescriptor, Result as BinderResult, StatusCode, Strong,
37 },
38};
39use log::warn;
40use std::{
41 fmt::{self, Debug, Formatter},
42 fs::File,
43 sync::Arc,
44 time::Duration,
45};
46
47const VIRTUALIZATION_SERVICE_BINDER_SERVICE_IDENTIFIER: &str =
48 "android.system.virtualizationservice";
49
50/// Connects to the VirtualizationService AIDL service.
51pub fn connect() -> Result<Strong<dyn IVirtualizationService>, StatusCode> {
52 wait_for_interface(VIRTUALIZATION_SERVICE_BINDER_SERVICE_IDENTIFIER)
53}
54
55/// A virtual machine which has been started by the VirtualizationService.
56pub struct VmInstance {
57 /// The `IVirtualMachine` Binder object representing the VM.
58 pub vm: Strong<dyn IVirtualMachine>,
59 cid: i32,
60 state: Arc<Monitor<VmState>>,
61 // Ensure that the DeathRecipient isn't dropped while someone might call wait_for_death, as it
62 // is removed from the Binder when it's dropped.
63 _death_recipient: DeathRecipient,
64}
65
66impl VmInstance {
67 /// Creates (but doesn't start) a new VM with the given configuration.
68 pub fn create(
69 service: &dyn IVirtualizationService,
70 config: &VirtualMachineConfig,
71 console: Option<File>,
72 log: Option<File>,
73 ) -> BinderResult<Self> {
74 let console = console.map(ParcelFileDescriptor::new);
75 let log = log.map(ParcelFileDescriptor::new);
76
77 let vm = service.createVm(config, console.as_ref(), log.as_ref())?;
78
79 let cid = vm.getCid()?;
80
81 // Register callback before starting VM, in case it dies immediately.
82 let state = Arc::new(Monitor::new(VmState::default()));
83 let callback = BnVirtualMachineCallback::new_binder(
84 VirtualMachineCallback { state: state.clone() },
85 BinderFeatures::default(),
86 );
87 vm.registerCallback(&callback)?;
88 let death_recipient = wait_for_binder_death(&mut vm.as_binder(), state.clone())?;
89
90 Ok(Self { vm, cid, state, _death_recipient: death_recipient })
91 }
92
93 /// Starts the VM.
94 pub fn start(&self) -> BinderResult<()> {
95 self.vm.start()
96 }
97
98 /// Returns the CID used for vsock connections to the VM.
99 pub fn cid(&self) -> i32 {
100 self.cid
101 }
102
103 /// Returns the current lifecycle state of the VM.
104 pub fn state(&self) -> BinderResult<VirtualMachineState> {
105 self.vm.getState()
106 }
107
108 /// Blocks until the VM or the VirtualizationService itself dies, and then returns the reason
109 /// why it died.
110 pub fn wait_for_death(&self) -> DeathReason {
111 self.state.wait_while(|state| state.death_reason.is_none()).unwrap().death_reason.unwrap()
112 }
113
Alan Stokes71403772022-06-21 14:56:28 +0100114 /// Blocks until the VM or the VirtualizationService itself dies, or the given timeout expires.
115 /// Returns the reason why it died if it did so.
116 pub fn wait_for_death_with_timeout(&self, timeout: Duration) -> Option<DeathReason> {
117 let (state, _timeout_result) =
118 self.state.wait_timeout_while(timeout, |state| state.death_reason.is_none()).unwrap();
119 // We don't care if it timed out - we just return the reason if there now is one
120 state.death_reason
121 }
122
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000123 /// Waits until the VM reports that it is ready.
124 ///
125 /// Returns an error if the VM dies first, or the `timeout` elapses before the VM is ready.
126 pub fn wait_until_ready(&self, timeout: Duration) -> Result<(), VmWaitError> {
127 let (state, timeout_result) = self
128 .state
129 .wait_timeout_while(timeout, |state| {
130 state.reported_state < VirtualMachineState::READY && state.death_reason.is_none()
131 })
132 .unwrap();
133 if timeout_result.timed_out() {
134 Err(VmWaitError::TimedOut)
135 } else if let Some(reason) = state.death_reason {
136 Err(VmWaitError::Died { reason })
137 } else if state.reported_state != VirtualMachineState::READY {
138 Err(VmWaitError::Finished)
139 } else {
140 Ok(())
141 }
142 }
Andrew Walbran1072cc02022-05-23 14:47:58 +0000143
144 /// Tries to connect to an RPC Binder service provided by the VM on the given vsock port.
Alan Stokes71403772022-06-21 14:56:28 +0100145 pub fn connect_service<T: FromIBinder + ?Sized>(
Andrew Walbran1072cc02022-05-23 14:47:58 +0000146 &self,
147 port: u32,
Alan Stokes71403772022-06-21 14:56:28 +0100148 ) -> Result<Strong<T>, ConnectServiceError> {
Andrew Walbran1072cc02022-05-23 14:47:58 +0000149 let mut vsock_factory = VsockFactory::new(&*self.vm, port);
150
151 let ibinder = vsock_factory.connect_rpc_client()?;
152
Alan Stokes71403772022-06-21 14:56:28 +0100153 FromIBinder::try_from(ibinder).map_err(ConnectServiceError::WrongServiceType)
Andrew Walbran1072cc02022-05-23 14:47:58 +0000154 }
Jiyong Parke558ab12022-07-07 20:18:55 +0900155
156 /// Get ramdump
157 pub fn get_ramdump(&self) -> Option<File> {
158 self.state.get_ramdump()
159 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000160}
161
162impl Debug for VmInstance {
163 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
164 f.debug_struct("VmInstance").field("cid", &self.cid).field("state", &self.state).finish()
165 }
166}
167
168/// Notify the VmState when the given Binder object dies.
169///
170/// If the returned DeathRecipient is dropped then this will no longer do anything.
171fn wait_for_binder_death(
172 binder: &mut impl IBinder,
173 state: Arc<Monitor<VmState>>,
174) -> BinderResult<DeathRecipient> {
175 let mut death_recipient = DeathRecipient::new(move || {
176 warn!("VirtualizationService unexpectedly died");
177 state.notify_death(DeathReason::VirtualizationServiceDied);
178 });
179 binder.link_to_death(&mut death_recipient)?;
180 Ok(death_recipient)
181}
182
183#[derive(Debug, Default)]
184struct VmState {
185 death_reason: Option<DeathReason>,
186 reported_state: VirtualMachineState,
Jiyong Parke558ab12022-07-07 20:18:55 +0900187 ramdump: Option<File>,
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000188}
189
190impl Monitor<VmState> {
191 fn notify_death(&self, reason: DeathReason) {
192 let state = &mut *self.state.lock().unwrap();
193 // In case this method is called more than once, ignore subsequent calls.
194 if state.death_reason.is_none() {
195 state.death_reason.replace(reason);
196 self.cv.notify_all();
197 }
198 }
199
200 fn notify_state(&self, state: VirtualMachineState) {
201 self.state.lock().unwrap().reported_state = state;
202 self.cv.notify_all();
203 }
Jiyong Parke558ab12022-07-07 20:18:55 +0900204
205 fn set_ramdump(&self, ramdump: File) {
206 self.state.lock().unwrap().ramdump = Some(ramdump);
207 }
208
209 fn get_ramdump(&self) -> Option<File> {
210 self.state.lock().unwrap().ramdump.as_ref().and_then(|f| f.try_clone().ok())
211 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000212}
213
214#[derive(Debug)]
215struct VirtualMachineCallback {
216 state: Arc<Monitor<VmState>>,
217}
218
219impl Interface for VirtualMachineCallback {}
220
221impl IVirtualMachineCallback for VirtualMachineCallback {
222 fn onPayloadStarted(
223 &self,
224 _cid: i32,
225 _stream: Option<&ParcelFileDescriptor>,
226 ) -> BinderResult<()> {
227 self.state.notify_state(VirtualMachineState::STARTED);
228 Ok(())
229 }
230
231 fn onPayloadReady(&self, _cid: i32) -> BinderResult<()> {
232 self.state.notify_state(VirtualMachineState::READY);
233 Ok(())
234 }
235
236 fn onPayloadFinished(&self, _cid: i32, _exit_code: i32) -> BinderResult<()> {
237 self.state.notify_state(VirtualMachineState::FINISHED);
238 Ok(())
239 }
240
241 fn onError(&self, _cid: i32, _error_code: i32, _message: &str) -> BinderResult<()> {
242 self.state.notify_state(VirtualMachineState::FINISHED);
243 Ok(())
244 }
245
Jiyong Parke558ab12022-07-07 20:18:55 +0900246 fn onRamdump(&self, _cid: i32, ramdump: &ParcelFileDescriptor) -> BinderResult<()> {
247 let ramdump: File = ramdump.as_ref().try_clone().unwrap();
248 self.state.set_ramdump(ramdump);
249 Ok(())
250 }
251
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000252 fn onDied(&self, _cid: i32, reason: AidlDeathReason) -> BinderResult<()> {
253 self.state.notify_death(reason.into());
254 Ok(())
255 }
256}