Skip to main content

session_manager_lib/
power.rs

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