Skip to main content

power_broker_client/
lib.rs

1// Copyright 2023 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4use anyhow::Result;
5use fidl::endpoints::{ClientEnd, ServerEnd, create_proxy};
6use fidl_fuchsia_power_broker as fbroker;
7use fuchsia_inspect::{self, Property};
8use futures::TryStreamExt;
9use futures::future::LocalBoxFuture;
10use zx::{HandleBased, Rights};
11
12/// A well-known set of PowerLevels to be specified as the valid_levels for a
13/// power element. This is the set of levels in fbroker::BinaryPowerLevel.
14pub 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    assertive_dependency_token: Option<fbroker::DependencyToken>,
23    name: String,
24    initial_level: fbroker::PowerLevel,
25}
26
27impl PowerElementContext {
28    pub fn builder<'a>(
29        topology: &'a fbroker::TopologyProxy,
30        element_name: &'a str,
31        valid_levels: &'a [fbroker::PowerLevel],
32        element_runner_client: ClientEnd<fbroker::ElementRunnerMarker>,
33    ) -> PowerElementContextBuilder<'a> {
34        PowerElementContextBuilder::new(topology, element_name, valid_levels, element_runner_client)
35    }
36
37    pub fn assertive_dependency_token(&self) -> Option<fbroker::DependencyToken> {
38        self.assertive_dependency_token.as_ref().and_then(|token| {
39            Some(token.duplicate_handle(Rights::SAME_RIGHTS).expect("failed to duplicate token"))
40        })
41    }
42
43    pub fn name(&self) -> &str {
44        &self.name
45    }
46
47    /// Runs a procedure that calls an update function when the required power level changes.
48    ///
49    /// The power element's power level is expected to be updated in `update_fn`, if supplied.
50    pub async fn run<'a>(
51        &self,
52        element_runner: ServerEnd<fbroker::ElementRunnerMarker>,
53        inspect_node: Option<fuchsia_inspect::Node>,
54        update_fn: Option<Box<dyn Fn(fbroker::PowerLevel) -> LocalBoxFuture<'a, ()> + 'a>>,
55    ) {
56        let mut stream = element_runner.into_stream();
57
58        let mut last_required_level: fbroker::PowerLevel = self.initial_level;
59        let power_level_node = inspect_node
60            .as_ref()
61            .map(|node| node.create_uint("power_level", last_required_level.into()));
62
63        while let Ok(Some(request)) = stream.try_next().await {
64            match request {
65                fbroker::ElementRunnerRequest::SetLevel { level: required_level, responder } => {
66                    log::debug!(
67                        element_name:? = &self.name,
68                        required_level:?,
69                        last_required_level:?;
70                        "PowerElementContext::run: new level requested"
71                    );
72                    if required_level != last_required_level {
73                        if let Some(update_fn) = &update_fn {
74                            update_fn(required_level).await;
75                        }
76                        if let Some(ref power_level_node) = power_level_node {
77                            power_level_node.set(required_level.into());
78                        }
79                        last_required_level = required_level;
80                    } else {
81                        log::debug!(
82                            element_name:? = &self.name,
83                            required_level:?,
84                            last_required_level:?;
85                            "PowerElementContext::run: required level has not changed, skipping."
86                        );
87                    }
88                    if let Some(err) = responder.send().err() {
89                        log::warn!("PowerElementContext::run: SetLevel response failed: {err}");
90                    }
91                }
92                fbroker::ElementRunnerRequest::_UnknownMethod { .. } => {}
93            }
94        }
95    }
96}
97
98pub struct PowerElementContextBuilder<'a> {
99    topology: &'a fbroker::TopologyProxy,
100    element_name: &'a str,
101    initial_current_level: fbroker::PowerLevel,
102    element_runner_client: ClientEnd<fbroker::ElementRunnerMarker>,
103    valid_levels: &'a [fbroker::PowerLevel],
104    dependencies: Vec<fbroker::LevelDependency>,
105    register_dependency_tokens: bool,
106}
107
108impl<'a> PowerElementContextBuilder<'a> {
109    pub fn new(
110        topology: &'a fbroker::TopologyProxy,
111        element_name: &'a str,
112        valid_levels: &'a [fbroker::PowerLevel],
113        element_runner_client: ClientEnd<fbroker::ElementRunnerMarker>,
114    ) -> Self {
115        Self {
116            topology,
117            element_name,
118            valid_levels,
119            element_runner_client,
120            initial_current_level: Default::default(),
121            dependencies: Default::default(),
122            register_dependency_tokens: true,
123        }
124    }
125
126    pub fn initial_current_level(mut self, value: fbroker::PowerLevel) -> Self {
127        self.initial_current_level = value;
128        self
129    }
130
131    pub fn dependencies(mut self, value: Vec<fbroker::LevelDependency>) -> Self {
132        self.dependencies = value;
133        self
134    }
135
136    pub fn register_dependency_tokens(mut self, enable: bool) -> Self {
137        self.register_dependency_tokens = enable;
138        self
139    }
140
141    pub async fn build(self) -> Result<PowerElementContext> {
142        let (lessor, lessor_server_end) = create_proxy::<fbroker::LessorMarker>();
143        let (element_control, element_control_server_end) =
144            create_proxy::<fbroker::ElementControlMarker>();
145        self.topology
146            .add_element(fbroker::ElementSchema {
147                element_name: Some(self.element_name.into()),
148                initial_current_level: Some(self.initial_current_level),
149                valid_levels: Some(self.valid_levels.to_vec()),
150                dependencies: Some(self.dependencies),
151                lessor_channel: Some(lessor_server_end),
152                element_control: Some(element_control_server_end),
153                element_runner: Some(self.element_runner_client),
154                ..Default::default()
155            })
156            .await?
157            .map_err(|d| anyhow::anyhow!("{d:?}"))?;
158
159        let assertive_dependency_token = match self.register_dependency_tokens {
160            true => {
161                let token = fbroker::DependencyToken::create();
162                let _ = element_control
163                    .register_dependency_token(
164                        token
165                            .duplicate_handle(Rights::SAME_RIGHTS)
166                            .expect("failed to duplicate token"),
167                    )
168                    .await?
169                    .expect("register assertive dependency token");
170                Some(token)
171            }
172            false => None,
173        };
174
175        Ok(PowerElementContext {
176            element_control,
177            lessor,
178            assertive_dependency_token,
179            name: self.element_name.to_string(),
180            initial_level: self.initial_current_level,
181        })
182    }
183}
184
185// TODO(https://fxbug.dev/349841776): Use this as a demo case for test library support for faking
186// Power Broker interfaces.
187#[cfg(test)]
188mod tests {
189    use super::*;
190    use diagnostics_assertions::assert_data_tree;
191    use fidl::endpoints::{ClientEnd, create_endpoints};
192    use fuchsia_async as fasync;
193    use futures::channel::mpsc;
194    use futures::{FutureExt, StreamExt};
195    use std::cell::RefCell;
196    use std::rc::Rc;
197
198    fn drive_element_runner(
199        element_runner: ClientEnd<fbroker::ElementRunnerMarker>,
200        required_power_levels: Vec<fbroker::PowerLevel>,
201    ) {
202        let proxy = element_runner.into_proxy();
203        fasync::Task::local(async move {
204            for level in required_power_levels.into_iter().rev() {
205                let _ = proxy.set_level(level).await;
206            }
207        })
208        .detach();
209    }
210
211    #[fuchsia::test]
212    async fn power_element_context_run_passes_required_level_to_update_fn() -> Result<()> {
213        let (tx, mut rx) = mpsc::channel(5);
214
215        let (element_control, _element_control_stream) =
216            fidl::endpoints::create_proxy_and_stream::<fbroker::ElementControlMarker>();
217        let (lessor, _lessor_stream) =
218            fidl::endpoints::create_proxy_and_stream::<fbroker::LessorMarker>();
219        let (element_runner_client, element_runner) =
220            create_endpoints::<fbroker::ElementRunnerMarker>();
221        drive_element_runner(element_runner_client, vec![1, 2]);
222        let power_element = PowerElementContext {
223            element_control,
224            lessor,
225            assertive_dependency_token: Some(fbroker::DependencyToken::create()),
226            name: "test_element".to_string(),
227            initial_level: 0,
228        };
229
230        power_element
231            .run(
232                element_runner,
233                None,
234                Some(Box::new(|power_level| {
235                    let mut tx = tx.clone();
236                    async move {
237                        tx.start_send(power_level).unwrap();
238                    }
239                    .boxed_local()
240                })),
241            )
242            .await;
243
244        assert_eq!(2, rx.next().await.unwrap());
245        assert_eq!(1, rx.next().await.unwrap());
246        Ok(())
247    }
248
249    #[fuchsia::test]
250    async fn power_element_context_run_skips_update_on_same_level() -> Result<()> {
251        let (tx, mut rx) = mpsc::channel(5);
252        let initial_level = 5;
253
254        let (element_control, _element_control_stream) =
255            fidl::endpoints::create_proxy_and_stream::<fbroker::ElementControlMarker>();
256        let (lessor, _lessor_stream) =
257            fidl::endpoints::create_proxy_and_stream::<fbroker::LessorMarker>();
258        let (element_runner_client, element_runner) =
259            create_endpoints::<fbroker::ElementRunnerMarker>();
260        drive_element_runner(element_runner_client, vec![3, 1, 1, 2, 2, initial_level]);
261
262        let power_element = PowerElementContext {
263            element_control,
264            lessor,
265            assertive_dependency_token: Some(fbroker::DependencyToken::create()),
266            name: "test_element".to_string(),
267            initial_level,
268        };
269
270        power_element
271            .run(
272                element_runner,
273                None,
274                Some(Box::new(|power_level| {
275                    let mut tx = tx.clone();
276                    async move {
277                        tx.start_send(power_level).unwrap();
278                    }
279                    .boxed_local()
280                })),
281            )
282            .await;
283
284        assert_eq!(2, rx.next().await.unwrap());
285        assert_eq!(1, rx.next().await.unwrap());
286        assert_eq!(3, rx.next().await.unwrap());
287        Ok(())
288    }
289
290    #[fuchsia::test]
291    async fn power_element_context_run_updates_inspect_node() -> Result<()> {
292        let inspector = fuchsia_inspect::Inspector::default();
293        let (mut tx, rx) = mpsc::channel(5);
294        let (tx2, mut rx2) = mpsc::channel(5);
295        let rx = Rc::new(RefCell::new(rx));
296
297        let (element_control, _element_control_stream) =
298            fidl::endpoints::create_proxy_and_stream::<fbroker::ElementControlMarker>();
299        let (lessor, _lessor_stream) =
300            fidl::endpoints::create_proxy_and_stream::<fbroker::LessorMarker>();
301        let (element_runner_client, element_runner) =
302            create_endpoints::<fbroker::ElementRunnerMarker>();
303        drive_element_runner(element_runner_client, vec![1, 4, 0, 3]);
304
305        let power_element = PowerElementContext {
306            element_control,
307            lessor,
308            assertive_dependency_token: Some(fbroker::DependencyToken::create()),
309            name: "test_element".to_string(),
310            initial_level: 0,
311        };
312
313        let root = inspector.root().clone_weak();
314        fasync::Task::local(async move {
315            power_element
316                .run(
317                    element_runner,
318                    Some(root),
319                    Some(Box::new(|_| {
320                        let rx = rx.clone();
321                        let mut tx2 = tx2.clone();
322                        async move {
323                            tx2.start_send(()).unwrap();
324                            rx.borrow_mut().next().await.unwrap();
325                        }
326                        .boxed_local()
327                    })),
328                )
329                .await;
330        })
331        .detach();
332
333        // The first communication hasn't updated the tree yet.
334        rx2.next().await.unwrap();
335        tx.start_send(()).unwrap();
336
337        // Now that the update function has been called twice, the inspect tree
338        // should show the first power level. This pattern continues
339        rx2.next().await.unwrap();
340        assert_data_tree!(inspector, root: {
341            power_level: 3u64
342        });
343        tx.start_send(()).unwrap();
344
345        rx2.next().await.unwrap();
346        assert_data_tree!(inspector, root: {
347            power_level: 0u64
348        });
349        tx.start_send(()).unwrap();
350
351        rx2.next().await.unwrap();
352        assert_data_tree!(inspector, root: {
353            power_level: 4u64
354        });
355        Ok(())
356    }
357}