1use anyhow::Error;
6use async_trait::async_trait;
7use fidl_fuchsia_power_broker as fbroker;
8use fidl_fuchsia_power_system as fsystem;
9use log::warn;
10
11pub const POWER_LEVEL_OFF: u8 = 0;
12pub const POWER_LEVEL_SUSPEND: u8 = 1;
13pub const POWER_LEVEL_ACTIVE: u8 = 2;
14
15#[derive(Debug)]
16pub struct WakeLease {
17 _token: fsystem::LeaseToken,
18}
19
20impl WakeLease {
21 pub fn from_token(token: fsystem::LeaseToken) -> Self {
22 Self { _token: token }
23 }
24
25 #[doc(hidden)]
28 pub fn from_token_for_test(token: fsystem::LeaseToken) -> Self {
29 Self { _token: token }
30 }
31}
32
33pub trait SyncPowerManager: Send + Sync + std::fmt::Debug {
34 fn take_wake_lease(&self, name: &str) -> Option<WakeLease>;
35}
36
37impl SyncPowerManager for () {
38 fn take_wake_lease(&self, _name: &str) -> Option<WakeLease> {
39 None
40 }
41}
42
43const ACQUIRE_WAKE_LEASE_TIMEOUT: zx::MonotonicDuration = zx::MonotonicDuration::from_seconds(2);
44
45#[derive(Debug)]
46pub struct DeviceSyncPowerManager {
47 proxy: Option<fsystem::ActivityGovernorSynchronousProxy>,
48}
49
50impl DeviceSyncPowerManager {
51 pub fn new(proxy: Option<fsystem::ActivityGovernorSynchronousProxy>) -> Self {
52 Self { proxy }
53 }
54}
55
56impl SyncPowerManager for DeviceSyncPowerManager {
57 fn take_wake_lease(&self, name: &str) -> Option<WakeLease> {
58 let activity_governor = self.proxy.as_ref()?;
59 let deadline = zx::MonotonicInstant::after(ACQUIRE_WAKE_LEASE_TIMEOUT);
60 match activity_governor.acquire_wake_lease(name, deadline) {
61 Ok(Ok(token)) => Some(WakeLease { _token: token }),
62 Ok(Err(e)) => {
63 warn!("Failed to acquire wake lease {}: {:?}", name, e);
64 None
65 }
66 Err(e) => {
67 warn!("FIDL error when acquiring wake lease {}: {:?}", name, e);
68 None
69 }
70 }
71 }
72}
73
74#[async_trait]
75pub trait PowerManager: Send + Sync {
76 async fn take_wake_lease(&self, name: &str) -> Option<WakeLease>;
77 async fn power_element_lease(
78 &self,
79 lease_name: &str,
80 dependency_token: fbroker::DependencyToken,
81 dependency_level: u8,
82 ) -> Result<fbroker::LeaseToken, Error>;
83 async fn register_suspend_blocker(
84 &self,
85 suspend_blocker: fidl::endpoints::ClientEnd<fsystem::SuspendBlockerMarker>,
86 name: &str,
87 ) -> Result<WakeLease, Error>;
88}
89
90pub struct DevicePowerManager {
91 proxies: Option<(fsystem::ActivityGovernorProxy, fbroker::TopologyProxy)>,
92}
93
94impl DevicePowerManager {
95 pub fn new(proxies: Option<(fsystem::ActivityGovernorProxy, fbroker::TopologyProxy)>) -> Self {
96 Self { proxies }
97 }
98}
99
100#[async_trait]
101impl PowerManager for DevicePowerManager {
102 async fn take_wake_lease(&self, name: &str) -> Option<WakeLease> {
103 let (activity_governor, _) = self.proxies.as_ref()?;
104 match activity_governor.acquire_wake_lease(name).await {
105 Ok(Ok(token)) => Some(WakeLease { _token: token }),
106 Ok(Err(e)) => {
107 warn!("Failed to acquire wake lease {}: {:?}", name, e);
108 None
109 }
110 Err(e) => {
111 warn!("FIDL error when acquiring wake lease {}: {:?}", name, e);
112 None
113 }
114 }
115 }
116
117 async fn power_element_lease(
118 &self,
119 lease_name: &str,
120 dependency_token: fbroker::DependencyToken,
121 dependency_level: u8,
122 ) -> Result<fbroker::LeaseToken, Error> {
123 let (_, power_broker) =
124 self.proxies.as_ref().ok_or_else(|| anyhow::anyhow!("Power broker not available"))?;
125
126 let dep = fbroker::LeaseDependency {
127 requires_token: Some(dependency_token),
128 requires_level: Some(dependency_level),
129 ..Default::default()
130 };
131 let (lease_token_local, lease_token_server) = zx::EventPair::create();
132 let schema = fbroker::LeaseSchema {
133 lease_token: Some(lease_token_server),
134 lease_name: Some(lease_name.to_string()),
135 dependencies: Some(vec![dep]),
136 ..Default::default()
137 };
138 power_broker
139 .lease(schema)
140 .await?
141 .map_err(|e| anyhow::anyhow!("Power broker returned error: {:?}", e))?;
142 Ok(lease_token_local)
143 }
144
145 async fn register_suspend_blocker(
146 &self,
147 suspend_blocker: fidl::endpoints::ClientEnd<fsystem::SuspendBlockerMarker>,
148 name: &str,
149 ) -> Result<WakeLease, Error> {
150 let (activity_governor, _) = self
151 .proxies
152 .as_ref()
153 .ok_or_else(|| anyhow::anyhow!("Activity governor not available"))?;
154 let payload = fsystem::ActivityGovernorRegisterSuspendBlockerRequest {
155 suspend_blocker: Some(suspend_blocker),
156 name: Some(name.to_string()),
157 ..Default::default()
158 };
159 match activity_governor.register_suspend_blocker(payload).await {
160 Ok(Ok(token)) => Ok(WakeLease { _token: token }),
161 Ok(Err(e)) => Err(anyhow::anyhow!("RegisterSuspendBlocker error: {:?}", e)),
162 Err(e) => Err(anyhow::anyhow!("FIDL error: {:?}", e)),
163 }
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170 use assert_matches::assert_matches;
171 use fuchsia_async as fasync;
172 use futures::StreamExt;
173
174 #[fasync::run_singlethreaded(test)]
175 async fn test_power_manager_disabled_take_wake_lease() {
176 let pm = DevicePowerManager::new(None);
177 assert!(pm.take_wake_lease("test-lease").await.is_none());
178 }
179
180 #[fasync::run_singlethreaded(test)]
181 async fn test_power_manager_disabled_power_element_lease() {
182 let pm = DevicePowerManager::new(None);
183 let token = zx::Event::create();
184 assert!(pm.power_element_lease("test-lease", token, 1).await.is_err());
185 }
186
187 #[fasync::run_singlethreaded(test)]
188 async fn test_power_manager_disabled_register_suspend_blocker() {
189 let pm = DevicePowerManager::new(None);
190 let (client, _) = fidl::endpoints::create_endpoints::<fsystem::SuspendBlockerMarker>();
191 assert!(pm.register_suspend_blocker(client, "test-blocker").await.is_err());
192 }
193
194 #[fasync::run_singlethreaded(test)]
195 async fn test_power_manager_take_wake_lease_success() {
196 let (proxy, mut stream) =
197 fidl::endpoints::create_proxy_and_stream::<fsystem::ActivityGovernorMarker>();
198 let (topology_proxy, _topology_server) =
199 fidl::endpoints::create_proxy::<fbroker::TopologyMarker>();
200 let pm = DevicePowerManager::new(Some((proxy, topology_proxy)));
201
202 let server_fut = async move {
203 while let Some(Ok(req)) = stream.next().await {
204 match req {
205 fsystem::ActivityGovernorRequest::AcquireWakeLease { name, responder } => {
206 assert_eq!(name, "test-wake-lease");
207 let (_server_token, client_token) = fsystem::LeaseToken::create();
208 let _ = responder.send(Ok(client_token));
209 }
210 other => panic!("Unexpected request: {:?}", other),
211 }
212 }
213 };
214 let client_fut = async move {
215 let lease = pm.take_wake_lease("test-wake-lease").await;
216 assert!(lease.is_some());
217 };
218 futures::join!(server_fut, client_fut);
219 }
220
221 #[fasync::run_singlethreaded(test)]
222 async fn test_power_manager_take_wake_lease_error() {
223 let (proxy, mut stream) =
224 fidl::endpoints::create_proxy_and_stream::<fsystem::ActivityGovernorMarker>();
225 let (topology_proxy, _topology_server) =
226 fidl::endpoints::create_proxy::<fbroker::TopologyMarker>();
227 let pm = DevicePowerManager::new(Some((proxy, topology_proxy)));
228
229 let server_fut = async move {
230 while let Some(Ok(req)) = stream.next().await {
231 match req {
232 fsystem::ActivityGovernorRequest::AcquireWakeLease { name, responder } => {
233 assert_eq!(name, "test-wake-lease");
234 let _ = responder.send(Err(fsystem::AcquireWakeLeaseError::Internal));
235 }
236 other => panic!("Unexpected request: {:?}", other),
237 }
238 }
239 };
240 let client_fut = async move {
241 let lease = pm.take_wake_lease("test-wake-lease").await;
242 assert!(lease.is_none());
243 };
244 futures::join!(server_fut, client_fut);
245 }
246
247 #[fasync::run_singlethreaded(test)]
248 async fn test_power_manager_register_suspend_blocker_success() {
249 let (proxy, mut stream) =
250 fidl::endpoints::create_proxy_and_stream::<fsystem::ActivityGovernorMarker>();
251 let (topology_proxy, _topology_server) =
252 fidl::endpoints::create_proxy::<fbroker::TopologyMarker>();
253 let pm = DevicePowerManager::new(Some((proxy, topology_proxy)));
254
255 let server_fut = async move {
256 while let Some(Ok(req)) = stream.next().await {
257 match req {
258 fsystem::ActivityGovernorRequest::RegisterSuspendBlocker {
259 payload,
260 responder,
261 } => {
262 assert_eq!(payload.name.as_deref(), Some("test-blocker"));
263 let (_server_token, client_token) = fsystem::LeaseToken::create();
264 let _ = responder.send(Ok(client_token));
265 }
266 other => panic!("Unexpected request: {:?}", other),
267 }
268 }
269 };
270 let client_fut = async move {
271 let (client, _server) =
272 fidl::endpoints::create_endpoints::<fsystem::SuspendBlockerMarker>();
273 let lease = pm.register_suspend_blocker(client, "test-blocker").await;
274 assert_matches!(lease, Ok(_));
275 };
276 futures::join!(server_fut, client_fut);
277 }
278
279 #[fasync::run_singlethreaded(test)]
280 async fn test_power_manager_register_suspend_blocker_error() {
281 let (proxy, mut stream) =
282 fidl::endpoints::create_proxy_and_stream::<fsystem::ActivityGovernorMarker>();
283 let (topology_proxy, _topology_server) =
284 fidl::endpoints::create_proxy::<fbroker::TopologyMarker>();
285 let pm = DevicePowerManager::new(Some((proxy, topology_proxy)));
286
287 let server_fut = async move {
288 while let Some(Ok(req)) = stream.next().await {
289 match req {
290 fsystem::ActivityGovernorRequest::RegisterSuspendBlocker {
291 payload,
292 responder,
293 } => {
294 assert_eq!(payload.name.as_deref(), Some("test-blocker"));
295 let _ = responder.send(Err(fsystem::RegisterSuspendBlockerError::Internal));
296 }
297 other => panic!("Unexpected request: {:?}", other),
298 }
299 }
300 };
301 let client_fut = async move {
302 let (client, _server) =
303 fidl::endpoints::create_endpoints::<fsystem::SuspendBlockerMarker>();
304 let lease = pm.register_suspend_blocker(client, "test-blocker").await;
305 assert_matches!(lease, Err(_));
306 };
307 futures::join!(server_fut, client_fut);
308 }
309
310 #[fasync::run_singlethreaded(test)]
311 async fn test_power_manager_power_element_lease_success() {
312 let (ag_proxy, _ag_server) =
313 fidl::endpoints::create_proxy::<fsystem::ActivityGovernorMarker>();
314 let (topology_proxy, mut stream) =
315 fidl::endpoints::create_proxy_and_stream::<fbroker::TopologyMarker>();
316 let pm = DevicePowerManager::new(Some((ag_proxy, topology_proxy)));
317
318 let server_fut = async move {
319 while let Some(Ok(req)) = stream.next().await {
320 match req {
321 fbroker::TopologyRequest::Lease { responder, .. } => {
322 let _ = responder.send(Ok(()));
323 }
324 other => panic!("Unexpected request: {:?}", other),
325 }
326 }
327 };
328 let client_fut = async move {
329 let token = zx::Event::create();
330 let lease = pm.power_element_lease("test-lease", token, 1).await;
331 assert_matches!(lease, Ok(_));
332 };
333 futures::join!(server_fut, client_fut);
334 }
335
336 #[fasync::run_singlethreaded(test)]
337 async fn test_power_manager_power_element_lease_error() {
338 let (ag_proxy, _ag_server) =
339 fidl::endpoints::create_proxy::<fsystem::ActivityGovernorMarker>();
340 let (topology_proxy, mut stream) =
341 fidl::endpoints::create_proxy_and_stream::<fbroker::TopologyMarker>();
342 let pm = DevicePowerManager::new(Some((ag_proxy, topology_proxy)));
343
344 let server_fut = async move {
345 while let Some(Ok(req)) = stream.next().await {
346 match req {
347 fbroker::TopologyRequest::Lease { responder, .. } => {
348 let _ = responder.send(Err(fbroker::LeaseError::Internal));
349 }
350 other => panic!("Unexpected request: {:?}", other),
351 }
352 }
353 };
354 let client_fut = async move {
355 let token = zx::Event::create();
356 let lease = pm.power_element_lease("test-lease", token, 1).await;
357 assert_matches!(lease, Err(_));
358 };
359 futures::join!(server_fut, client_fut);
360 }
361
362 #[test]
363 fn test_sync_power_manager_disabled_take_wake_lease() {
364 let pm = DeviceSyncPowerManager::new(None);
365 assert!(pm.take_wake_lease("test-lease").is_none());
366 let unit_pm = ();
367 assert!(unit_pm.take_wake_lease("test-lease").is_none());
368 }
369
370 #[test]
371 fn test_sync_power_manager_take_wake_lease_success() {
372 let (client_end, server_end) =
373 fidl::endpoints::create_endpoints::<fsystem::ActivityGovernorMarker>();
374 let proxy = client_end.into_sync_proxy();
375 let pm = DeviceSyncPowerManager::new(Some(proxy));
376
377 let thread_handle = std::thread::spawn(move || {
378 let mut exec = fasync::LocalExecutor::default();
379 let mut stream = server_end.into_stream();
380 exec.run_singlethreaded(async move {
381 if let Some(Ok(req)) = stream.next().await {
382 match req {
383 fsystem::ActivityGovernorRequest::AcquireWakeLease { name, responder } => {
384 assert_eq!(name, "test-sync-wake-lease");
385 let (_server_token, client_token) = fsystem::LeaseToken::create();
386 let _ = responder.send(Ok(client_token));
387 }
388 other => panic!("Unexpected request: {:?}", other),
389 }
390 }
391 });
392 });
393
394 let lease = pm.take_wake_lease("test-sync-wake-lease");
395 assert!(lease.is_some());
396 thread_handle.join().unwrap();
397 }
398
399 #[test]
400 fn test_sync_power_manager_take_wake_lease_error() {
401 let (client_end, server_end) =
402 fidl::endpoints::create_endpoints::<fsystem::ActivityGovernorMarker>();
403 let proxy = client_end.into_sync_proxy();
404 let pm = DeviceSyncPowerManager::new(Some(proxy));
405
406 let thread_handle = std::thread::spawn(move || {
407 let mut exec = fasync::LocalExecutor::default();
408 let mut stream = server_end.into_stream();
409 exec.run_singlethreaded(async move {
410 if let Some(Ok(req)) = stream.next().await {
411 match req {
412 fsystem::ActivityGovernorRequest::AcquireWakeLease { name, responder } => {
413 assert_eq!(name, "test-sync-wake-lease");
414 let _ = responder.send(Err(fsystem::AcquireWakeLeaseError::Internal));
415 }
416 other => panic!("Unexpected request: {:?}", other),
417 }
418 }
419 });
420 });
421
422 let lease = pm.take_wake_lease("test-sync-wake-lease");
423 assert!(lease.is_none());
424 thread_handle.join().unwrap();
425 }
426}