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