1use core::future::Future;
6use fdf_component::{Driver, DriverContext};
7use fidl_fuchsia_hardware_power as fhw_power;
8use fidl_fuchsia_power_broker as fpower_broker;
9use fidl_fuchsia_power_system as fpower;
10use fuchsia_async as fasync;
11use futures::TryStreamExt;
12use log::{error, warn};
13use std::sync::{Arc, Weak};
14use zx::Status;
15
16use fidl_next as _;
17
18pub trait SuspendableDriver: Driver {
21 fn suspend(&self) -> impl Future<Output = ()> + Send;
24
25 fn resume(&self) -> impl Future<Output = ()> + Send;
28
29 fn suspend_enabled(&self) -> bool;
32}
33
34pub struct Suspendable<T: Driver> {
36 #[expect(unused)]
37 scope: Option<fasync::Scope>,
38 driver: Arc<T>,
39}
40
41async fn run_suspend_blocker<T: SuspendableDriver>(
42 driver: Weak<T>,
43 mut service: fpower::SuspendBlockerRequestStream,
44) {
45 use fpower::SuspendBlockerRequest::*;
46 while let Some(req) = service.try_next().await.unwrap() {
47 match req {
48 BeforeSuspend { responder, .. } => {
49 if let Some(driver) = driver.upgrade() {
50 driver.suspend().await;
51 } else {
52 return;
53 }
54 let _ = responder.send();
55 }
56 AfterResume { responder, .. } => {
57 if let Some(driver) = driver.upgrade() {
58 driver.resume().await;
59 } else {
60 return;
61 }
62 let _ = responder.send();
63 }
64 _ => {
66 warn!("Received unknown sag listener request");
67 }
68 }
69 }
70}
71
72async fn run_element_runner<T: SuspendableDriver>(
73 driver: Weak<T>,
74 mut service: fpower_broker::ElementRunnerRequestStream,
75) {
76 let mut first_activation_occurred = false;
77 while let Some(req) = service.try_next().await.unwrap_or_default() {
78 if let fpower_broker::ElementRunnerRequest::SetLevel { level, responder } = req {
79 if level != fhw_power::FrameworkElementLevels::Off.into_primitive() as u8 {
80 first_activation_occurred = true;
81 if let Some(driver) = driver.upgrade() {
82 driver.resume().await;
83 } else {
84 return;
85 }
86 } else if first_activation_occurred {
87 if let Some(driver) = driver.upgrade() {
88 driver.suspend().await;
89 } else {
90 return;
91 }
92 }
93 let _ = responder.send();
94 }
95 }
96}
97
98impl<T: SuspendableDriver + Send + Sync> Driver for Suspendable<T> {
99 const NAME: &str = T::NAME;
100
101 async fn start(mut context: DriverContext) -> Result<Self, Status> {
102 let mut runner = context
103 .start_args
104 .power_element_args
105 .as_mut()
106 .and_then(|args| args.runner_server.take());
107
108 let mut sag = None;
109 if runner.is_none() {
110 sag = Some(
111 context.incoming.connect_protocol::<fpower::ActivityGovernorProxy>().map_err(
112 |err| {
113 error!("Error connecting to sag: {err}");
114 Status::INTERNAL
115 },
116 )?,
117 );
118 }
119
120 let driver = Arc::new(T::start(context).await?);
121
122 let scope = if driver.suspend_enabled() {
123 let scope = fasync::Scope::new_with_name("suspend");
124 if let Some(runner) = runner.take() {
125 let weak_driver = Arc::downgrade(&driver);
126 scope.spawn(
127 async move { run_element_runner(weak_driver, runner.into_stream()).await },
128 );
129 } else if let Some(sag) = sag.take() {
130 let (client, server) = fidl::endpoints::create_endpoints();
131
132 let _ = sag
133 .register_suspend_blocker(
134 fpower::ActivityGovernorRegisterSuspendBlockerRequest {
135 suspend_blocker: Some(client),
136 name: Some(Self::NAME.into()),
137 ..Default::default()
138 },
139 )
140 .await
141 .map_err(|err| {
142 error!("Error connecting to sag: {err}");
143 Status::INTERNAL
144 })?
145 .map_err(|err| {
146 error!("Error connecting to sag: {err:?}");
147 Status::INTERNAL
148 })?;
149
150 let weak_driver = Arc::downgrade(&driver);
151 scope.spawn(
152 async move { run_suspend_blocker(weak_driver, server.into_stream()).await },
153 );
154 }
155 Some(scope)
156 } else {
157 None
158 };
159
160 Ok(Self { driver, scope })
161 }
162
163 async fn stop(&self) {
164 self.driver.stop().await;
165 }
166}
167
168#[cfg(test)]
169mod tests {
170 use super::*;
171 use fdf_component::testing::harness::TestHarness;
172 use fidl_fuchsia_driver_framework as fdf;
173 use std::sync::atomic::{AtomicBool, Ordering};
174
175 struct TestDriver {
176 suspend_called: Arc<AtomicBool>,
177 resume_called: Arc<AtomicBool>,
178 suspend_enabled: bool,
179 stop_called: Arc<AtomicBool>,
180 }
181
182 impl Driver for TestDriver {
183 const NAME: &str = "test_driver";
184
185 async fn start(_context: DriverContext) -> Result<Self, Status> {
186 Ok(Self {
187 suspend_called: Arc::new(AtomicBool::new(false)),
188 resume_called: Arc::new(AtomicBool::new(false)),
189 suspend_enabled: true,
190 stop_called: Arc::new(AtomicBool::new(false)),
191 })
192 }
193
194 async fn stop(&self) {
195 self.stop_called.store(true, Ordering::SeqCst);
196 }
197 }
198
199 impl SuspendableDriver for TestDriver {
200 async fn suspend(&self) {
201 self.suspend_called.store(true, Ordering::SeqCst);
202 }
203
204 async fn resume(&self) {
205 self.resume_called.store(true, Ordering::SeqCst);
206 }
207
208 fn suspend_enabled(&self) -> bool {
209 self.suspend_enabled
210 }
211 }
212
213 #[fuchsia::test]
214 async fn test_suspend_resume_with_runner() {
215 let (runner_client, runner_server) =
216 fidl::endpoints::create_endpoints::<fpower_broker::ElementRunnerMarker>();
217
218 let mut harness = TestHarness::<Suspendable<TestDriver>>::new().set_power_element_args(
219 fdf::PowerElementArgs { runner_server: Some(runner_server), ..Default::default() },
220 );
221
222 let driver_under_test = harness.start_driver().await.expect("Failed to start driver");
223 let (test_driver_stop_called, test_driver_resume_called, test_driver_suspend_called) = {
224 let suspendable = driver_under_test.get_driver().expect("Failed to get driver");
225 (
226 suspendable.driver.stop_called.clone(),
227 suspendable.driver.resume_called.clone(),
228 suspendable.driver.suspend_called.clone(),
229 )
230 };
231
232 let runner_proxy = runner_client.into_proxy();
233
234 runner_proxy.set_level(1).await.expect("Failed to set level");
236 assert!(test_driver_resume_called.load(Ordering::SeqCst));
237 test_driver_resume_called.store(false, Ordering::SeqCst);
238
239 runner_proxy.set_level(0).await.expect("Failed to set level");
241 assert!(test_driver_suspend_called.load(Ordering::SeqCst));
242 test_driver_suspend_called.store(false, Ordering::SeqCst);
243
244 runner_proxy.set_level(1).await.expect("Failed to set level");
246 assert!(test_driver_resume_called.load(Ordering::SeqCst));
247
248 driver_under_test.stop_driver().await;
249 assert!(test_driver_stop_called.load(Ordering::SeqCst));
250 }
251}