1// Copyright 2024 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.
45use anyhow::{anyhow, Context};
6use fidl::endpoints::{ClientEnd, Proxy};
7use power_broker_client::{basic_update_fn_factory, run_power_element, PowerElementContext};
8use rand::distributions::Alphanumeric;
9use rand::Rng;
10use std::sync::Arc;
11use {
12 fidl_fuchsia_power_broker as fbroker, fidl_fuchsia_power_system as fsystem,
13 fuchsia_async as fasync,
14};
1516/// A power element representing the session.
17///
18/// This power element is owned and registered by `session_manager`. This power element is
19/// added in the power topology as a dependent on the Application Activity element that is
20/// owned by the SAG.
21///
22/// After `session_manager` starts, a power-on lease will be created and retained.
23/// The session component may fetch the lease from `session_manager` and decide when to
24/// drop it.
25///
26/// When stopping or restarting the session, the power element and the power-on lease will
27/// be recreated, returning thing to the initial started state.
28pub struct PowerElement {
29// Keeps the element alive.
30#[allow(dead_code)]
31power_element_context: Arc<PowerElementContext>,
3233/// The first lease on the power element.
34lease: Option<ClientEnd<fbroker::LeaseControlMarker>>,
35}
3637/// The power levels defined for the session manager power element.
38///
39/// | Power Mode | Level |
40/// | ----------------- | ----- |
41/// | On | 1 |
42/// | Off | 0 |
43///
44static POWER_ON_LEVEL: fbroker::PowerLevel = 1;
4546impl PowerElement {
47/// # Panics
48 /// If internal invariants about the state of the `lease` field are violated.
49pub async fn new() -> Result<Self, anyhow::Error> {
50let topology = fuchsia_component::client::connect_to_protocol::<fbroker::TopologyMarker>()?;
51let activity_governor =
52 fuchsia_component::client::connect_to_protocol::<fsystem::ActivityGovernorMarker>()?;
5354// Create the PowerMode power element depending on the Execution State of SAG.
55let power_elements = activity_governor
56 .get_power_elements()
57 .await
58.context("cannot get power elements from SAG")?;
59let Some(Some(application_activity_token)) = power_elements
60 .application_activity
61 .map(|application_activity| application_activity.assertive_dependency_token)
62else {
63return Err(anyhow!("Did not find application activity assertive dependency token"));
64 };
6566// TODO(https://fxbug.dev/316023943): also depend on execution_resume_latency after implemented.
67let power_levels: Vec<u8> = (0..=POWER_ON_LEVEL).collect();
68let random_string: String =
69 rand::thread_rng().sample_iter(&Alphanumeric).take(8).map(char::from).collect();
70let power_element_context = Arc::new(
71 PowerElementContext::builder(
72&topology,
73format!("session-manager-element-{random_string}").as_str(),
74&power_levels,
75 )
76 .initial_current_level(POWER_ON_LEVEL)
77 .dependencies(vec![fbroker::LevelDependency {
78 dependency_type: fbroker::DependencyType::Assertive,
79 dependent_level: POWER_ON_LEVEL,
80 requires_token: application_activity_token,
81 requires_level_by_preference: vec![
82 fsystem::ApplicationActivityLevel::Active.into_primitive()
83 ],
84 }])
85 .build()
86 .await
87.map_err(|e| anyhow!("PowerBroker::AddElementError({e:?}"))?,
88 );
89let pe_context = power_element_context.clone();
90 fasync::Task::local(async move {
91 run_power_element(
92 pe_context.name(),
93&pe_context.required_level,
94 POWER_ON_LEVEL, /* initial_level */
95None, /* inspect_node */
96basic_update_fn_factory(&pe_context),
97 )
98 .await;
99 })
100 .detach();
101102// Power on by holding a lease.
103let lease = power_element_context
104 .lessor
105 .lease(POWER_ON_LEVEL)
106 .await?
107.map_err(|e| anyhow!("PowerBroker::LeaseError({e:?})"))?;
108109// Wait for the lease to be satisfied.
110let lease = lease.into_proxy();
111let mut status = fbroker::LeaseStatus::Unknown;
112loop {
113match lease.watch_status(status).await? {
114 fbroker::LeaseStatus::Satisfied => break,
115 new_status => status = new_status,
116 }
117 }
118let lease = lease
119 .into_client_end()
120 .expect("Proxy should be in a valid state to convert into client end");
121122let boot_control =
123 fuchsia_component::client::connect_to_protocol::<fsystem::BootControlMarker>()?;
124let () = boot_control.set_boot_complete().await?;
125126Ok(Self { power_element_context, lease: Some(lease) })
127 }
128129pub fn take_lease(&mut self) -> Option<ClientEnd<fbroker::LeaseControlMarker>> {
130self.lease.take()
131 }
132133pub fn has_lease(&self) -> bool {
134self.lease.is_some()
135 }
136}