blob: b3bb635d1d2162073562fa8621bc945bec12eb27 [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
67impl VmInstance {
68 /// Creates (but doesn't start) a new VM with the given configuration.
69 pub fn create(
70 service: &dyn IVirtualizationService,
71 config: &VirtualMachineConfig,
72 console: Option<File>,
73 log: Option<File>,
74 ) -> BinderResult<Self> {
75 let console = console.map(ParcelFileDescriptor::new);
76 let log = log.map(ParcelFileDescriptor::new);
77
78 let vm = service.createVm(config, console.as_ref(), log.as_ref())?;
79
80 let cid = vm.getCid()?;
81
82 // Register callback before starting VM, in case it dies immediately.
83 let state = Arc::new(Monitor::new(VmState::default()));
84 let callback = BnVirtualMachineCallback::new_binder(
85 VirtualMachineCallback { state: state.clone() },
86 BinderFeatures::default(),
87 );
88 vm.registerCallback(&callback)?;
89 let death_recipient = wait_for_binder_death(&mut vm.as_binder(), state.clone())?;
90
91 Ok(Self { vm, cid, state, _death_recipient: death_recipient })
92 }
93
94 /// Starts the VM.
95 pub fn start(&self) -> BinderResult<()> {
96 self.vm.start()
97 }
98
99 /// Returns the CID used for vsock connections to the VM.
100 pub fn cid(&self) -> i32 {
101 self.cid
102 }
103
104 /// Returns the current lifecycle state of the VM.
105 pub fn state(&self) -> BinderResult<VirtualMachineState> {
106 self.vm.getState()
107 }
108
109 /// Blocks until the VM or the VirtualizationService itself dies, and then returns the reason
110 /// why it died.
111 pub fn wait_for_death(&self) -> DeathReason {
112 self.state.wait_while(|state| state.death_reason.is_none()).unwrap().death_reason.unwrap()
113 }
114
Alan Stokes71403772022-06-21 14:56:28 +0100115 /// Blocks until the VM or the VirtualizationService itself dies, or the given timeout expires.
116 /// Returns the reason why it died if it did so.
117 pub fn wait_for_death_with_timeout(&self, timeout: Duration) -> Option<DeathReason> {
118 let (state, _timeout_result) =
119 self.state.wait_timeout_while(timeout, |state| state.death_reason.is_none()).unwrap();
120 // We don't care if it timed out - we just return the reason if there now is one
121 state.death_reason
122 }
123
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000124 /// Waits until the VM reports that it is ready.
125 ///
126 /// Returns an error if the VM dies first, or the `timeout` elapses before the VM is ready.
127 pub fn wait_until_ready(&self, timeout: Duration) -> Result<(), VmWaitError> {
128 let (state, timeout_result) = self
129 .state
130 .wait_timeout_while(timeout, |state| {
131 state.reported_state < VirtualMachineState::READY && state.death_reason.is_none()
132 })
133 .unwrap();
134 if timeout_result.timed_out() {
135 Err(VmWaitError::TimedOut)
136 } else if let Some(reason) = state.death_reason {
137 Err(VmWaitError::Died { reason })
138 } else if state.reported_state != VirtualMachineState::READY {
139 Err(VmWaitError::Finished)
140 } else {
141 Ok(())
142 }
143 }
Andrew Walbran1072cc02022-05-23 14:47:58 +0000144
145 /// Tries to connect to an RPC Binder service provided by the VM on the given vsock port.
Alan Stokes71403772022-06-21 14:56:28 +0100146 pub fn connect_service<T: FromIBinder + ?Sized>(
Andrew Walbran1072cc02022-05-23 14:47:58 +0000147 &self,
148 port: u32,
Andrew Walbranc944fae2022-08-02 16:16:28 +0000149 ) -> Result<Strong<T>, StatusCode> {
150 connect_preconnected_rpc_binder(|| {
151 match self.vm.connectVsock(port as i32) {
152 Ok(vsock) => {
153 // Ownership of the fd is transferred to binder
154 Some(vsock.into_raw_fd())
155 }
156 Err(e) => {
157 warn!("Vsock connection failed: {}", e);
158 None
159 }
160 }
161 })
Andrew Walbran1072cc02022-05-23 14:47:58 +0000162 }
Jiyong Parke558ab12022-07-07 20:18:55 +0900163
164 /// Get ramdump
165 pub fn get_ramdump(&self) -> Option<File> {
166 self.state.get_ramdump()
167 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000168}
169
170impl Debug for VmInstance {
171 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
172 f.debug_struct("VmInstance").field("cid", &self.cid).field("state", &self.state).finish()
173 }
174}
175
176/// Notify the VmState when the given Binder object dies.
177///
178/// If the returned DeathRecipient is dropped then this will no longer do anything.
179fn wait_for_binder_death(
180 binder: &mut impl IBinder,
181 state: Arc<Monitor<VmState>>,
182) -> BinderResult<DeathRecipient> {
183 let mut death_recipient = DeathRecipient::new(move || {
184 warn!("VirtualizationService unexpectedly died");
185 state.notify_death(DeathReason::VirtualizationServiceDied);
186 });
187 binder.link_to_death(&mut death_recipient)?;
188 Ok(death_recipient)
189}
190
191#[derive(Debug, Default)]
192struct VmState {
193 death_reason: Option<DeathReason>,
194 reported_state: VirtualMachineState,
Jiyong Parke558ab12022-07-07 20:18:55 +0900195 ramdump: Option<File>,
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000196}
197
198impl Monitor<VmState> {
199 fn notify_death(&self, reason: DeathReason) {
200 let state = &mut *self.state.lock().unwrap();
201 // In case this method is called more than once, ignore subsequent calls.
202 if state.death_reason.is_none() {
203 state.death_reason.replace(reason);
204 self.cv.notify_all();
205 }
206 }
207
208 fn notify_state(&self, state: VirtualMachineState) {
209 self.state.lock().unwrap().reported_state = state;
210 self.cv.notify_all();
211 }
Jiyong Parke558ab12022-07-07 20:18:55 +0900212
213 fn set_ramdump(&self, ramdump: File) {
214 self.state.lock().unwrap().ramdump = Some(ramdump);
215 }
216
217 fn get_ramdump(&self) -> Option<File> {
218 self.state.lock().unwrap().ramdump.as_ref().and_then(|f| f.try_clone().ok())
219 }
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000220}
221
222#[derive(Debug)]
223struct VirtualMachineCallback {
224 state: Arc<Monitor<VmState>>,
225}
226
227impl Interface for VirtualMachineCallback {}
228
229impl IVirtualMachineCallback for VirtualMachineCallback {
230 fn onPayloadStarted(
231 &self,
232 _cid: i32,
233 _stream: Option<&ParcelFileDescriptor>,
234 ) -> BinderResult<()> {
235 self.state.notify_state(VirtualMachineState::STARTED);
236 Ok(())
237 }
238
239 fn onPayloadReady(&self, _cid: i32) -> BinderResult<()> {
240 self.state.notify_state(VirtualMachineState::READY);
241 Ok(())
242 }
243
244 fn onPayloadFinished(&self, _cid: i32, _exit_code: i32) -> BinderResult<()> {
245 self.state.notify_state(VirtualMachineState::FINISHED);
246 Ok(())
247 }
248
249 fn onError(&self, _cid: i32, _error_code: i32, _message: &str) -> BinderResult<()> {
250 self.state.notify_state(VirtualMachineState::FINISHED);
251 Ok(())
252 }
253
Jiyong Parke558ab12022-07-07 20:18:55 +0900254 fn onRamdump(&self, _cid: i32, ramdump: &ParcelFileDescriptor) -> BinderResult<()> {
255 let ramdump: File = ramdump.as_ref().try_clone().unwrap();
256 self.state.set_ramdump(ramdump);
257 Ok(())
258 }
259
Andrew Walbrand0ef4002022-05-16 16:14:10 +0000260 fn onDied(&self, _cid: i32, reason: AidlDeathReason) -> BinderResult<()> {
261 self.state.notify_death(reason.into());
262 Ok(())
263 }
264}