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#[cfg_attr(test, derive(Debug))]
16pub struct WakeLease {
17 _token: fsystem::LeaseToken,
18}
19
20impl WakeLease {
21 #[doc(hidden)]
24 pub fn from_token_for_test(token: fsystem::LeaseToken) -> Self {
25 Self { _token: token }
26 }
27}
28
29#[async_trait]
30pub trait PowerManager: Send + Sync {
31 async fn take_wake_lease(&self, name: &str) -> Option<WakeLease>;
32 async fn power_element_lease(
33 &self,
34 lease_name: &str,
35 dependency_token: fbroker::DependencyToken,
36 dependency_level: u8,
37 ) -> Result<fbroker::LeaseToken, Error>;
38 async fn register_suspend_blocker(
39 &self,
40 suspend_blocker: fidl::endpoints::ClientEnd<fsystem::SuspendBlockerMarker>,
41 name: &str,
42 ) -> Result<WakeLease, Error>;
43}
44
45pub struct DevicePowerManager {
46 proxies: Option<(fsystem::ActivityGovernorProxy, fbroker::TopologyProxy)>,
47}
48
49impl DevicePowerManager {
50 pub fn new(proxies: Option<(fsystem::ActivityGovernorProxy, fbroker::TopologyProxy)>) -> Self {
51 Self { proxies }
52 }
53}
54
55#[async_trait]
56impl PowerManager for DevicePowerManager {
57 async fn take_wake_lease(&self, name: &str) -> Option<WakeLease> {
58 let (activity_governor, _) = self.proxies.as_ref()?;
59 match activity_governor.acquire_wake_lease(name).await {
60 Ok(Ok(token)) => Some(WakeLease { _token: token }),
61 Ok(Err(e)) => {
62 warn!("Failed to acquire wake lease {}: {:?}", name, e);
63 None
64 }
65 Err(e) => {
66 warn!("FIDL error when acquiring wake lease {}: {:?}", name, e);
67 None
68 }
69 }
70 }
71
72 async fn power_element_lease(
73 &self,
74 lease_name: &str,
75 dependency_token: fbroker::DependencyToken,
76 dependency_level: u8,
77 ) -> Result<fbroker::LeaseToken, Error> {
78 let (_, power_broker) =
79 self.proxies.as_ref().ok_or_else(|| anyhow::anyhow!("Power broker not available"))?;
80
81 let dep = fbroker::LeaseDependency {
82 requires_token: Some(dependency_token),
83 requires_level: Some(dependency_level),
84 ..Default::default()
85 };
86 let (lease_token_local, lease_token_server) = zx::EventPair::create();
87 let schema = fbroker::LeaseSchema {
88 lease_token: Some(lease_token_server),
89 lease_name: Some(lease_name.to_string()),
90 dependencies: Some(vec![dep]),
91 ..Default::default()
92 };
93 power_broker
94 .lease(schema)
95 .await?
96 .map_err(|e| anyhow::anyhow!("Power broker returned error: {:?}", e))?;
97 Ok(lease_token_local)
98 }
99
100 async fn register_suspend_blocker(
101 &self,
102 suspend_blocker: fidl::endpoints::ClientEnd<fsystem::SuspendBlockerMarker>,
103 name: &str,
104 ) -> Result<WakeLease, Error> {
105 let (activity_governor, _) = self
106 .proxies
107 .as_ref()
108 .ok_or_else(|| anyhow::anyhow!("Activity governor not available"))?;
109 let payload = fsystem::ActivityGovernorRegisterSuspendBlockerRequest {
110 suspend_blocker: Some(suspend_blocker),
111 name: Some(name.to_string()),
112 ..Default::default()
113 };
114 match activity_governor.register_suspend_blocker(payload).await {
115 Ok(Ok(token)) => Ok(WakeLease { _token: token }),
116 Ok(Err(e)) => Err(anyhow::anyhow!("RegisterSuspendBlocker error: {:?}", e)),
117 Err(e) => Err(anyhow::anyhow!("FIDL error: {:?}", e)),
118 }
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use assert_matches::assert_matches;
126 use fuchsia_async as fasync;
127 use futures::StreamExt;
128
129 #[fasync::run_singlethreaded(test)]
130 async fn test_power_manager_disabled_take_wake_lease() {
131 let pm = DevicePowerManager::new(None);
132 assert!(pm.take_wake_lease("test-lease").await.is_none());
133 }
134
135 #[fasync::run_singlethreaded(test)]
136 async fn test_power_manager_disabled_power_element_lease() {
137 let pm = DevicePowerManager::new(None);
138 let token = zx::Event::create();
139 assert!(pm.power_element_lease("test-lease", token, 1).await.is_err());
140 }
141
142 #[fasync::run_singlethreaded(test)]
143 async fn test_power_manager_disabled_register_suspend_blocker() {
144 let pm = DevicePowerManager::new(None);
145 let (client, _) = fidl::endpoints::create_endpoints::<fsystem::SuspendBlockerMarker>();
146 assert!(pm.register_suspend_blocker(client, "test-blocker").await.is_err());
147 }
148
149 #[fasync::run_singlethreaded(test)]
150 async fn test_power_manager_take_wake_lease_success() {
151 let (proxy, mut stream) =
152 fidl::endpoints::create_proxy_and_stream::<fsystem::ActivityGovernorMarker>();
153 let (topology_proxy, _topology_server) =
154 fidl::endpoints::create_proxy::<fbroker::TopologyMarker>();
155 let pm = DevicePowerManager::new(Some((proxy, topology_proxy)));
156
157 let server_fut = async move {
158 while let Some(Ok(req)) = stream.next().await {
159 match req {
160 fsystem::ActivityGovernorRequest::AcquireWakeLease { name, responder } => {
161 assert_eq!(name, "test-wake-lease");
162 let (_server_token, client_token) = fsystem::LeaseToken::create();
163 let _ = responder.send(Ok(client_token));
164 }
165 other => panic!("Unexpected request: {:?}", other),
166 }
167 }
168 };
169 let client_fut = async move {
170 let lease = pm.take_wake_lease("test-wake-lease").await;
171 assert!(lease.is_some());
172 };
173 futures::join!(server_fut, client_fut);
174 }
175
176 #[fasync::run_singlethreaded(test)]
177 async fn test_power_manager_take_wake_lease_error() {
178 let (proxy, mut stream) =
179 fidl::endpoints::create_proxy_and_stream::<fsystem::ActivityGovernorMarker>();
180 let (topology_proxy, _topology_server) =
181 fidl::endpoints::create_proxy::<fbroker::TopologyMarker>();
182 let pm = DevicePowerManager::new(Some((proxy, topology_proxy)));
183
184 let server_fut = async move {
185 while let Some(Ok(req)) = stream.next().await {
186 match req {
187 fsystem::ActivityGovernorRequest::AcquireWakeLease { name, responder } => {
188 assert_eq!(name, "test-wake-lease");
189 let _ = responder.send(Err(fsystem::AcquireWakeLeaseError::Internal));
190 }
191 other => panic!("Unexpected request: {:?}", other),
192 }
193 }
194 };
195 let client_fut = async move {
196 let lease = pm.take_wake_lease("test-wake-lease").await;
197 assert!(lease.is_none());
198 };
199 futures::join!(server_fut, client_fut);
200 }
201
202 #[fasync::run_singlethreaded(test)]
203 async fn test_power_manager_register_suspend_blocker_success() {
204 let (proxy, mut stream) =
205 fidl::endpoints::create_proxy_and_stream::<fsystem::ActivityGovernorMarker>();
206 let (topology_proxy, _topology_server) =
207 fidl::endpoints::create_proxy::<fbroker::TopologyMarker>();
208 let pm = DevicePowerManager::new(Some((proxy, topology_proxy)));
209
210 let server_fut = async move {
211 while let Some(Ok(req)) = stream.next().await {
212 match req {
213 fsystem::ActivityGovernorRequest::RegisterSuspendBlocker {
214 payload,
215 responder,
216 } => {
217 assert_eq!(payload.name.as_deref(), Some("test-blocker"));
218 let (_server_token, client_token) = fsystem::LeaseToken::create();
219 let _ = responder.send(Ok(client_token));
220 }
221 other => panic!("Unexpected request: {:?}", other),
222 }
223 }
224 };
225 let client_fut = async move {
226 let (client, _server) =
227 fidl::endpoints::create_endpoints::<fsystem::SuspendBlockerMarker>();
228 let lease = pm.register_suspend_blocker(client, "test-blocker").await;
229 assert_matches!(lease, Ok(_));
230 };
231 futures::join!(server_fut, client_fut);
232 }
233
234 #[fasync::run_singlethreaded(test)]
235 async fn test_power_manager_register_suspend_blocker_error() {
236 let (proxy, mut stream) =
237 fidl::endpoints::create_proxy_and_stream::<fsystem::ActivityGovernorMarker>();
238 let (topology_proxy, _topology_server) =
239 fidl::endpoints::create_proxy::<fbroker::TopologyMarker>();
240 let pm = DevicePowerManager::new(Some((proxy, topology_proxy)));
241
242 let server_fut = async move {
243 while let Some(Ok(req)) = stream.next().await {
244 match req {
245 fsystem::ActivityGovernorRequest::RegisterSuspendBlocker {
246 payload,
247 responder,
248 } => {
249 assert_eq!(payload.name.as_deref(), Some("test-blocker"));
250 let _ = responder.send(Err(fsystem::RegisterSuspendBlockerError::Internal));
251 }
252 other => panic!("Unexpected request: {:?}", other),
253 }
254 }
255 };
256 let client_fut = async move {
257 let (client, _server) =
258 fidl::endpoints::create_endpoints::<fsystem::SuspendBlockerMarker>();
259 let lease = pm.register_suspend_blocker(client, "test-blocker").await;
260 assert_matches!(lease, Err(_));
261 };
262 futures::join!(server_fut, client_fut);
263 }
264
265 #[fasync::run_singlethreaded(test)]
266 async fn test_power_manager_power_element_lease_success() {
267 let (ag_proxy, _ag_server) =
268 fidl::endpoints::create_proxy::<fsystem::ActivityGovernorMarker>();
269 let (topology_proxy, mut stream) =
270 fidl::endpoints::create_proxy_and_stream::<fbroker::TopologyMarker>();
271 let pm = DevicePowerManager::new(Some((ag_proxy, topology_proxy)));
272
273 let server_fut = async move {
274 while let Some(Ok(req)) = stream.next().await {
275 match req {
276 fbroker::TopologyRequest::Lease { responder, .. } => {
277 let _ = responder.send(Ok(()));
278 }
279 other => panic!("Unexpected request: {:?}", other),
280 }
281 }
282 };
283 let client_fut = async move {
284 let token = zx::Event::create();
285 let lease = pm.power_element_lease("test-lease", token, 1).await;
286 assert_matches!(lease, Ok(_));
287 };
288 futures::join!(server_fut, client_fut);
289 }
290
291 #[fasync::run_singlethreaded(test)]
292 async fn test_power_manager_power_element_lease_error() {
293 let (ag_proxy, _ag_server) =
294 fidl::endpoints::create_proxy::<fsystem::ActivityGovernorMarker>();
295 let (topology_proxy, mut stream) =
296 fidl::endpoints::create_proxy_and_stream::<fbroker::TopologyMarker>();
297 let pm = DevicePowerManager::new(Some((ag_proxy, topology_proxy)));
298
299 let server_fut = async move {
300 while let Some(Ok(req)) = stream.next().await {
301 match req {
302 fbroker::TopologyRequest::Lease { responder, .. } => {
303 let _ = responder.send(Err(fbroker::LeaseError::Internal));
304 }
305 other => panic!("Unexpected request: {:?}", other),
306 }
307 }
308 };
309 let client_fut = async move {
310 let token = zx::Event::create();
311 let lease = pm.power_element_lease("test-lease", token, 1).await;
312 assert_matches!(lease, Err(_));
313 };
314 futures::join!(server_fut, client_fut);
315 }
316}