1use anyhow::{anyhow, Result};
5use fidl::endpoints::create_proxy;
6use fuchsia_inspect::Property;
7use futures::future::{FutureExt, LocalBoxFuture};
8use std::sync::Arc;
9use zx::{HandleBased, Rights};
10use {fidl_fuchsia_power_broker as fbroker, fuchsia_async as fasync};
11
12pub const BINARY_POWER_LEVELS: [fbroker::PowerLevel; 2] = [
15 fbroker::BinaryPowerLevel::Off.into_primitive(),
16 fbroker::BinaryPowerLevel::On.into_primitive(),
17];
18
19pub struct PowerElementContext {
20 pub element_control: fbroker::ElementControlProxy,
21 pub lessor: fbroker::LessorProxy,
22 pub required_level: fbroker::RequiredLevelProxy,
23 pub current_level: fbroker::CurrentLevelProxy,
24 assertive_dependency_token: Option<fbroker::DependencyToken>,
25 opportunistic_dependency_token: Option<fbroker::DependencyToken>,
26 name: String,
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 ) -> PowerElementContextBuilder<'a> {
35 PowerElementContextBuilder::new(topology, element_name, valid_levels)
36 }
37
38 pub fn assertive_dependency_token(&self) -> Option<fbroker::DependencyToken> {
39 self.assertive_dependency_token.as_ref().and_then(|token| {
40 Some(token.duplicate_handle(Rights::SAME_RIGHTS).expect("failed to duplicate token"))
41 })
42 }
43
44 pub fn opportunistic_dependency_token(&self) -> Option<fbroker::DependencyToken> {
45 self.opportunistic_dependency_token.as_ref().and_then(|token| {
46 Some(token.duplicate_handle(Rights::SAME_RIGHTS).expect("failed to duplicate token"))
47 })
48 }
49
50 pub fn name(&self) -> &str {
51 &self.name
52 }
53}
54
55pub struct PowerElementContextBuilder<'a> {
56 topology: &'a fbroker::TopologyProxy,
57 element_name: &'a str,
58 initial_current_level: fbroker::PowerLevel,
59 valid_levels: &'a [fbroker::PowerLevel],
60 dependencies: Vec<fbroker::LevelDependency>,
61 register_dependency_tokens: bool,
62}
63
64impl<'a> PowerElementContextBuilder<'a> {
65 pub fn new(
66 topology: &'a fbroker::TopologyProxy,
67 element_name: &'a str,
68 valid_levels: &'a [fbroker::PowerLevel],
69 ) -> Self {
70 Self {
71 topology,
72 element_name,
73 valid_levels,
74 initial_current_level: Default::default(),
75 dependencies: Default::default(),
76 register_dependency_tokens: true,
77 }
78 }
79
80 pub fn initial_current_level(mut self, value: fbroker::PowerLevel) -> Self {
81 self.initial_current_level = value;
82 self
83 }
84
85 pub fn dependencies(mut self, value: Vec<fbroker::LevelDependency>) -> Self {
86 self.dependencies = value;
87 self
88 }
89
90 pub fn register_dependency_tokens(mut self, enable: bool) -> Self {
91 self.register_dependency_tokens = enable;
92 self
93 }
94
95 pub async fn build(self) -> Result<PowerElementContext> {
96 let (current_level, current_level_server_end) =
97 create_proxy::<fbroker::CurrentLevelMarker>();
98 let (required_level, required_level_server_end) =
99 create_proxy::<fbroker::RequiredLevelMarker>();
100 let (lessor, lessor_server_end) = create_proxy::<fbroker::LessorMarker>();
101 let (element_control, element_control_server_end) =
102 create_proxy::<fbroker::ElementControlMarker>();
103 self.topology
104 .add_element(fbroker::ElementSchema {
105 element_name: Some(self.element_name.into()),
106 initial_current_level: Some(self.initial_current_level),
107 valid_levels: Some(self.valid_levels.to_vec()),
108 dependencies: Some(self.dependencies),
109 level_control_channels: Some(fbroker::LevelControlChannels {
110 current: current_level_server_end,
111 required: required_level_server_end,
112 }),
113 lessor_channel: Some(lessor_server_end),
114 element_control: Some(element_control_server_end),
115 ..Default::default()
116 })
117 .await?
118 .map_err(|d| anyhow::anyhow!("{d:?}"))?;
119
120 let assertive_dependency_token = match self.register_dependency_tokens {
121 true => {
122 let token = fbroker::DependencyToken::create();
123 let _ = element_control
124 .register_dependency_token(
125 token
126 .duplicate_handle(Rights::SAME_RIGHTS)
127 .expect("failed to duplicate token"),
128 fbroker::DependencyType::Assertive,
129 )
130 .await?
131 .expect("register assertive dependency token");
132 Some(token)
133 }
134 false => None,
135 };
136
137 let opportunistic_dependency_token = match self.register_dependency_tokens {
138 true => {
139 let token = fbroker::DependencyToken::create();
140 let _ = element_control
141 .register_dependency_token(
142 token
143 .duplicate_handle(Rights::SAME_RIGHTS)
144 .expect("failed to duplicate token"),
145 fbroker::DependencyType::Opportunistic,
146 )
147 .await?
148 .expect("register opportunistic dependency token");
149 Some(token)
150 }
151 false => None,
152 };
153
154 Ok(PowerElementContext {
155 element_control,
156 lessor,
157 required_level,
158 current_level,
159 assertive_dependency_token,
160 opportunistic_dependency_token,
161 name: self.element_name.to_string(),
162 })
163 }
164}
165
166pub fn basic_update_fn_factory<'a>(
171 power_element: &'a PowerElementContext,
172) -> Box<dyn Fn(fbroker::PowerLevel) -> LocalBoxFuture<'a, ()> + 'a> {
173 Box::new(move |new_power_level: fbroker::PowerLevel| {
174 async move {
175 let element_name = power_element.name();
176
177 log::debug!(
178 element_name:?,
179 new_power_level:?;
180 "basic_update_fn_factory: updating current level"
181 );
182
183 let res = power_element.current_level.update(new_power_level).await;
184 if let Err(error) = res {
185 log::warn!(
186 element_name:?,
187 error:?;
188 "basic_update_fn_factory: updating current level failed"
189 );
190 }
191 }
192 .boxed_local()
193 })
194}
195
196pub async fn run_power_element<'a>(
201 element_name: &'a str,
202 required_level_proxy: &'a fbroker::RequiredLevelProxy,
203 initial_level: fbroker::PowerLevel,
204 inspect_node: Option<fuchsia_inspect::Node>,
205 update_fn: Box<dyn Fn(fbroker::PowerLevel) -> LocalBoxFuture<'a, ()> + 'a>,
206) {
207 let mut last_required_level = initial_level;
208 let power_level_node = inspect_node
209 .as_ref()
210 .map(|node| node.create_uint("power_level", last_required_level.into()));
211
212 loop {
213 log::debug!(
214 element_name:?,
215 last_required_level:?;
216 "run_power_element: waiting for new level"
217 );
218 match required_level_proxy.watch().await {
219 Ok(Ok(required_level)) => {
220 log::debug!(
221 element_name:?,
222 required_level:?,
223 last_required_level:?;
224 "run_power_element: new level requested"
225 );
226 if required_level == last_required_level {
227 log::debug!(
228 element_name:?,
229 required_level:?,
230 last_required_level:?;
231 "run_power_element: required level has not changed, skipping."
232 );
233 continue;
234 }
235
236 update_fn(required_level).await;
237 if let Some(ref power_level_node) = power_level_node {
238 power_level_node.set(required_level.into());
239 }
240 last_required_level = required_level;
241 }
242 error => {
243 log::warn!(element_name:?, error:?; "run_power_element: watch_required_level failed");
244 return;
245 }
246 }
247 }
248}
249
250pub struct LeaseDependency {
253 pub dependency_type: fbroker::DependencyType,
254 pub requires_token: fbroker::DependencyToken,
255 pub requires_level_by_preference: Vec<fbroker::PowerLevel>,
256}
257
258pub struct LeaseHelper {
263 lessor: fbroker::LessorProxy,
264 _element_runner: fasync::Task<()>,
265}
266
267pub struct Lease {
268 pub control_proxy: fbroker::LeaseControlProxy,
271
272 _helper: Arc<LeaseHelper>,
275}
276
277impl Lease {
278 pub async fn wait_until_satisfied(&self) -> Result<(), fidl::Error> {
279 let mut status = fbroker::LeaseStatus::Unknown;
280 loop {
281 match self.control_proxy.watch_status(status).await? {
282 fbroker::LeaseStatus::Satisfied => break Ok(()),
283 new_status @ _ => status = new_status,
284 }
285 }
286 }
287}
288
289impl LeaseHelper {
290 pub async fn new<'a>(
293 topology: &'a fbroker::TopologyProxy,
294 name: &'a str,
295 lease_dependencies: Vec<LeaseDependency>,
296 ) -> Result<Arc<Self>> {
297 let level_dependencies = lease_dependencies
298 .into_iter()
299 .map(|d| fbroker::LevelDependency {
300 dependency_type: d.dependency_type,
301 dependent_level: BINARY_POWER_LEVELS[1],
302 requires_token: d.requires_token,
303 requires_level_by_preference: d.requires_level_by_preference,
304 })
305 .collect();
306
307 let element_context = PowerElementContext::builder(topology, name, &BINARY_POWER_LEVELS)
308 .dependencies(level_dependencies)
309 .initial_current_level(BINARY_POWER_LEVELS[0])
310 .build()
311 .await?;
312
313 let lessor = element_context.lessor.clone();
314
315 let _element_runner = fasync::Task::local(async move {
316 run_power_element(
317 &element_context.name(),
318 &element_context.required_level,
319 BINARY_POWER_LEVELS[0], None, basic_update_fn_factory(&element_context),
322 )
323 .await;
324 });
325
326 Ok(Arc::new(Self { lessor, _element_runner }))
327 }
328
329 pub async fn create_lease_and_wait_until_satisfied(self: &Arc<Self>) -> Result<Lease> {
332 let lease = self.create_lease().await?;
333 lease.wait_until_satisfied().await?;
334 Ok(lease)
335 }
336
337 pub async fn create_lease(self: &Arc<Self>) -> Result<Lease> {
338 let lease = self
339 .lessor
340 .lease(BINARY_POWER_LEVELS[1])
341 .await?
342 .map_err(|e| anyhow!("PowerBroker::LeaseError({e:?})"))?;
343 Ok(Lease { control_proxy: lease.into_proxy(), _helper: self.clone() })
344 }
345}
346
347#[cfg(test)]
350mod tests {
351 use super::*;
352 use diagnostics_assertions::assert_data_tree;
353 use fuchsia_async as fasync;
354 use futures::channel::mpsc;
355 use futures::StreamExt;
356 use std::cell::RefCell;
357 use std::rc::Rc;
358
359 fn run_required_level_server(
360 mut required_power_levels: Vec<fbroker::PowerLevel>,
361 ) -> fbroker::RequiredLevelProxy {
362 let (required_level_proxy, mut required_level_stream) =
363 fidl::endpoints::create_proxy_and_stream::<fbroker::RequiredLevelMarker>();
364
365 fasync::Task::local(async move {
366 while let Some(Ok(request)) = required_level_stream.next().await {
367 match request {
368 fbroker::RequiredLevelRequest::Watch { responder } => {
369 responder
370 .send(
371 required_power_levels
372 .pop()
373 .ok_or_else(|| fbroker::RequiredLevelError::Internal),
374 )
375 .unwrap();
376 }
377 _ => unreachable!("Unexpected method call"),
378 }
379 }
380 })
381 .detach();
382
383 required_level_proxy
384 }
385
386 #[fuchsia::test]
387 async fn basic_update_fn_factory_performs_update() -> Result<()> {
388 let (element_control, _element_control_stream) =
389 fidl::endpoints::create_proxy_and_stream::<fbroker::ElementControlMarker>();
390 let (lessor, _lessor_stream) =
391 fidl::endpoints::create_proxy_and_stream::<fbroker::LessorMarker>();
392 let (required_level, _required_level_stream) =
393 fidl::endpoints::create_proxy_and_stream::<fbroker::RequiredLevelMarker>();
394 let (current_level, mut current_level_stream) =
395 fidl::endpoints::create_proxy_and_stream::<fbroker::CurrentLevelMarker>();
396
397 let power_element = PowerElementContext {
398 element_control,
399 lessor,
400 required_level,
401 current_level,
402 assertive_dependency_token: Some(fbroker::DependencyToken::create()),
403 opportunistic_dependency_token: Some(fbroker::DependencyToken::create()),
404 name: "test_name".to_string(),
405 };
406
407 fasync::Task::local(async move {
408 let update_fn = basic_update_fn_factory(&power_element);
409 update_fn(100).await;
410 })
411 .detach();
412
413 let Some(Ok(fbroker::CurrentLevelRequest::Update { current_level, responder })) =
414 current_level_stream.next().await
415 else {
416 unreachable!();
417 };
418
419 responder.send(Ok(())).unwrap();
420 assert_eq!(100, current_level);
421 Ok(())
422 }
423
424 #[fuchsia::test]
425 async fn run_power_element_passes_required_level_to_update_fn() -> Result<()> {
426 let (tx, mut rx) = mpsc::channel(5);
427 let required_level_proxy = run_required_level_server(vec![1, 2]);
428
429 run_power_element(
430 "test_element",
431 &required_level_proxy,
432 0,
433 None,
434 Box::new(|power_level| {
435 let mut tx = tx.clone();
436 async move {
437 tx.start_send(power_level).unwrap();
438 }
439 .boxed_local()
440 }),
441 )
442 .await;
443
444 assert_eq!(2, rx.next().await.unwrap());
445 assert_eq!(1, rx.next().await.unwrap());
446 Ok(())
447 }
448
449 #[fuchsia::test]
450 async fn run_power_element_skips_update_on_same_level() -> Result<()> {
451 let (tx, mut rx) = mpsc::channel(5);
452 let initial_level = 5;
453 let required_level_proxy = run_required_level_server(vec![3, 1, 1, 2, 2, initial_level]);
454
455 run_power_element(
456 "test_element",
457 &required_level_proxy,
458 initial_level,
459 None,
460 Box::new(|power_level| {
461 let mut tx = tx.clone();
462 async move {
463 tx.start_send(power_level).unwrap();
464 }
465 .boxed_local()
466 }),
467 )
468 .await;
469
470 assert_eq!(2, rx.next().await.unwrap());
471 assert_eq!(1, rx.next().await.unwrap());
472 assert_eq!(3, rx.next().await.unwrap());
473 Ok(())
474 }
475
476 #[fuchsia::test]
477 async fn run_power_element_updates_inspect_node() -> Result<()> {
478 let inspector = fuchsia_inspect::Inspector::default();
479 let (mut tx, rx) = mpsc::channel(5);
480 let (tx2, mut rx2) = mpsc::channel(5);
481 let rx = Rc::new(RefCell::new(rx));
482 let required_level_proxy = run_required_level_server(vec![1, 4, 0, 3]);
483
484 let root = inspector.root().clone_weak();
485 fasync::Task::local(async move {
486 run_power_element(
487 "test_element",
488 &required_level_proxy,
489 0,
490 Some(root),
491 Box::new(|_| {
492 let rx = rx.clone();
493 let mut tx2 = tx2.clone();
494 async move {
495 tx2.start_send(()).unwrap();
496 rx.borrow_mut().next().await.unwrap();
497 }
498 .boxed_local()
499 }),
500 )
501 .await;
502 })
503 .detach();
504
505 rx2.next().await.unwrap();
507 tx.start_send(()).unwrap();
508
509 rx2.next().await.unwrap();
512 assert_data_tree!(inspector, root: {
513 power_level: 3u64
514 });
515 tx.start_send(()).unwrap();
516
517 rx2.next().await.unwrap();
518 assert_data_tree!(inspector, root: {
519 power_level: 0u64
520 });
521 tx.start_send(()).unwrap();
522
523 rx2.next().await.unwrap();
524 assert_data_tree!(inspector, root: {
525 power_level: 4u64
526 });
527 Ok(())
528 }
529}