1use 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
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 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 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#[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 rx2.next().await.unwrap();
335 tx.start_send(()).unwrap();
336
337 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}