blob: 888092f9318766767ccb6eaf90ced5780debad74 [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;
22pub use crate::errors::VmWaitError;
23use crate::sync::Monitor;
24use 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::{
34 wait_for_interface, BinderFeatures, DeathRecipient, IBinder, Interface,
35 ParcelFileDescriptor, Result as BinderResult, StatusCode, Strong,
36 },
37};
38use log::warn;
39use std::{
40 fmt::{self, Debug, Formatter},
41 fs::File,
42 sync::Arc,
43 time::Duration,
44};
45
46const VIRTUALIZATION_SERVICE_BINDER_SERVICE_IDENTIFIER: &str =
47 "android.system.virtualizationservice";
48
49/// Connects to the VirtualizationService AIDL service.
50pub fn connect() -> Result<Strong<dyn IVirtualizationService>, StatusCode> {
51 wait_for_interface(VIRTUALIZATION_SERVICE_BINDER_SERVICE_IDENTIFIER)
52}
53
54/// A virtual machine which has been started by the VirtualizationService.
55pub struct VmInstance {
56 /// The `IVirtualMachine` Binder object representing the VM.
57 pub vm: Strong<dyn IVirtualMachine>,
58 cid: i32,
59 state: Arc<Monitor<VmState>>,
60 // Ensure that the DeathRecipient isn't dropped while someone might call wait_for_death, as it
61 // is removed from the Binder when it's dropped.
62 _death_recipient: DeathRecipient,
63}
64
65impl VmInstance {
66 /// Creates (but doesn't start) a new VM with the given configuration.
67 pub fn create(
68 service: &dyn IVirtualizationService,
69 config: &VirtualMachineConfig,
70 console: Option<File>,
71 log: Option<File>,
72 ) -> BinderResult<Self> {
73 let console = console.map(ParcelFileDescriptor::new);
74 let log = log.map(ParcelFileDescriptor::new);
75
76 let vm = service.createVm(config, console.as_ref(), log.as_ref())?;
77
78 let cid = vm.getCid()?;
79
80 // Register callback before starting VM, in case it dies immediately.
81 let state = Arc::new(Monitor::new(VmState::default()));
82 let callback = BnVirtualMachineCallback::new_binder(
83 VirtualMachineCallback { state: state.clone() },
84 BinderFeatures::default(),
85 );
86 vm.registerCallback(&callback)?;
87 let death_recipient = wait_for_binder_death(&mut vm.as_binder(), state.clone())?;
88
89 Ok(Self { vm, cid, state, _death_recipient: death_recipient })
90 }
91
92 /// Starts the VM.
93 pub fn start(&self) -> BinderResult<()> {
94 self.vm.start()
95 }
96
97 /// Returns the CID used for vsock connections to the VM.
98 pub fn cid(&self) -> i32 {
99 self.cid
100 }
101
102 /// Returns the current lifecycle state of the VM.
103 pub fn state(&self) -> BinderResult<VirtualMachineState> {
104 self.vm.getState()
105 }
106
107 /// Blocks until the VM or the VirtualizationService itself dies, and then returns the reason
108 /// why it died.
109 pub fn wait_for_death(&self) -> DeathReason {
110 self.state.wait_while(|state| state.death_reason.is_none()).unwrap().death_reason.unwrap()
111 }
112
113 /// Waits until the VM reports that it is ready.
114 ///
115 /// Returns an error if the VM dies first, or the `timeout` elapses before the VM is ready.
116 pub fn wait_until_ready(&self, timeout: Duration) -> Result<(), VmWaitError> {
117 let (state, timeout_result) = self
118 .state
119 .wait_timeout_while(timeout, |state| {
120 state.reported_state < VirtualMachineState::READY && state.death_reason.is_none()
121 })
122 .unwrap();
123 if timeout_result.timed_out() {
124 Err(VmWaitError::TimedOut)
125 } else if let Some(reason) = state.death_reason {
126 Err(VmWaitError::Died { reason })
127 } else if state.reported_state != VirtualMachineState::READY {
128 Err(VmWaitError::Finished)
129 } else {
130 Ok(())
131 }
132 }
133}
134
135impl Debug for VmInstance {
136 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
137 f.debug_struct("VmInstance").field("cid", &self.cid).field("state", &self.state).finish()
138 }
139}
140
141/// Notify the VmState when the given Binder object dies.
142///
143/// If the returned DeathRecipient is dropped then this will no longer do anything.
144fn wait_for_binder_death(
145 binder: &mut impl IBinder,
146 state: Arc<Monitor<VmState>>,
147) -> BinderResult<DeathRecipient> {
148 let mut death_recipient = DeathRecipient::new(move || {
149 warn!("VirtualizationService unexpectedly died");
150 state.notify_death(DeathReason::VirtualizationServiceDied);
151 });
152 binder.link_to_death(&mut death_recipient)?;
153 Ok(death_recipient)
154}
155
156#[derive(Debug, Default)]
157struct VmState {
158 death_reason: Option<DeathReason>,
159 reported_state: VirtualMachineState,
160}
161
162impl Monitor<VmState> {
163 fn notify_death(&self, reason: DeathReason) {
164 let state = &mut *self.state.lock().unwrap();
165 // In case this method is called more than once, ignore subsequent calls.
166 if state.death_reason.is_none() {
167 state.death_reason.replace(reason);
168 self.cv.notify_all();
169 }
170 }
171
172 fn notify_state(&self, state: VirtualMachineState) {
173 self.state.lock().unwrap().reported_state = state;
174 self.cv.notify_all();
175 }
176}
177
178#[derive(Debug)]
179struct VirtualMachineCallback {
180 state: Arc<Monitor<VmState>>,
181}
182
183impl Interface for VirtualMachineCallback {}
184
185impl IVirtualMachineCallback for VirtualMachineCallback {
186 fn onPayloadStarted(
187 &self,
188 _cid: i32,
189 _stream: Option<&ParcelFileDescriptor>,
190 ) -> BinderResult<()> {
191 self.state.notify_state(VirtualMachineState::STARTED);
192 Ok(())
193 }
194
195 fn onPayloadReady(&self, _cid: i32) -> BinderResult<()> {
196 self.state.notify_state(VirtualMachineState::READY);
197 Ok(())
198 }
199
200 fn onPayloadFinished(&self, _cid: i32, _exit_code: i32) -> BinderResult<()> {
201 self.state.notify_state(VirtualMachineState::FINISHED);
202 Ok(())
203 }
204
205 fn onError(&self, _cid: i32, _error_code: i32, _message: &str) -> BinderResult<()> {
206 self.state.notify_state(VirtualMachineState::FINISHED);
207 Ok(())
208 }
209
210 fn onDied(&self, _cid: i32, reason: AidlDeathReason) -> BinderResult<()> {
211 self.state.notify_death(reason.into());
212 Ok(())
213 }
214}