1use anyhow::{Result, anyhow};
5use fidl::endpoints::{ClientEnd, ServerEnd, create_endpoints, create_proxy};
6use fuchsia_inspect::{self, Property};
7use futures::TryStreamExt;
8use futures::future::LocalBoxFuture;
9use std::sync::Arc;
10use zx::{HandleBased, Rights};
11use {fidl_fuchsia_power_broker as fbroker, fuchsia_async as fasync};
12
13pub const BINARY_POWER_LEVELS: [fbroker::PowerLevel; 2] = [
16 fbroker::BinaryPowerLevel::Off.into_primitive(),
17 fbroker::BinaryPowerLevel::On.into_primitive(),
18];
19
20pub struct PowerElementContext {
21 pub element_control: fbroker::ElementControlProxy,
22 pub lessor: fbroker::LessorProxy,
23 assertive_dependency_token: Option<fbroker::DependencyToken>,
24 opportunistic_dependency_token: Option<fbroker::DependencyToken>,
25 name: String,
26 initial_level: fbroker::PowerLevel,
27}
28
29impl PowerElementContext {
30 pub fn builder<'a>(
31 topology: &'a fbroker::TopologyProxy,
32 element_name: &'a str,
33 valid_levels: &'a [fbroker::PowerLevel],
34 element_runner_client: ClientEnd<fbroker::ElementRunnerMarker>,
35 ) -> PowerElementContextBuilder<'a> {
36 PowerElementContextBuilder::new(topology, element_name, valid_levels, element_runner_client)
37 }
38
39 pub fn assertive_dependency_token(&self) -> Option<fbroker::DependencyToken> {
40 self.assertive_dependency_token.as_ref().and_then(|token| {
41 Some(token.duplicate_handle(Rights::SAME_RIGHTS).expect("failed to duplicate token"))
42 })
43 }
44
45 pub fn opportunistic_dependency_token(&self) -> Option<fbroker::DependencyToken> {
46 self.opportunistic_dependency_token.as_ref().and_then(|token| {
47 Some(token.duplicate_handle(Rights::SAME_RIGHTS).expect("failed to duplicate token"))
48 })
49 }
50
51 pub fn name(&self) -> &str {
52 &self.name
53 }
54
55 pub async fn run<'a>(
59 &self,
60 element_runner: ServerEnd<fbroker::ElementRunnerMarker>,
61 inspect_node: Option<fuchsia_inspect::Node>,
62 update_fn: Option<Box<dyn Fn(fbroker::PowerLevel) -> LocalBoxFuture<'a, ()> + 'a>>,
63 ) {
64 let mut stream = element_runner.into_stream();
65
66 let mut last_required_level: fbroker::PowerLevel = self.initial_level;
67 let power_level_node = inspect_node
68 .as_ref()
69 .map(|node| node.create_uint("power_level", last_required_level.into()));
70
71 while let Ok(Some(request)) = stream.try_next().await {
72 match request {
73 fbroker::ElementRunnerRequest::SetLevel { level: required_level, responder } => {
74 log::debug!(
75 element_name:? = &self.name,
76 required_level:?,
77 last_required_level:?;
78 "PowerElementContext::run: new level requested"
79 );
80 if required_level != last_required_level {
81 if let Some(update_fn) = &update_fn {
82 update_fn(required_level).await;
83 }
84 if let Some(ref power_level_node) = power_level_node {
85 power_level_node.set(required_level.into());
86 }
87 last_required_level = required_level;
88 } else {
89 log::debug!(
90 element_name:? = &self.name,
91 required_level:?,
92 last_required_level:?;
93 "PowerElementContext::run: required level has not changed, skipping."
94 );
95 }
96 if let Some(err) = responder.send().err() {
97 log::warn!("PowerElementContext::run: SetLevel response failed: {err}");
98 }
99 }
100 fbroker::ElementRunnerRequest::_UnknownMethod { .. } => {}
101 }
102 }
103 }
104}
105
106pub struct PowerElementContextBuilder<'a> {
107 topology: &'a fbroker::TopologyProxy,
108 element_name: &'a str,
109 initial_current_level: fbroker::PowerLevel,
110 element_runner_client: ClientEnd<fbroker::ElementRunnerMarker>,
111 valid_levels: &'a [fbroker::PowerLevel],
112 dependencies: Vec<fbroker::LevelDependency>,
113 register_dependency_tokens: bool,
114}
115
116impl<'a> PowerElementContextBuilder<'a> {
117 pub fn new(
118 topology: &'a fbroker::TopologyProxy,
119 element_name: &'a str,
120 valid_levels: &'a [fbroker::PowerLevel],
121 element_runner_client: ClientEnd<fbroker::ElementRunnerMarker>,
122 ) -> Self {
123 Self {
124 topology,
125 element_name,
126 valid_levels,
127 element_runner_client,
128 initial_current_level: Default::default(),
129 dependencies: Default::default(),
130 register_dependency_tokens: true,
131 }
132 }
133
134 pub fn initial_current_level(mut self, value: fbroker::PowerLevel) -> Self {
135 self.initial_current_level = value;
136 self
137 }
138
139 pub fn dependencies(mut self, value: Vec<fbroker::LevelDependency>) -> Self {
140 self.dependencies = value;
141 self
142 }
143
144 pub fn register_dependency_tokens(mut self, enable: bool) -> Self {
145 self.register_dependency_tokens = enable;
146 self
147 }
148
149 pub async fn build(self) -> Result<PowerElementContext> {
150 let (lessor, lessor_server_end) = create_proxy::<fbroker::LessorMarker>();
151 let (element_control, element_control_server_end) =
152 create_proxy::<fbroker::ElementControlMarker>();
153 self.topology
154 .add_element(fbroker::ElementSchema {
155 element_name: Some(self.element_name.into()),
156 initial_current_level: Some(self.initial_current_level),
157 valid_levels: Some(self.valid_levels.to_vec()),
158 dependencies: Some(self.dependencies),
159 lessor_channel: Some(lessor_server_end),
160 element_control: Some(element_control_server_end),
161 element_runner: Some(self.element_runner_client),
162 ..Default::default()
163 })
164 .await?
165 .map_err(|d| anyhow::anyhow!("{d:?}"))?;
166
167 let assertive_dependency_token = match self.register_dependency_tokens {
168 true => {
169 let token = fbroker::DependencyToken::create();
170 let _ = element_control
171 .register_dependency_token(
172 token
173 .duplicate_handle(Rights::SAME_RIGHTS)
174 .expect("failed to duplicate token"),
175 fbroker::DependencyType::Assertive,
176 )
177 .await?
178 .expect("register assertive dependency token");
179 Some(token)
180 }
181 false => None,
182 };
183
184 let opportunistic_dependency_token = match self.register_dependency_tokens {
185 true => {
186 let token = fbroker::DependencyToken::create();
187 let _ = element_control
188 .register_dependency_token(
189 token
190 .duplicate_handle(Rights::SAME_RIGHTS)
191 .expect("failed to duplicate token"),
192 fbroker::DependencyType::Opportunistic,
193 )
194 .await?
195 .expect("register opportunistic dependency token");
196 Some(token)
197 }
198 false => None,
199 };
200
201 Ok(PowerElementContext {
202 element_control,
203 lessor,
204 assertive_dependency_token,
205 opportunistic_dependency_token,
206 name: self.element_name.to_string(),
207 initial_level: self.initial_current_level,
208 })
209 }
210}
211
212pub struct LeaseDependency {
215 pub dependency_type: fbroker::DependencyType,
216 pub requires_token: fbroker::DependencyToken,
217 pub requires_level_by_preference: Vec<fbroker::PowerLevel>,
218}
219
220pub struct LeaseHelper {
225 lessor: fbroker::LessorProxy,
226}
227
228pub struct Lease {
229 pub control_proxy: fbroker::LeaseControlProxy,
232
233 _helper: Arc<LeaseHelper>,
236}
237
238impl Lease {
239 pub async fn wait_until_satisfied(&self) -> Result<(), fidl::Error> {
240 let mut status = fbroker::LeaseStatus::Unknown;
241 loop {
242 match self.control_proxy.watch_status(status).await? {
243 fbroker::LeaseStatus::Satisfied => break Ok(()),
244 new_status @ _ => status = new_status,
245 }
246 }
247 }
248}
249
250impl LeaseHelper {
251 pub async fn new<'a>(
254 topology: &'a fbroker::TopologyProxy,
255 name: &'a str,
256 lease_dependencies: Vec<LeaseDependency>,
257 ) -> Result<Arc<Self>> {
258 let level_dependencies = lease_dependencies
259 .into_iter()
260 .map(|d| fbroker::LevelDependency {
261 dependency_type: d.dependency_type,
262 dependent_level: BINARY_POWER_LEVELS[1],
263 requires_token: d.requires_token,
264 requires_level_by_preference: d.requires_level_by_preference,
265 })
266 .collect();
267
268 let (element_runner_client, element_runner) =
269 create_endpoints::<fbroker::ElementRunnerMarker>();
270 let element_context = PowerElementContext::builder(
271 topology,
272 name,
273 &BINARY_POWER_LEVELS,
274 element_runner_client,
275 )
276 .dependencies(level_dependencies)
277 .initial_current_level(BINARY_POWER_LEVELS[0])
278 .build()
279 .await?;
280
281 let lessor = element_context.lessor.clone();
282
283 fasync::Task::local(async move {
284 element_context.run(element_runner, None , None).await;
285 })
286 .detach();
287
288 Ok(Arc::new(Self { lessor }))
289 }
290
291 pub async fn create_lease_and_wait_until_satisfied(self: &Arc<Self>) -> Result<Lease> {
294 let lease = self.create_lease().await?;
295 lease.wait_until_satisfied().await?;
296 Ok(lease)
297 }
298
299 pub async fn create_lease(self: &Arc<Self>) -> Result<Lease> {
300 let lease = self
301 .lessor
302 .lease(BINARY_POWER_LEVELS[1])
303 .await?
304 .map_err(|e| anyhow!("PowerBroker::LeaseError({e:?})"))?;
305 Ok(Lease { control_proxy: lease.into_proxy(), _helper: self.clone() })
306 }
307}
308
309#[cfg(test)]
312mod tests {
313 use super::*;
314 use diagnostics_assertions::assert_data_tree;
315 use fidl::endpoints::ClientEnd;
316 use fuchsia_async as fasync;
317 use futures::channel::mpsc;
318 use futures::{FutureExt, StreamExt};
319 use std::cell::RefCell;
320 use std::rc::Rc;
321
322 fn drive_element_runner(
323 element_runner: ClientEnd<fbroker::ElementRunnerMarker>,
324 required_power_levels: Vec<fbroker::PowerLevel>,
325 ) {
326 let proxy = element_runner.into_proxy();
327 fasync::Task::local(async move {
328 for level in required_power_levels.into_iter().rev() {
329 let _ = proxy.set_level(level).await;
330 }
331 })
332 .detach();
333 }
334
335 #[fuchsia::test]
336 async fn power_element_context_run_passes_required_level_to_update_fn() -> Result<()> {
337 let (tx, mut rx) = mpsc::channel(5);
338
339 let (element_control, _element_control_stream) =
340 fidl::endpoints::create_proxy_and_stream::<fbroker::ElementControlMarker>();
341 let (lessor, _lessor_stream) =
342 fidl::endpoints::create_proxy_and_stream::<fbroker::LessorMarker>();
343 let (element_runner_client, element_runner) =
344 create_endpoints::<fbroker::ElementRunnerMarker>();
345 drive_element_runner(element_runner_client, vec![1, 2]);
346
347 let power_element = PowerElementContext {
348 element_control,
349 lessor,
350 assertive_dependency_token: Some(fbroker::DependencyToken::create()),
351 opportunistic_dependency_token: Some(fbroker::DependencyToken::create()),
352 name: "test_element".to_string(),
353 initial_level: 0,
354 };
355
356 power_element
357 .run(
358 element_runner,
359 None,
360 Some(Box::new(|power_level| {
361 let mut tx = tx.clone();
362 async move {
363 tx.start_send(power_level).unwrap();
364 }
365 .boxed_local()
366 })),
367 )
368 .await;
369
370 assert_eq!(2, rx.next().await.unwrap());
371 assert_eq!(1, rx.next().await.unwrap());
372 Ok(())
373 }
374
375 #[fuchsia::test]
376 async fn power_element_context_run_skips_update_on_same_level() -> Result<()> {
377 let (tx, mut rx) = mpsc::channel(5);
378 let initial_level = 5;
379
380 let (element_control, _element_control_stream) =
381 fidl::endpoints::create_proxy_and_stream::<fbroker::ElementControlMarker>();
382 let (lessor, _lessor_stream) =
383 fidl::endpoints::create_proxy_and_stream::<fbroker::LessorMarker>();
384 let (element_runner_client, element_runner) =
385 create_endpoints::<fbroker::ElementRunnerMarker>();
386 drive_element_runner(element_runner_client, vec![3, 1, 1, 2, 2, initial_level]);
387
388 let power_element = PowerElementContext {
389 element_control,
390 lessor,
391 assertive_dependency_token: Some(fbroker::DependencyToken::create()),
392 opportunistic_dependency_token: Some(fbroker::DependencyToken::create()),
393 name: "test_element".to_string(),
394 initial_level,
395 };
396
397 power_element
398 .run(
399 element_runner,
400 None,
401 Some(Box::new(|power_level| {
402 let mut tx = tx.clone();
403 async move {
404 tx.start_send(power_level).unwrap();
405 }
406 .boxed_local()
407 })),
408 )
409 .await;
410
411 assert_eq!(2, rx.next().await.unwrap());
412 assert_eq!(1, rx.next().await.unwrap());
413 assert_eq!(3, rx.next().await.unwrap());
414 Ok(())
415 }
416
417 #[fuchsia::test]
418 async fn power_element_context_run_updates_inspect_node() -> Result<()> {
419 let inspector = fuchsia_inspect::Inspector::default();
420 let (mut tx, rx) = mpsc::channel(5);
421 let (tx2, mut rx2) = mpsc::channel(5);
422 let rx = Rc::new(RefCell::new(rx));
423
424 let (element_control, _element_control_stream) =
425 fidl::endpoints::create_proxy_and_stream::<fbroker::ElementControlMarker>();
426 let (lessor, _lessor_stream) =
427 fidl::endpoints::create_proxy_and_stream::<fbroker::LessorMarker>();
428 let (element_runner_client, element_runner) =
429 create_endpoints::<fbroker::ElementRunnerMarker>();
430 drive_element_runner(element_runner_client, vec![1, 4, 0, 3]);
431
432 let power_element = PowerElementContext {
433 element_control,
434 lessor,
435 assertive_dependency_token: Some(fbroker::DependencyToken::create()),
436 opportunistic_dependency_token: Some(fbroker::DependencyToken::create()),
437 name: "test_element".to_string(),
438 initial_level: 0,
439 };
440
441 let root = inspector.root().clone_weak();
442 fasync::Task::local(async move {
443 power_element
444 .run(
445 element_runner,
446 Some(root),
447 Some(Box::new(|_| {
448 let rx = rx.clone();
449 let mut tx2 = tx2.clone();
450 async move {
451 tx2.start_send(()).unwrap();
452 rx.borrow_mut().next().await.unwrap();
453 }
454 .boxed_local()
455 })),
456 )
457 .await;
458 })
459 .detach();
460
461 rx2.next().await.unwrap();
463 tx.start_send(()).unwrap();
464
465 rx2.next().await.unwrap();
468 assert_data_tree!(inspector, root: {
469 power_level: 3u64
470 });
471 tx.start_send(()).unwrap();
472
473 rx2.next().await.unwrap();
474 assert_data_tree!(inspector, root: {
475 power_level: 0u64
476 });
477 tx.start_send(()).unwrap();
478
479 rx2.next().await.unwrap();
480 assert_data_tree!(inspector, root: {
481 power_level: 4u64
482 });
483 Ok(())
484 }
485}