1use anyhow::{Context as _, Error};
6use async_trait::async_trait;
7use derivative::Derivative;
8use fidl::endpoints::ProtocolMarker;
9use fidl_fuchsia_hardware_backlight as backlight;
10
11use fidl_fuchsia_hardware_backlight::{DeviceProxy as BacklightProxy, State as BacklightCommand};
12use fidl_fuchsia_ui_display_singleton::{DisplayPowerMarker, DisplayPowerProxy};
13use fuchsia_async as fasync;
14use fuchsia_component::client::{connect_to_protocol, Service};
15use futures::channel::oneshot;
16use futures::lock::Mutex;
17use std::sync::Arc;
18
19const MIN_REGULATED_BRIGHTNESS: f64 = 0.0004;
21const MAX_REGULATED_BRIGHTNESS: f64 = 1.0;
23
24async fn open_backlight() -> Result<BacklightProxy, Error> {
25 log::trace!("Opening backlight device");
26 let device = Service::open(backlight::ServiceMarker)
27 .context("Failed to open service")?
28 .watch_for_any()
29 .await
30 .context("Failed to find instance")?
31 .connect_to_backlight()
32 .context("Failed to connect to backlight service")?;
33 log::info!("Opening backlight");
34 Ok(device)
35}
36
37fn open_display_power_service() -> Result<DisplayPowerProxy, Error> {
38 log::info!("Opening display controller");
39 connect_to_protocol::<DisplayPowerMarker>()
40 .context("Failed to connect to display power service")
41}
42
43#[derive(Derivative)]
51#[derivative(Debug)]
52enum PowerState {
53 Indeterminate,
56 BothOn,
57 BacklightOffDisplayPoweringDown(#[derivative(Debug = "ignore")] fasync::Task<()>),
59 BothOff,
60 DisplayOnBacklightPoweringUp(
63 #[derivative(Debug = "ignore")] fasync::Task<()>,
64 Vec<PendingBacklightCommand>,
65 ),
66}
67
68impl Default for PowerState {
69 fn default() -> Self {
70 Self::Indeterminate
71 }
72}
73
74#[derive(Debug)]
76struct PendingBacklightCommand {
77 command: BacklightCommand,
78 future_handle: oneshot::Sender<Result<(), Error>>,
81}
82
83#[derive(Debug, Clone)]
84pub struct Backlight {
85 backlight_proxy: BacklightProxy,
86 display_power: Option<DisplayPower>,
87}
88
89impl Backlight {
90 pub async fn without_display_power() -> Result<Self, Error> {
93 let backlight_proxy = open_backlight().await?;
94 Self::without_display_power_internal(backlight_proxy)
95 }
96
97 fn without_display_power_internal(backlight_proxy: BacklightProxy) -> Result<Self, Error> {
98 Ok(Backlight { backlight_proxy, display_power: None })
99 }
100
101 #[allow(unused)]
104 pub async fn with_display_power(
105 power_off_delay_millis: u16,
106 power_on_delay_millis: u16,
107 ) -> Result<Self, Error> {
108 let backlight_proxy = open_backlight().await?;
109 let display_power_proxy = open_display_power_service()?;
110 Self::with_display_power_internal(
111 backlight_proxy,
112 display_power_proxy,
113 zx::MonotonicDuration::from_millis(power_off_delay_millis as i64),
114 zx::MonotonicDuration::from_millis(power_on_delay_millis as i64),
115 )
116 .await
117 }
118
119 async fn with_display_power_internal(
120 backlight_proxy: BacklightProxy,
121 display_power_proxy: DisplayPowerProxy,
122 power_off_delay: impl Into<zx::MonotonicDuration>,
123 power_on_delay: impl Into<zx::MonotonicDuration>,
124 ) -> Result<Self, Error> {
125 let display_power = DisplayPower::new(
126 &backlight_proxy,
127 display_power_proxy,
128 power_off_delay,
129 power_on_delay,
130 )
131 .await?;
132 Ok(Backlight { backlight_proxy, display_power: Some(display_power) })
133 }
134
135 pub async fn get_max_absolute_brightness(&self) -> Result<f64, Error> {
136 let connection = self
137 .backlight_proxy
138 .get_max_absolute_brightness()
139 .await
140 .context("Didn't connect correctly")?;
141 let max_brightness: f64 = connection
142 .map_err(zx::Status::from_raw)
143 .context("Failed to get_max_absolute_brightness")?;
144 Ok(max_brightness)
145 }
146
147 async fn get(&self) -> Result<f64, Error> {
148 let backlight_info = get_state_normalized(&self.backlight_proxy).await?;
149 assert!(backlight_info.brightness >= 0.0);
150 assert!(backlight_info.brightness <= MAX_REGULATED_BRIGHTNESS);
151 Ok(if backlight_info.backlight_on { backlight_info.brightness } else { 0.0 })
152 }
153
154 async fn set(&self, value: f64) -> Result<(), Error> {
155 let regulated_value =
157 num_traits::clamp(value, MIN_REGULATED_BRIGHTNESS, MAX_REGULATED_BRIGHTNESS);
158 let backlight_on = value > 0.0;
159
160 match self.display_power.as_ref() {
161 None => self.set_backlight_state_normalized(regulated_value, backlight_on).await,
162 Some(display_power) => {
163 self.clone().set_dual_state(display_power, regulated_value, backlight_on).await
164 }
165 }
166 }
167
168 async fn set_dual_state(
169 &self,
170 display_power: &DisplayPower,
171 regulated_value: f64,
172 backlight_on: bool,
173 ) -> Result<(), Error> {
174 let power_state_arc = display_power.power_state.clone();
175 let mut power_state = power_state_arc.lock().await;
179 match &mut *power_state {
180 PowerState::BothOn => {
181 if backlight_on {
182 } else {
184 self.set_backlight_state_normalized(regulated_value, backlight_on).await?;
185 log::info!("Turned backlight off");
186 log::info!("DDIC power off scheduled");
187 let task =
188 self.clone().make_scheduled_updates_task(display_power.power_off_delay);
189 *power_state = PowerState::BacklightOffDisplayPoweringDown(task);
190 }
191 drop(power_state);
192 self.set_backlight_state_normalized(regulated_value, backlight_on).await
193 }
194 PowerState::BacklightOffDisplayPoweringDown(_task) => {
195 if backlight_on {
196 log::info!("DDIC power on cancelled");
197 *power_state = PowerState::BothOn;
199 drop(power_state);
200 self.set_backlight_state_normalized(regulated_value, backlight_on).await
201 } else {
202 drop(power_state);
204 Ok(())
205 }
206 }
207 PowerState::BothOff => {
208 if backlight_on {
209 display_power.set_display_power_and_log_errors(true).await?;
210 let (pending_change, receiver) =
211 Self::make_pending_change(regulated_value, backlight_on);
212 let task =
213 self.clone().make_scheduled_updates_task(display_power.power_on_delay);
214 *power_state =
215 PowerState::DisplayOnBacklightPoweringUp(task, vec![pending_change]);
216 drop(power_state);
217 log::info!("Backlight power on scheduled");
218 receiver.await?
219 } else {
220 display_power.set_display_power_and_log_errors(false).await?;
221 drop(power_state);
222 Ok(())
223 }
224 }
225 PowerState::DisplayOnBacklightPoweringUp(_task, ref mut pending_changes) => {
226 if backlight_on {
227 let (pending_change, receiver) =
228 Self::make_pending_change(regulated_value, backlight_on);
229 pending_changes.push(pending_change);
230 drop(power_state);
231 receiver.await?
232 } else {
233 log::info!("Backlight power on cancelled");
234 *power_state = PowerState::BothOff;
236 drop(power_state);
237 Ok(())
238 }
239 }
240 PowerState::Indeterminate => {
241 unreachable!()
242 }
243 }
244 }
245
246 fn make_scheduled_updates_task(&self, delay: zx::MonotonicDuration) -> fasync::Task<()> {
247 let time = fasync::MonotonicInstant::after(delay);
248 log::trace!("Setting timer for {:?}", &time);
249 let timer = fasync::Timer::new(time);
250 let self_ = self.clone();
251 let fut = async move {
252 log::trace!("Awaiting timer");
253 timer.await;
254 log::trace!("Timer {:?} elapsed", time);
255 self_.process_scheduled_updates().await;
256 };
257 fasync::Task::local(fut)
258 }
259
260 fn make_pending_change(
263 regulated_value: f64,
264 backlight_on: bool,
265 ) -> (PendingBacklightCommand, oneshot::Receiver<Result<(), Error>>) {
266 let (sender, receiver) = oneshot::channel::<Result<(), Error>>();
267 let pending_change = PendingBacklightCommand {
268 command: BacklightCommand { backlight_on, brightness: regulated_value },
269 future_handle: sender,
270 };
271 (pending_change, receiver)
272 }
273
274 async fn process_scheduled_updates(&self) {
276 let self_ = self.clone();
277 match &self.display_power {
278 Some(display_power) => {
279 let power_state_arc = display_power.power_state.clone();
280 let mut power_state_guard = power_state_arc.lock().await;
281 let power_state = std::mem::take(&mut *power_state_guard);
282
283 log::debug!(
284 "Processing scheduled updates after timer. Most recent state: {:?}",
285 &power_state
286 );
287
288 match power_state {
289 PowerState::BacklightOffDisplayPoweringDown(_) => {
290 if let Ok(_) = display_power.set_display_power_and_log_errors(false).await {
291 *power_state_guard = PowerState::BothOff;
292 } else {
293 *power_state_guard = PowerState::BothOn;
296 }
297 }
298 PowerState::DisplayOnBacklightPoweringUp(_, pending_changes) => {
299 assert!(!pending_changes.is_empty());
300 let mut turned_on = false;
301 for pending_change in pending_changes.into_iter() {
302 assert!(pending_change.command.backlight_on);
303 let result = self_
304 .set_backlight_state_normalized(
305 pending_change.command.brightness,
306 pending_change.command.backlight_on,
307 )
308 .await;
309 *power_state_guard = PowerState::BothOn;
312 log::debug!("Sending result for pending change {:?}", &pending_change);
313 if let Err(e) = pending_change.future_handle.send(result) {
314 log::warn!("Failed to send result for pending change: {:#?}", e);
315 } else if !turned_on {
316 turned_on = true;
317 log::info!("Turned backlight on");
318 }
319 }
320 }
321 PowerState::Indeterminate => {
322 unreachable!()
323 }
324 _ => {}
325 }
326 }
327 None => {
328 unreachable!()
329 }
330 }
331 }
332
333 async fn set_backlight_state_normalized(
334 &self,
335 regulated_value: f64,
336 backlight_on: bool,
337 ) -> Result<(), Error> {
338 log::debug!(
339 "set_state_normalized(brightness: {:.3}, backlight_on: {}",
340 regulated_value,
341 backlight_on
342 );
343 self.backlight_proxy
344 .set_state_normalized(&BacklightCommand { backlight_on, brightness: regulated_value })
345 .await?
346 .map_err(|e| zx::Status::from_raw(e))
347 .context("Failed to set backlight state")
348 }
349}
350
351#[derive(Debug, Clone)]
353struct DisplayPower {
354 proxy: DisplayPowerProxy,
355 power_state: Arc<Mutex<PowerState>>,
356 power_off_delay: zx::MonotonicDuration,
357 power_on_delay: zx::MonotonicDuration,
358}
359
360impl DisplayPower {
361 async fn new(
362 backlight_proxy: &BacklightProxy,
363 display_power_proxy: DisplayPowerProxy,
364 power_off_delay: impl Into<zx::MonotonicDuration>,
365 power_on_delay: impl Into<zx::MonotonicDuration>,
366 ) -> Result<Self, Error> {
367 let initial_state = if get_state_normalized(&backlight_proxy).await?.backlight_on {
370 PowerState::BothOn
371 } else {
372 PowerState::BothOff
373 };
374 log::info!("Initial power state: {:?}", &initial_state);
375
376 Ok(DisplayPower {
377 proxy: display_power_proxy,
378 power_state: Arc::new(Mutex::new(initial_state)),
379 power_off_delay: power_off_delay.into(),
380 power_on_delay: power_on_delay.into(),
381 })
382 }
383
384 async fn set_display_power_and_log_errors(&self, display_on: bool) -> Result<(), Error> {
385 let on_off = if display_on { "on" } else { "off" };
386 log::info!("Turning DDIC power {}", on_off);
387 self.proxy
388 .set_display_power(display_on)
389 .await
390 .map_err(|fidl_error| Into::<Error>::into(fidl_error))
391 .with_context(|| format!("Failed to connect to {}", DisplayPowerMarker::DEBUG_NAME))
392 .and_then(|inner| {
393 inner.map_err(|e| {
394 let status = zx::Status::from_raw(e);
395 Error::from(status)
396 })
397 })
398 .with_context(|| format!("Failed to turn {on_off} display"))
399 .map_err(|e| {
400 log::error!("{:#?}", &e);
401 e
402 })?;
403 log::info!("Turned DDIC power {}", on_off);
404 Ok(())
405 }
406}
407
408async fn get_state_normalized(backlight_proxy: &BacklightProxy) -> Result<BacklightCommand, Error> {
409 backlight_proxy
410 .get_state_normalized()
411 .await?
412 .map_err(|e| zx::Status::from_raw(e))
413 .context("Failed to get_state_normalized")
414}
415
416#[async_trait]
417pub trait BacklightControl: std::fmt::Debug + Send + Sync {
418 async fn get_brightness(&self) -> Result<f64, Error>;
419 async fn set_brightness(&self, value: f64) -> Result<(), Error>;
420 async fn get_max_absolute_brightness(&self) -> Result<f64, Error>;
421}
422
423#[async_trait]
424impl BacklightControl for Backlight {
425 async fn get_brightness(&self) -> Result<f64, Error> {
426 self.get().await
427 }
428 async fn set_brightness(&self, value: f64) -> Result<(), Error> {
429 self.clone().set(value).await
430 }
431 async fn get_max_absolute_brightness(&self) -> Result<f64, Error> {
432 self.get_max_absolute_brightness().await
433 }
434}
435
436#[cfg(test)]
437mod tests {
438 use super::*;
439 use fidl::endpoints::create_proxy_and_stream;
440 use fidl_fuchsia_hardware_backlight::{
441 DeviceMarker as BacklightMarker, DeviceRequestStream as BacklightRequestStream,
442 };
443 use fuchsia_async::{self as fasync};
444 use futures::join;
445 use futures::prelude::future;
446 use futures_util::stream::StreamExt;
447
448 fn mock_backlight() -> (Arc<Backlight>, BacklightRequestStream) {
449 let (backlight_proxy, backlight_stream) = create_proxy_and_stream::<BacklightMarker>();
450
451 (
452 Arc::new(Backlight::without_display_power_internal(backlight_proxy).unwrap()),
453 backlight_stream,
454 )
455 }
456
457 async fn mock_device_set(mut reqs: BacklightRequestStream) -> BacklightCommand {
458 match reqs.next().await.unwrap() {
459 Ok(fidl_fuchsia_hardware_backlight::DeviceRequest::SetStateNormalized {
460 state: command,
461 ..
462 }) => {
463 return command;
464 }
465 request => panic!("Unexpected request: {:?}", request),
466 }
467 }
468
469 async fn mock_device_get(
470 mut reqs: BacklightRequestStream,
471 backlight_command: BacklightCommand,
472 ) {
473 match reqs.next().await.unwrap() {
474 Ok(fidl_fuchsia_hardware_backlight::DeviceRequest::GetStateNormalized {
475 responder,
476 }) => {
477 let response = backlight_command;
478 let _ = responder.send(Ok(&response));
479 }
480 Ok(fidl_fuchsia_hardware_backlight::DeviceRequest::GetMaxAbsoluteBrightness {
481 responder,
482 }) => {
483 if let Err(e) = responder.send(Ok(250.0)) {
484 panic!("Failed to reply to GetMaxAbsoluteBrightness: {}", e);
485 }
486 }
487 request => panic!("Unexpected request: {:?}", request),
488 }
489 }
490
491 #[fasync::run_singlethreaded(test)]
492 async fn test_brightness_returns_zero_if_backlight_off() {
493 let (mock, backlight_stream) = mock_backlight();
495 let backlight_fut = mock_device_get(
496 backlight_stream,
497 BacklightCommand { backlight_on: false, brightness: 0.04 },
498 );
499
500 let get_fut = mock.get();
502 let (brightness, _) = future::join(get_fut, backlight_fut).await;
503
504 assert_eq!(brightness.unwrap(), 0.0);
506 }
507
508 #[fasync::run_singlethreaded(test)]
509 async fn test_brightness_returns_non_zero_if_backlight_on() {
510 let (mock, backlight_stream) = mock_backlight();
512 let backlight_fut = mock_device_get(
513 backlight_stream,
514 BacklightCommand { backlight_on: true, brightness: 0.04 },
515 );
516
517 let get_fut = mock.get();
519 let (brightness, _) = future::join(get_fut, backlight_fut).await;
520
521 assert_eq!(brightness.unwrap(), 0.04);
523 }
524
525 #[fasync::run_singlethreaded(test)]
526 async fn test_zero_brightness_turns_backlight_off() {
527 let (mock, backlight_stream) = mock_backlight();
529 let backlight_fut = mock_device_set(backlight_stream);
530
531 let set_fut = mock.set(0.0);
533 let (_, backlight_command) = futures::join!(set_fut, backlight_fut);
534
535 assert_eq!(backlight_command.backlight_on, false);
537 }
538
539 #[fasync::run_singlethreaded(test)]
540 async fn test_negative_brightness_turns_backlight_off() {
541 let (mock, backlight_stream) = mock_backlight();
543 let backlight_fut = mock_device_set(backlight_stream);
544
545 let set_fut = mock.set(-0.01);
547 let (_, backlight_command) = join!(set_fut, backlight_fut);
548
549 assert_eq!(backlight_command.backlight_on, false);
551 }
552
553 #[fasync::run_singlethreaded(test)]
554 async fn test_brightness_turns_backlight_on() {
555 let (mock, backlight_stream) = mock_backlight();
557 let backlight_fut = mock_device_set(backlight_stream);
558
559 let set_fut = mock.set(0.55);
561 let (_, backlight_command) = join!(set_fut, backlight_fut);
562
563 assert_eq!(backlight_command.backlight_on, true);
565 assert_eq!(backlight_command.brightness, 0.55);
566 }
567
568 #[fasync::run_singlethreaded(test)]
569 async fn test_get_max_absolute_brightness() {
570 let (mock, backlight_stream) = mock_backlight();
572 let backlight_fut = mock_device_get(
573 backlight_stream,
574 BacklightCommand { backlight_on: false, brightness: 0.04 },
575 );
576
577 let mock_fut = mock.get_max_absolute_brightness();
579 let (max_brightness, _) = future::join(mock_fut, backlight_fut).await;
580
581 assert_eq!(max_brightness.unwrap(), 250.0);
583 }
584}
585
586#[cfg(test)]
587mod dual_state_tests {
588 use super::*;
589 use assert_matches::assert_matches;
590 use fidl::endpoints::create_proxy_and_stream;
591 use fidl_fuchsia_hardware_backlight::{
592 DeviceMarker as BacklightMarker, DeviceRequestStream as BacklightRequestStream,
593 };
594 use fidl_fuchsia_ui_display_singleton::{DisplayPowerRequest, DisplayPowerRequestStream};
595 use fuchsia_async::{self as fasync, Task};
596 use futures::prelude::future;
597 use futures::{Future, TryStreamExt};
598 use std::task::Poll;
599 use test_helpers::ResettableFuture;
600
601 #[derive(Debug, Clone)]
602 struct FakeBacklightService {
603 get_state_normalized_response: ResettableFuture<Result<BacklightCommand, i32>>,
604 set_state_normalized_response: Arc<Mutex<Result<(), i32>>>,
605 }
606
607 #[allow(dead_code)]
608 impl FakeBacklightService {
609 pub fn new() -> Self {
610 Self {
611 get_state_normalized_response: ResettableFuture::new(),
612 set_state_normalized_response: Arc::new(Mutex::new(Ok(()))),
613 }
614 }
615
616 pub fn start(&self) -> Result<(BacklightProxy, Task<()>), Error> {
617 let (proxy, stream) = create_proxy_and_stream::<BacklightMarker>();
618 let task = Task::local(self.clone().process_requests(stream));
619 Ok((proxy, task))
620 }
621
622 async fn process_requests(self, mut stream: BacklightRequestStream) {
623 use fidl_fuchsia_hardware_backlight::DeviceRequest::*;
624
625 log::debug!("FakeBacklightService::process_requests");
626 while let Ok(Some(req)) = stream.try_next().await {
627 log::debug!("FakeBacklightService: {}", req.method_name());
628 match req {
629 GetStateNormalized { responder } => {
630 let result = self.get_state_normalized_response.get().await;
631 responder
632 .send(result.as_ref().map_err(|e| *e))
633 .expect("send GetStateNormalized");
634 }
635 SetStateNormalized { state, responder } => {
636 let result = self.set_state_normalized_response.lock().await.clone();
637 if result.is_ok() {
638 self.set_get_state_normalized_response(Ok(state)).await;
639 }
640 responder.send(result).expect("send SetStateNormalized");
641 }
642 _ => {
643 unimplemented!();
644 }
645 };
646 }
647 }
648
649 pub async fn set_get_state_normalized_response(
650 &self,
651 response: Result<BacklightCommand, i32>,
652 ) {
653 self.get_state_normalized_response.set(response).await;
654 }
655
656 pub async fn clear_get_state_normalized_response(&self) {
657 self.get_state_normalized_response.clear().await;
658 }
659
660 pub async fn set_set_state_normalized_response(&self, result: Result<(), i32>) {
661 let mut guard = self.set_state_normalized_response.lock().await;
662 *guard = result;
663 }
664 }
665
666 #[derive(Debug, Clone)]
667 struct FakeDisplayPowerService {
668 set_display_power_response: Arc<Mutex<Result<(), i32>>>,
669 last_set_display_power_value: Arc<Mutex<Option<bool>>>,
670 }
671
672 #[allow(dead_code)]
673 impl FakeDisplayPowerService {
674 pub fn new() -> Self {
675 Self {
676 set_display_power_response: Arc::new(Mutex::new(Ok(()))),
677 last_set_display_power_value: Arc::new(Mutex::new(None)),
678 }
679 }
680
681 pub fn start(&self) -> Result<(DisplayPowerProxy, Task<()>), Error> {
682 let (proxy, stream) = create_proxy_and_stream::<DisplayPowerMarker>();
683 let task = Task::local(self.clone().process_requests(stream));
684 Ok((proxy, task))
685 }
686
687 async fn process_requests(self, mut stream: DisplayPowerRequestStream) {
688 log::debug!("FakeDisplayPowerService::process_requests");
689 while let Ok(Some(req)) = stream.try_next().await {
690 log::debug!("FakeDisplayPowerService: {}", req.method_name());
691 match req {
692 DisplayPowerRequest::SetDisplayPower { power_on, responder } => {
693 let result = self.set_display_power_response.lock().await.clone();
694 if result.is_ok() {
695 self.last_set_display_power_value.lock().await.replace(power_on);
696 }
697 responder.send(result).expect("send SetDisplayPower");
698 }
699 DisplayPowerRequest::_UnknownMethod { ordinal, .. } => {
700 panic!("Unexpected method: {}", ordinal);
701 }
702 };
703 }
704 log::warn!("FakeDisplayPowerService stopped");
705 }
706
707 pub async fn set_set_display_power_response(&self, response: Result<(), i32>) {
708 (*self.set_display_power_response.lock().await) = response;
709 }
710
711 pub async fn last_set_display_power_value(&self) -> Option<bool> {
712 self.last_set_display_power_value.lock().await.clone()
713 }
714 }
715
716 trait PollExt<T> {
717 fn into_option(self) -> Option<T>;
718 #[allow(dead_code)]
719 fn unwrap(self) -> T;
720 }
721
722 impl<T> PollExt<T> for Poll<T> {
723 fn into_option(self) -> Option<T> {
724 match self {
725 Poll::Ready(x) => Some(x),
726 Poll::Pending => None,
727 }
728 }
729
730 fn unwrap(self) -> T {
731 self.into_option().unwrap()
732 }
733 }
734
735 trait TestExecutorExt {
736 fn pin_and_run_until_stalled<F: Future>(&mut self, main_future: F) -> Option<F::Output>;
737 fn wake_timers_and_run_until_stalled(&mut self) -> bool;
740 }
741
742 impl TestExecutorExt for fasync::TestExecutor {
743 fn pin_and_run_until_stalled<F: Future>(&mut self, main_future: F) -> Option<F::Output> {
744 self.run_until_stalled(&mut Box::pin(main_future)).into_option()
745 }
746
747 fn wake_timers_and_run_until_stalled(&mut self) -> bool {
748 let did_wake_timers = self.wake_expired_timers();
749 let _ = self.run_until_stalled(&mut future::pending::<()>());
750 did_wake_timers
751 }
752 }
753
754 #[allow(dead_code)]
755 struct Handles {
756 fake_backlight_service: FakeBacklightService,
757 backlight_proxy: BacklightProxy,
758 backlight_task: Task<()>,
759
760 fake_display_power_service: FakeDisplayPowerService,
761 display_power_proxy: DisplayPowerProxy,
762 display_power_task: Task<()>,
763
764 backlight: Backlight,
765 }
766
767 impl Handles {
768 fn new(
771 power_off_delay_ms: i64,
772 power_on_delay_ms: i64,
773 initial_backlight_state: BacklightCommand,
774 ) -> (fasync::TestExecutor, Handles) {
775 let mut exec = fasync::TestExecutor::new_with_fake_time();
776 exec.set_fake_time(zx::MonotonicInstant::ZERO.into());
777
778 let fake_backlight_service = FakeBacklightService::new();
779 let (backlight_proxy, backlight_task) = fake_backlight_service.start().unwrap();
780
781 let fake_display_power_service = FakeDisplayPowerService::new();
782 let (display_power_proxy, display_power_task) =
783 fake_display_power_service.start().unwrap();
784
785 let fake_backlight_service_ = fake_backlight_service.clone();
786
787 let backlight = exec
788 .pin_and_run_until_stalled(async {
789 fake_backlight_service_
790 .set_get_state_normalized_response(Ok(initial_backlight_state))
791 .await;
792
793 Backlight::with_display_power_internal(
794 backlight_proxy.clone(),
795 display_power_proxy.clone(),
796 zx::MonotonicDuration::from_millis(power_off_delay_ms),
797 zx::MonotonicDuration::from_millis(power_on_delay_ms),
798 )
799 .await
800 .unwrap()
801 })
802 .unwrap();
803
804 (
805 exec,
806 Handles {
807 fake_backlight_service,
808 backlight_proxy,
809 backlight_task,
810 fake_display_power_service,
811 display_power_proxy,
812 display_power_task,
813 backlight,
814 },
815 )
816 }
817 }
818
819 #[test]
820 fn positive_brightness_changes_without_affecting_ddic() {
821 let power_off_delay_ms = 100;
822 let power_on_delay_ms = 50;
823
824 let (mut exec, h) = Handles::new(
825 power_off_delay_ms,
826 power_on_delay_ms,
827 BacklightCommand { backlight_on: true, brightness: 1.0 },
828 );
829
830 exec.pin_and_run_until_stalled(async {
831 assert_eq!(h.backlight.get().await.unwrap(), 1.0);
832
833 h.backlight.set(0.9).await.unwrap();
834 assert_eq!(h.backlight.get().await.unwrap(), 0.9);
835
836 h.backlight.set(0.5).await.unwrap();
837 assert_eq!(h.backlight.get().await.unwrap(), 0.5);
838
839 assert_eq!(h.fake_display_power_service.last_set_display_power_value().await, None)
840 })
841 .unwrap();
842 }
843
844 #[test]
845 fn zero_brightness_turns_ddic_off() {
846 let power_off_delay_ms = 100;
847 let power_on_delay_ms = 50;
848
849 let (mut exec, h) = Handles::new(
850 power_off_delay_ms,
851 power_on_delay_ms,
852 BacklightCommand { backlight_on: true, brightness: 1.0 },
853 );
854
855 exec.pin_and_run_until_stalled(async {
856 assert_eq!(h.backlight.get().await.unwrap(), 1.0);
857 assert_eq!(h.fake_display_power_service.last_set_display_power_value().await, None);
858
859 h.backlight.set(0.0).await.unwrap();
860 assert_eq!(h.backlight.get().await.unwrap(), 0.0);
861 assert_eq!(h.fake_display_power_service.last_set_display_power_value().await, None);
862 })
863 .unwrap();
864
865 exec.set_fake_time(
867 (zx::MonotonicInstant::ZERO
868 + zx::MonotonicDuration::from_millis(power_off_delay_ms - 1))
869 .into(),
870 );
871 assert_eq!(exec.wake_timers_and_run_until_stalled(), false);
872
873 exec.pin_and_run_until_stalled(async {
874 assert_eq!(h.fake_display_power_service.last_set_display_power_value().await, None);
875 })
876 .unwrap();
877
878 exec.set_fake_time(
880 (zx::MonotonicInstant::ZERO
881 + zx::MonotonicDuration::from_millis(power_off_delay_ms + 1))
882 .into(),
883 );
884 assert_eq!(exec.wake_timers_and_run_until_stalled(), true);
885
886 exec.pin_and_run_until_stalled(async {
887 assert_eq!(
888 h.fake_display_power_service.last_set_display_power_value().await,
889 Some(false)
890 );
891 })
892 .unwrap();
893 }
894
895 #[test]
896 fn backlight_turns_on_after_ddic() {
897 let power_off_delay_ms = 100;
898 let power_on_delay_ms = 50;
899
900 let (mut exec, h) = Handles::new(
901 power_off_delay_ms,
902 power_on_delay_ms,
903 BacklightCommand { backlight_on: false, brightness: MIN_REGULATED_BRIGHTNESS },
904 );
905
906 exec.pin_and_run_until_stalled(async {
907 assert_eq!(h.backlight.get().await.unwrap(), 0.0);
908 assert_eq!(h.fake_display_power_service.last_set_display_power_value().await, None);
909 })
910 .unwrap();
911
912 let backlight_ = h.backlight.clone();
913
914 let mut turn_on_backlight_fut = Box::pin(async {
915 backlight_.set(0.1).await.unwrap();
916 });
917 assert_matches!(exec.run_until_stalled(&mut turn_on_backlight_fut), Poll::Pending);
918
919 exec.pin_and_run_until_stalled(async {
920 assert_eq!(h.backlight.get().await.unwrap(), 0.0);
921 assert_eq!(
922 h.fake_display_power_service.last_set_display_power_value().await,
923 Some(true)
924 );
925 })
926 .unwrap();
927
928 exec.set_fake_time(
929 (zx::MonotonicInstant::ZERO
930 + zx::MonotonicDuration::from_millis(power_on_delay_ms - 1))
931 .into(),
932 );
933 assert_eq!(exec.wake_timers_and_run_until_stalled(), false);
934 assert_matches!(exec.run_until_stalled(&mut turn_on_backlight_fut), Poll::Pending);
935 exec.pin_and_run_until_stalled(async {
936 assert_eq!(h.backlight.get().await.unwrap(), 0.0);
937 })
938 .unwrap();
939
940 exec.set_fake_time(
941 (zx::MonotonicInstant::ZERO
942 + zx::MonotonicDuration::from_millis(power_on_delay_ms + 1))
943 .into(),
944 );
945 assert_eq!(exec.wake_timers_and_run_until_stalled(), true);
946 assert_matches!(exec.run_until_stalled(&mut turn_on_backlight_fut), Poll::Ready(()));
947 exec.pin_and_run_until_stalled(async {
948 assert_eq!(h.backlight.get().await.unwrap(), 0.1);
949 })
950 .unwrap();
951 }
952
953 #[test]
954 fn repeated_backlight_off_commands_do_not_affect_ddic() {
955 let power_off_delay_ms = 100;
956 let power_on_delay_ms = 50;
957
958 let (mut exec, h) = Handles::new(
959 power_off_delay_ms,
960 power_on_delay_ms,
961 BacklightCommand { backlight_on: false, brightness: MIN_REGULATED_BRIGHTNESS },
962 );
963
964 exec.pin_and_run_until_stalled(async {
965 assert_eq!(h.backlight.get().await.unwrap(), 0.0);
966 assert_eq!(h.fake_display_power_service.last_set_display_power_value().await, None);
967
968 h.backlight.set(0.0).await.unwrap();
969 assert_eq!(h.backlight.get().await.unwrap(), 0.0);
970 assert_eq!(
971 h.fake_display_power_service.last_set_display_power_value().await,
972 Some(false)
973 );
974 })
975 .unwrap();
976 }
977
978 #[test]
979 fn ddic_power_off_is_preempted_by_backlight_on_commands() {
980 let power_off_delay_ms = 100;
981 let power_on_delay_ms = 50;
982
983 let (mut exec, h) = Handles::new(
984 power_off_delay_ms,
985 power_on_delay_ms,
986 BacklightCommand { backlight_on: true, brightness: 1.0 },
987 );
988
989 exec.pin_and_run_until_stalled(async {
990 h.backlight.set(0.0).await.unwrap();
991 assert_eq!(h.backlight.get().await.unwrap(), 0.0);
992 assert_eq!(h.fake_display_power_service.last_set_display_power_value().await, None);
993 })
994 .unwrap();
995
996 exec.set_fake_time(
998 (zx::MonotonicInstant::ZERO
999 + zx::MonotonicDuration::from_millis(power_off_delay_ms - 10))
1000 .into(),
1001 );
1002 assert_eq!(exec.wake_timers_and_run_until_stalled(), false);
1003
1004 exec.pin_and_run_until_stalled(async {
1005 assert_eq!(h.fake_display_power_service.last_set_display_power_value().await, None);
1006 })
1007 .unwrap();
1008
1009 exec.pin_and_run_until_stalled(async {
1010 h.backlight.set(0.5).await.unwrap();
1011 assert_eq!(h.backlight.get().await.unwrap(), 0.5);
1012 assert_eq!(h.fake_display_power_service.last_set_display_power_value().await, None);
1013 })
1014 .unwrap();
1015
1016 exec.set_fake_time(
1018 (zx::MonotonicInstant::ZERO
1019 + zx::MonotonicDuration::from_millis(power_off_delay_ms + 10))
1020 .into(),
1021 );
1022 assert_eq!(exec.wake_timers_and_run_until_stalled(), false);
1024
1025 exec.pin_and_run_until_stalled(async {
1026 assert_eq!(h.fake_display_power_service.last_set_display_power_value().await, None);
1027 })
1028 .unwrap();
1029 }
1030
1031 #[test]
1032 fn backlight_power_on_is_preempted_by_ddic_off_commands() {
1033 let power_off_delay_ms = 100;
1034 let power_on_delay_ms = 50;
1035
1036 let (mut exec, h) = Handles::new(
1037 power_off_delay_ms,
1038 power_on_delay_ms,
1039 BacklightCommand { backlight_on: false, brightness: MIN_REGULATED_BRIGHTNESS },
1040 );
1041
1042 let mut turn_on_backlight_1_fut = Box::pin(h.backlight.set(0.1));
1043 let mut turn_on_backlight_2_fut = Box::pin(h.backlight.set(0.2));
1044 assert_matches!(exec.run_until_stalled(&mut turn_on_backlight_1_fut), Poll::Pending);
1045 assert_matches!(exec.run_until_stalled(&mut turn_on_backlight_2_fut), Poll::Pending);
1046
1047 exec.set_fake_time(
1048 (zx::MonotonicInstant::ZERO
1049 + zx::MonotonicDuration::from_millis(power_on_delay_ms - 1))
1050 .into(),
1051 );
1052 assert_eq!(exec.wake_timers_and_run_until_stalled(), false);
1053 assert_matches!(exec.run_until_stalled(&mut turn_on_backlight_1_fut), Poll::Pending);
1054 assert_matches!(exec.run_until_stalled(&mut turn_on_backlight_2_fut), Poll::Pending);
1055
1056 let turn_off_backlight_fut = Box::pin(h.backlight.set(0.0));
1057 exec.pin_and_run_until_stalled(async {
1058 assert_matches!(turn_off_backlight_fut.await, Ok(()));
1059 assert_matches!(
1061 turn_on_backlight_1_fut.await.unwrap_err().downcast::<oneshot::Canceled>(),
1062 Ok(_)
1063 );
1064 assert_matches!(
1065 turn_on_backlight_2_fut.await.unwrap_err().downcast::<oneshot::Canceled>(),
1066 Ok(_)
1067 );
1068
1069 assert_eq!(h.backlight.get().await.unwrap(), 0.0);
1070 })
1071 .unwrap();
1072
1073 exec.set_fake_time(
1074 (zx::MonotonicInstant::ZERO
1075 + zx::MonotonicDuration::from_millis(power_on_delay_ms + 1))
1076 .into(),
1077 );
1078 assert_eq!(exec.wake_timers_and_run_until_stalled(), false);
1079
1080 exec.pin_and_run_until_stalled(async {
1081 assert_eq!(h.backlight.get().await.unwrap(), 0.0);
1082 })
1083 .unwrap();
1084 }
1085
1086 #[test]
1087 fn error_in_ddic_power_off_does_not_affect_later_backlight_commands() {
1088 let power_off_delay_ms = 100;
1089 let power_on_delay_ms = 50;
1090
1091 let (mut exec, h) = Handles::new(
1092 power_off_delay_ms,
1093 power_on_delay_ms,
1094 BacklightCommand { backlight_on: true, brightness: 1.0 },
1095 );
1096
1097 exec.pin_and_run_until_stalled(async {
1098 h.fake_display_power_service
1099 .set_set_display_power_response(Err(zx::Status::BAD_STATE.into_raw()))
1100 .await;
1101
1102 assert_eq!(h.backlight.get().await.unwrap(), 1.0);
1103 assert_eq!(h.fake_display_power_service.last_set_display_power_value().await, None);
1104
1105 h.backlight.set(0.0).await.unwrap();
1106 assert_eq!(h.backlight.get().await.unwrap(), 0.0);
1107 assert_eq!(h.fake_display_power_service.last_set_display_power_value().await, None);
1108 })
1109 .unwrap();
1110
1111 exec.set_fake_time(
1112 (zx::MonotonicInstant::ZERO
1113 + zx::MonotonicDuration::from_millis(power_off_delay_ms + 1))
1114 .into(),
1115 );
1116 assert_eq!(exec.wake_timers_and_run_until_stalled(), true);
1117
1118 exec.pin_and_run_until_stalled(async {
1119 assert_eq!(h.fake_display_power_service.last_set_display_power_value().await, None);
1120
1121 h.backlight.set(0.5).await.unwrap();
1122 assert_eq!(h.backlight.get().await.unwrap(), 0.5);
1123 assert_eq!(h.fake_display_power_service.last_set_display_power_value().await, None);
1124 })
1125 .unwrap();
1126 }
1127
1128 #[test]
1129 fn error_in_ddic_power_on_is_recoverable() {
1130 let power_off_delay_ms = 100;
1131 let power_on_delay_ms = 50;
1132
1133 let (mut exec, h) = Handles::new(
1134 power_off_delay_ms,
1135 power_on_delay_ms,
1136 BacklightCommand { backlight_on: false, brightness: MIN_REGULATED_BRIGHTNESS },
1137 );
1138
1139 exec.pin_and_run_until_stalled(async {
1140 h.fake_display_power_service
1141 .set_set_display_power_response(Err(zx::Status::UNAVAILABLE.into_raw()))
1142 .await;
1143
1144 assert_matches!(h.backlight.set(0.5).await, Err(_));
1145 assert_eq!(h.fake_display_power_service.last_set_display_power_value().await, None);
1146
1147 h.fake_display_power_service.set_set_display_power_response(Ok(())).await;
1148 })
1149 .unwrap();
1150
1151 let mut retry_fut = Box::pin(h.backlight.set(0.7));
1152 assert_matches!(exec.run_until_stalled(&mut retry_fut), Poll::Pending);
1153
1154 exec.set_fake_time(
1155 (zx::MonotonicInstant::ZERO
1156 + zx::MonotonicDuration::from_millis(power_on_delay_ms + 1))
1157 .into(),
1158 );
1159 assert_eq!(exec.wake_timers_and_run_until_stalled(), true);
1160
1161 assert_matches!(exec.run_until_stalled(&mut retry_fut), Poll::Ready(Ok(())));
1162 }
1163
1164 #[test]
1165 fn ddic_does_not_power_off_if_backlight_fails_to_power_off() {
1166 let power_off_delay_ms = 100;
1167 let power_on_delay_ms = 50;
1168
1169 let (mut exec, h) = Handles::new(
1170 power_off_delay_ms,
1171 power_on_delay_ms,
1172 BacklightCommand { backlight_on: true, brightness: 0.5 },
1173 );
1174
1175 exec.pin_and_run_until_stalled(async {
1176 h.fake_backlight_service
1177 .set_set_state_normalized_response(Err(zx::Status::NO_RESOURCES.into_raw()))
1178 .await;
1179 assert_matches!(h.backlight.set(0.0).await, Err(_));
1180 })
1181 .unwrap();
1182
1183 exec.set_fake_time(
1184 (zx::MonotonicInstant::ZERO
1185 + zx::MonotonicDuration::from_millis(power_off_delay_ms + 1))
1186 .into(),
1187 );
1188 assert_eq!(exec.wake_timers_and_run_until_stalled(), false);
1189
1190 exec.pin_and_run_until_stalled(async {
1191 assert_eq!(h.fake_display_power_service.last_set_display_power_value().await, None);
1192 })
1193 .unwrap();
1194 }
1195}