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 power_broker_client::PowerElementContext;
8use rand::Rng;
9use rand::distr::Alphanumeric;
10use std::sync::Arc;
11use {
12    fidl_fuchsia_power_broker as fbroker, fidl_fuchsia_power_system as fsystem,
13    fuchsia_async as fasync,
14};
15
16/// 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)]
31    power_element_context: Arc<PowerElementContext>,
32
33    /// The first lease on the power element.
34    lease: Option<ClientEnd<fbroker::LeaseControlMarker>>,
35}
36
37/// 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;
45
46impl PowerElement {
47    /// # Panics
48    /// If internal invariants about the state of the `lease` field are violated.
49    pub async fn new() -> Result<Self, anyhow::Error> {
50        let topology = fuchsia_component::client::connect_to_protocol::<fbroker::TopologyMarker>()?;
51        let activity_governor =
52            fuchsia_component::client::connect_to_protocol::<fsystem::ActivityGovernorMarker>()?;
53
54        // Create the PowerMode power element depending on the Execution State of SAG.
55        let power_elements = activity_governor
56            .get_power_elements()
57            .await
58            .context("cannot get power elements from SAG")?;
59        let Some(Some(application_activity_token)) = power_elements
60            .application_activity
61            .map(|application_activity| application_activity.assertive_dependency_token)
62        else {
63            return Err(anyhow!("Did not find application activity assertive dependency token"));
64        };
65
66        // TODO(https://fxbug.dev/316023943): also depend on execution_resume_latency after implemented.
67        let power_levels: Vec<u8> = (0..=POWER_ON_LEVEL).collect();
68        let random_string: String =
69            rand::rng().sample_iter(&Alphanumeric).take(8).map(char::from).collect();
70        let (element_runner_client, element_runner) =
71            create_endpoints::<fbroker::ElementRunnerMarker>();
72        let power_element_context = Arc::new(
73            PowerElementContext::builder(
74                &topology,
75                format!("session-manager-element-{random_string}").as_str(),
76                &power_levels,
77                element_runner_client,
78            )
79            .initial_current_level(POWER_ON_LEVEL)
80            .dependencies(vec![fbroker::LevelDependency {
81                dependency_type: fbroker::DependencyType::Assertive,
82                dependent_level: POWER_ON_LEVEL,
83                requires_token: application_activity_token,
84                requires_level_by_preference: vec![
85                    fsystem::ApplicationActivityLevel::Active.into_primitive(),
86                ],
87            }])
88            .build()
89            .await
90            .map_err(|e| anyhow!("PowerBroker::AddElementError({e:?}"))?,
91        );
92        let pe_context = power_element_context.clone();
93        fasync::Task::local(async move {
94            pe_context.run(element_runner, None /* inspect_node */, None /* update_fn */).await;
95        })
96        .detach();
97
98        // Power on by holding a lease.
99        let lease = power_element_context
100            .lessor
101            .lease(POWER_ON_LEVEL)
102            .await?
103            .map_err(|e| anyhow!("PowerBroker::LeaseError({e:?})"))?;
104
105        // Wait for the lease to be satisfied.
106        let lease = lease.into_proxy();
107        let mut status = fbroker::LeaseStatus::Unknown;
108        loop {
109            match lease.watch_status(status).await? {
110                fbroker::LeaseStatus::Satisfied => break,
111                new_status => status = new_status,
112            }
113        }
114        let lease = lease
115            .into_client_end()
116            .expect("Proxy should be in a valid state to convert into client end");
117
118        let boot_control =
119            fuchsia_component::client::connect_to_protocol::<fsystem::BootControlMarker>()?;
120        let () = boot_control.set_boot_complete().await?;
121
122        Ok(Self { power_element_context, lease: Some(lease) })
123    }
124
125    pub fn take_lease(&mut self) -> Option<ClientEnd<fbroker::LeaseControlMarker>> {
126        self.lease.take()
127    }
128
129    pub fn has_lease(&self) -> bool {
130        self.lease.is_some()
131    }
132}