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, PowerMode};
13use fuchsia_async as fasync;
14use fuchsia_component::client::{Service, connect_to_protocol};
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, 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_power_mode(if display_on { PowerMode::On } else { PowerMode::Off })
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 futures::join;
444 use futures::prelude::future;
445 use futures_util::stream::StreamExt;
446
447 fn mock_backlight() -> (Arc<Backlight>, BacklightRequestStream) {
448 let (backlight_proxy, backlight_stream) = create_proxy_and_stream::<BacklightMarker>();
449
450 (
451 Arc::new(Backlight::without_display_power_internal(backlight_proxy).unwrap()),
452 backlight_stream,
453 )
454 }
455
456 async fn mock_device_set(mut reqs: BacklightRequestStream) -> BacklightCommand {
457 match reqs.next().await.unwrap() {
458 Ok(fidl_fuchsia_hardware_backlight::DeviceRequest::SetStateNormalized {
459 state: command,
460 ..
461 }) => {
462 return command;
463 }
464 request => panic!("Unexpected request: {:?}", request),
465 }
466 }
467
468 async fn mock_device_get(
469 mut reqs: BacklightRequestStream,
470 backlight_command: BacklightCommand,
471 ) {
472 match reqs.next().await.unwrap() {
473 Ok(fidl_fuchsia_hardware_backlight::DeviceRequest::GetStateNormalized {
474 responder,
475 }) => {
476 let response = backlight_command;
477 let _ = responder.send(Ok(&response));
478 }
479 Ok(fidl_fuchsia_hardware_backlight::DeviceRequest::GetMaxAbsoluteBrightness {
480 responder,
481 }) => {
482 if let Err(e) = responder.send(Ok(250.0)) {
483 panic!("Failed to reply to GetMaxAbsoluteBrightness: {}", e);
484 }
485 }
486 request => panic!("Unexpected request: {:?}", request),
487 }
488 }
489
490 #[fuchsia::test]
491 async fn test_brightness_returns_zero_if_backlight_off() {
492 let (mock, backlight_stream) = mock_backlight();
494 let backlight_fut = mock_device_get(
495 backlight_stream,
496 BacklightCommand { backlight_on: false, brightness: 0.04 },
497 );
498
499 let get_fut = mock.get();
501 let (brightness, _) = future::join(get_fut, backlight_fut).await;
502
503 assert_eq!(brightness.unwrap(), 0.0);
505 }
506
507 #[fuchsia::test]
508 async fn test_brightness_returns_non_zero_if_backlight_on() {
509 let (mock, backlight_stream) = mock_backlight();
511 let backlight_fut = mock_device_get(
512 backlight_stream,
513 BacklightCommand { backlight_on: true, brightness: 0.04 },
514 );
515
516 let get_fut = mock.get();
518 let (brightness, _) = future::join(get_fut, backlight_fut).await;
519
520 assert_eq!(brightness.unwrap(), 0.04);
522 }
523
524 #[fuchsia::test]
525 async fn test_zero_brightness_turns_backlight_off() {
526 let (mock, backlight_stream) = mock_backlight();
528 let backlight_fut = mock_device_set(backlight_stream);
529
530 let set_fut = mock.set(0.0);
532 let (_, backlight_command) = futures::join!(set_fut, backlight_fut);
533
534 assert_eq!(backlight_command.backlight_on, false);
536 }
537
538 #[fuchsia::test]
539 async fn test_negative_brightness_turns_backlight_off() {
540 let (mock, backlight_stream) = mock_backlight();
542 let backlight_fut = mock_device_set(backlight_stream);
543
544 let set_fut = mock.set(-0.01);
546 let (_, backlight_command) = join!(set_fut, backlight_fut);
547
548 assert_eq!(backlight_command.backlight_on, false);
550 }
551
552 #[fuchsia::test]
553 async fn test_brightness_turns_backlight_on() {
554 let (mock, backlight_stream) = mock_backlight();
556 let backlight_fut = mock_device_set(backlight_stream);
557
558 let set_fut = mock.set(0.55);
560 let (_, backlight_command) = join!(set_fut, backlight_fut);
561
562 assert_eq!(backlight_command.backlight_on, true);
564 assert_eq!(backlight_command.brightness, 0.55);
565 }
566
567 #[fuchsia::test]
568 async fn test_get_max_absolute_brightness() {
569 let (mock, backlight_stream) = mock_backlight();
571 let backlight_fut = mock_device_get(
572 backlight_stream,
573 BacklightCommand { backlight_on: false, brightness: 0.04 },
574 );
575
576 let mock_fut = mock.get_max_absolute_brightness();
578 let (max_brightness, _) = future::join(mock_fut, backlight_fut).await;
579
580 assert_eq!(max_brightness.unwrap(), 250.0);
582 }
583}
584
585#[cfg(test)]
586mod dual_state_tests {
587 use super::*;
588 use assert_matches::assert_matches;
589 use fidl::endpoints::create_proxy_and_stream;
590 use fidl_fuchsia_hardware_backlight::{
591 DeviceMarker as BacklightMarker, DeviceRequestStream as BacklightRequestStream,
592 };
593 use fidl_fuchsia_ui_display_singleton::{
594 DisplayPowerRequest, DisplayPowerRequestStream, PowerMode,
595 };
596 use fuchsia_async::{self as fasync, Task};
597 use futures::prelude::future;
598 use futures::{Future, TryStreamExt};
599 use std::task::Poll;
600 use test_helpers::ResettableFuture;
601
602 #[derive(Debug, Clone)]
603 struct FakeBacklightService {
604 get_state_normalized_response: ResettableFuture<Result<BacklightCommand, i32>>,
605 set_state_normalized_response: Arc<Mutex<Result<(), i32>>>,
606 }
607
608 #[allow(dead_code)]
609 impl FakeBacklightService {
610 pub fn new() -> Self {
611 Self {
612 get_state_normalized_response: ResettableFuture::new(),
613 set_state_normalized_response: Arc::new(Mutex::new(Ok(()))),
614 }
615 }
616
617 pub fn start(&self) -> Result<(BacklightProxy, Task<()>), Error> {
618 let (proxy, stream) = create_proxy_and_stream::<BacklightMarker>();
619 let task = Task::local(self.clone().process_requests(stream));
620 Ok((proxy, task))
621 }
622
623 async fn process_requests(self, mut stream: BacklightRequestStream) {
624 use fidl_fuchsia_hardware_backlight::DeviceRequest::*;
625
626 log::debug!("FakeBacklightService::process_requests");
627 while let Ok(Some(req)) = stream.try_next().await {
628 log::debug!("FakeBacklightService: {}", req.method_name());
629 match req {
630 GetStateNormalized { responder } => {
631 let result = self.get_state_normalized_response.get().await;
632 responder
633 .send(result.as_ref().map_err(|e| *e))
634 .expect("send GetStateNormalized");
635 }
636 SetStateNormalized { state, responder } => {
637 let result = self.set_state_normalized_response.lock().await.clone();
638 if result.is_ok() {
639 self.set_get_state_normalized_response(Ok(state)).await;
640 }
641 responder.send(result).expect("send SetStateNormalized");
642 }
643 _ => {
644 unimplemented!();
645 }
646 };
647 }
648 }
649
650 pub async fn set_get_state_normalized_response(
651 &self,
652 response: Result<BacklightCommand, i32>,
653 ) {
654 self.get_state_normalized_response.set(response).await;
655 }
656
657 pub async fn clear_get_state_normalized_response(&self) {
658 self.get_state_normalized_response.clear().await;
659 }
660
661 pub async fn set_set_state_normalized_response(&self, result: Result<(), i32>) {
662 let mut guard = self.set_state_normalized_response.lock().await;
663 *guard = result;
664 }
665 }
666
667 #[derive(Debug, Clone)]
668 struct FakeDisplayPowerService {
669 set_power_mode_response: Arc<Mutex<Result<(), i32>>>,
670 last_set_power_mode_value: Arc<Mutex<Option<PowerMode>>>,
671 }
672
673 #[allow(dead_code)]
674 impl FakeDisplayPowerService {
675 pub fn new() -> Self {
676 Self {
677 set_power_mode_response: Arc::new(Mutex::new(Ok(()))),
678 last_set_power_mode_value: Arc::new(Mutex::new(None)),
679 }
680 }
681
682 pub fn start(&self) -> Result<(DisplayPowerProxy, Task<()>), Error> {
683 let (proxy, stream) = create_proxy_and_stream::<DisplayPowerMarker>();
684 let task = Task::local(self.clone().process_requests(stream));
685 Ok((proxy, task))
686 }
687
688 async fn process_requests(self, mut stream: DisplayPowerRequestStream) {
689 log::debug!("FakeDisplayPowerService::process_requests");
690 while let Ok(Some(req)) = stream.try_next().await {
691 log::debug!("FakeDisplayPowerService: {}", req.method_name());
692 match req {
693 DisplayPowerRequest::SetPowerMode { power_mode, responder } => {
694 let result = self.set_power_mode_response.lock().await.clone();
695 if result.is_ok() {
696 self.last_set_power_mode_value.lock().await.replace(power_mode);
697 }
698 responder.send(result).expect("send SetPowerMode");
699 }
700 DisplayPowerRequest::_UnknownMethod { ordinal, .. } => {
701 panic!("Unexpected method: {}", ordinal);
702 }
703 };
704 }
705 log::warn!("FakeDisplayPowerService stopped");
706 }
707
708 pub async fn set_set_power_mode_response(&self, response: Result<(), i32>) {
709 (*self.set_power_mode_response.lock().await) = response;
710 }
711
712 pub async fn last_set_power_mode_value(&self) -> Option<PowerMode> {
713 self.last_set_power_mode_value.lock().await.clone()
714 }
715 }
716
717 trait PollExt<T> {
718 fn into_option(self) -> Option<T>;
719 #[allow(dead_code)]
720 fn unwrap(self) -> T;
721 }
722
723 impl<T> PollExt<T> for Poll<T> {
724 fn into_option(self) -> Option<T> {
725 match self {
726 Poll::Ready(x) => Some(x),
727 Poll::Pending => None,
728 }
729 }
730
731 fn unwrap(self) -> T {
732 self.into_option().unwrap()
733 }
734 }
735
736 trait TestExecutorExt {
737 fn pin_and_run_until_stalled<F: Future>(&mut self, main_future: F) -> Option<F::Output>;
738 fn wake_timers_and_run_until_stalled(&mut self) -> bool;
741 }
742
743 impl TestExecutorExt for fasync::TestExecutor {
744 fn pin_and_run_until_stalled<F: Future>(&mut self, main_future: F) -> Option<F::Output> {
745 self.run_until_stalled(&mut Box::pin(main_future)).into_option()
746 }
747
748 fn wake_timers_and_run_until_stalled(&mut self) -> bool {
749 let did_wake_timers = self.wake_expired_timers();
750 let _ = self.run_until_stalled(&mut future::pending::<()>());
751 did_wake_timers
752 }
753 }
754
755 #[allow(dead_code)]
756 struct Handles {
757 fake_backlight_service: FakeBacklightService,
758 backlight_proxy: BacklightProxy,
759 backlight_task: Task<()>,
760
761 fake_display_power_service: FakeDisplayPowerService,
762 display_power_proxy: DisplayPowerProxy,
763 display_power_task: Task<()>,
764
765 backlight: Backlight,
766 }
767
768 impl Handles {
769 fn new(
772 power_off_delay_ms: i64,
773 power_on_delay_ms: i64,
774 initial_backlight_state: BacklightCommand,
775 ) -> (fasync::TestExecutor, Handles) {
776 let mut exec = fasync::TestExecutor::new_with_fake_time();
777 exec.set_fake_time(zx::MonotonicInstant::ZERO.into());
778
779 let fake_backlight_service = FakeBacklightService::new();
780 let (backlight_proxy, backlight_task) = fake_backlight_service.start().unwrap();
781
782 let fake_display_power_service = FakeDisplayPowerService::new();
783 let (display_power_proxy, display_power_task) =
784 fake_display_power_service.start().unwrap();
785
786 let fake_backlight_service_ = fake_backlight_service.clone();
787
788 let backlight = exec
789 .pin_and_run_until_stalled(async {
790 fake_backlight_service_
791 .set_get_state_normalized_response(Ok(initial_backlight_state))
792 .await;
793
794 Backlight::with_display_power_internal(
795 backlight_proxy.clone(),
796 display_power_proxy.clone(),
797 zx::MonotonicDuration::from_millis(power_off_delay_ms),
798 zx::MonotonicDuration::from_millis(power_on_delay_ms),
799 )
800 .await
801 .unwrap()
802 })
803 .unwrap();
804
805 (
806 exec,
807 Handles {
808 fake_backlight_service,
809 backlight_proxy,
810 backlight_task,
811 fake_display_power_service,
812 display_power_proxy,
813 display_power_task,
814 backlight,
815 },
816 )
817 }
818 }
819
820 #[test]
821 fn positive_brightness_changes_without_affecting_ddic() {
822 let power_off_delay_ms = 100;
823 let power_on_delay_ms = 50;
824
825 let (mut exec, h) = Handles::new(
826 power_off_delay_ms,
827 power_on_delay_ms,
828 BacklightCommand { backlight_on: true, brightness: 1.0 },
829 );
830
831 exec.pin_and_run_until_stalled(async {
832 assert_eq!(h.backlight.get().await.unwrap(), 1.0);
833
834 h.backlight.set(0.9).await.unwrap();
835 assert_eq!(h.backlight.get().await.unwrap(), 0.9);
836
837 h.backlight.set(0.5).await.unwrap();
838 assert_eq!(h.backlight.get().await.unwrap(), 0.5);
839
840 assert_eq!(h.fake_display_power_service.last_set_power_mode_value().await, None)
841 })
842 .unwrap();
843 }
844
845 #[test]
846 fn zero_brightness_turns_ddic_off() {
847 let power_off_delay_ms = 100;
848 let power_on_delay_ms = 50;
849
850 let (mut exec, h) = Handles::new(
851 power_off_delay_ms,
852 power_on_delay_ms,
853 BacklightCommand { backlight_on: true, brightness: 1.0 },
854 );
855
856 exec.pin_and_run_until_stalled(async {
857 assert_eq!(h.backlight.get().await.unwrap(), 1.0);
858 assert_eq!(h.fake_display_power_service.last_set_power_mode_value().await, None);
859
860 h.backlight.set(0.0).await.unwrap();
861 assert_eq!(h.backlight.get().await.unwrap(), 0.0);
862 assert_eq!(h.fake_display_power_service.last_set_power_mode_value().await, None);
863 })
864 .unwrap();
865
866 exec.set_fake_time(
868 (zx::MonotonicInstant::ZERO
869 + zx::MonotonicDuration::from_millis(power_off_delay_ms - 1))
870 .into(),
871 );
872 assert_eq!(exec.wake_timers_and_run_until_stalled(), false);
873
874 exec.pin_and_run_until_stalled(async {
875 assert_eq!(h.fake_display_power_service.last_set_power_mode_value().await, None);
876 })
877 .unwrap();
878
879 exec.set_fake_time(
881 (zx::MonotonicInstant::ZERO
882 + zx::MonotonicDuration::from_millis(power_off_delay_ms + 1))
883 .into(),
884 );
885 assert_eq!(exec.wake_timers_and_run_until_stalled(), true);
886
887 exec.pin_and_run_until_stalled(async {
888 assert_eq!(
889 h.fake_display_power_service.last_set_power_mode_value().await,
890 Some(PowerMode::Off)
891 );
892 })
893 .unwrap();
894 }
895
896 #[test]
897 fn backlight_turns_on_after_ddic() {
898 let power_off_delay_ms = 100;
899 let power_on_delay_ms = 50;
900
901 let (mut exec, h) = Handles::new(
902 power_off_delay_ms,
903 power_on_delay_ms,
904 BacklightCommand { backlight_on: false, brightness: MIN_REGULATED_BRIGHTNESS },
905 );
906
907 exec.pin_and_run_until_stalled(async {
908 assert_eq!(h.backlight.get().await.unwrap(), 0.0);
909 assert_eq!(h.fake_display_power_service.last_set_power_mode_value().await, None);
910 })
911 .unwrap();
912
913 let backlight_ = h.backlight.clone();
914
915 let mut turn_on_backlight_fut = Box::pin(async {
916 backlight_.set(0.1).await.unwrap();
917 });
918 assert_matches!(exec.run_until_stalled(&mut turn_on_backlight_fut), Poll::Pending);
919
920 exec.pin_and_run_until_stalled(async {
921 assert_eq!(h.backlight.get().await.unwrap(), 0.0);
922 assert_eq!(
923 h.fake_display_power_service.last_set_power_mode_value().await,
924 Some(PowerMode::On)
925 );
926 })
927 .unwrap();
928
929 exec.set_fake_time(
930 (zx::MonotonicInstant::ZERO
931 + zx::MonotonicDuration::from_millis(power_on_delay_ms - 1))
932 .into(),
933 );
934 assert_eq!(exec.wake_timers_and_run_until_stalled(), false);
935 assert_matches!(exec.run_until_stalled(&mut turn_on_backlight_fut), Poll::Pending);
936 exec.pin_and_run_until_stalled(async {
937 assert_eq!(h.backlight.get().await.unwrap(), 0.0);
938 })
939 .unwrap();
940
941 exec.set_fake_time(
942 (zx::MonotonicInstant::ZERO
943 + zx::MonotonicDuration::from_millis(power_on_delay_ms + 1))
944 .into(),
945 );
946 assert_eq!(exec.wake_timers_and_run_until_stalled(), true);
947 assert_matches!(exec.run_until_stalled(&mut turn_on_backlight_fut), Poll::Ready(()));
948 exec.pin_and_run_until_stalled(async {
949 assert_eq!(h.backlight.get().await.unwrap(), 0.1);
950 })
951 .unwrap();
952 }
953
954 #[test]
955 fn repeated_backlight_off_commands_do_not_affect_ddic() {
956 let power_off_delay_ms = 100;
957 let power_on_delay_ms = 50;
958
959 let (mut exec, h) = Handles::new(
960 power_off_delay_ms,
961 power_on_delay_ms,
962 BacklightCommand { backlight_on: false, brightness: MIN_REGULATED_BRIGHTNESS },
963 );
964
965 exec.pin_and_run_until_stalled(async {
966 assert_eq!(h.backlight.get().await.unwrap(), 0.0);
967 assert_eq!(h.fake_display_power_service.last_set_power_mode_value().await, None);
968
969 h.backlight.set(0.0).await.unwrap();
970 assert_eq!(h.backlight.get().await.unwrap(), 0.0);
971 assert_eq!(
972 h.fake_display_power_service.last_set_power_mode_value().await,
973 Some(PowerMode::Off)
974 );
975 })
976 .unwrap();
977 }
978
979 #[test]
980 fn ddic_power_off_is_preempted_by_backlight_on_commands() {
981 let power_off_delay_ms = 100;
982 let power_on_delay_ms = 50;
983
984 let (mut exec, h) = Handles::new(
985 power_off_delay_ms,
986 power_on_delay_ms,
987 BacklightCommand { backlight_on: true, brightness: 1.0 },
988 );
989
990 exec.pin_and_run_until_stalled(async {
991 h.backlight.set(0.0).await.unwrap();
992 assert_eq!(h.backlight.get().await.unwrap(), 0.0);
993 assert_eq!(h.fake_display_power_service.last_set_power_mode_value().await, None);
994 })
995 .unwrap();
996
997 exec.set_fake_time(
999 (zx::MonotonicInstant::ZERO
1000 + zx::MonotonicDuration::from_millis(power_off_delay_ms - 10))
1001 .into(),
1002 );
1003 assert_eq!(exec.wake_timers_and_run_until_stalled(), false);
1004
1005 exec.pin_and_run_until_stalled(async {
1006 assert_eq!(h.fake_display_power_service.last_set_power_mode_value().await, None);
1007 })
1008 .unwrap();
1009
1010 exec.pin_and_run_until_stalled(async {
1011 h.backlight.set(0.5).await.unwrap();
1012 assert_eq!(h.backlight.get().await.unwrap(), 0.5);
1013 assert_eq!(h.fake_display_power_service.last_set_power_mode_value().await, None);
1014 })
1015 .unwrap();
1016
1017 exec.set_fake_time(
1019 (zx::MonotonicInstant::ZERO
1020 + zx::MonotonicDuration::from_millis(power_off_delay_ms + 10))
1021 .into(),
1022 );
1023 assert_eq!(exec.wake_timers_and_run_until_stalled(), false);
1025
1026 exec.pin_and_run_until_stalled(async {
1027 assert_eq!(h.fake_display_power_service.last_set_power_mode_value().await, None);
1028 })
1029 .unwrap();
1030 }
1031
1032 #[test]
1033 fn backlight_power_on_is_preempted_by_ddic_off_commands() {
1034 let power_off_delay_ms = 100;
1035 let power_on_delay_ms = 50;
1036
1037 let (mut exec, h) = Handles::new(
1038 power_off_delay_ms,
1039 power_on_delay_ms,
1040 BacklightCommand { backlight_on: false, brightness: MIN_REGULATED_BRIGHTNESS },
1041 );
1042
1043 let mut turn_on_backlight_1_fut = Box::pin(h.backlight.set(0.1));
1044 let mut turn_on_backlight_2_fut = Box::pin(h.backlight.set(0.2));
1045 assert_matches!(exec.run_until_stalled(&mut turn_on_backlight_1_fut), Poll::Pending);
1046 assert_matches!(exec.run_until_stalled(&mut turn_on_backlight_2_fut), Poll::Pending);
1047
1048 exec.set_fake_time(
1049 (zx::MonotonicInstant::ZERO
1050 + zx::MonotonicDuration::from_millis(power_on_delay_ms - 1))
1051 .into(),
1052 );
1053 assert_eq!(exec.wake_timers_and_run_until_stalled(), false);
1054 assert_matches!(exec.run_until_stalled(&mut turn_on_backlight_1_fut), Poll::Pending);
1055 assert_matches!(exec.run_until_stalled(&mut turn_on_backlight_2_fut), Poll::Pending);
1056
1057 let turn_off_backlight_fut = Box::pin(h.backlight.set(0.0));
1058 exec.pin_and_run_until_stalled(async {
1059 assert_matches!(turn_off_backlight_fut.await, Ok(()));
1060 assert_matches!(
1062 turn_on_backlight_1_fut.await.unwrap_err().downcast::<oneshot::Canceled>(),
1063 Ok(_)
1064 );
1065 assert_matches!(
1066 turn_on_backlight_2_fut.await.unwrap_err().downcast::<oneshot::Canceled>(),
1067 Ok(_)
1068 );
1069
1070 assert_eq!(h.backlight.get().await.unwrap(), 0.0);
1071 })
1072 .unwrap();
1073
1074 exec.set_fake_time(
1075 (zx::MonotonicInstant::ZERO
1076 + zx::MonotonicDuration::from_millis(power_on_delay_ms + 1))
1077 .into(),
1078 );
1079 assert_eq!(exec.wake_timers_and_run_until_stalled(), false);
1080
1081 exec.pin_and_run_until_stalled(async {
1082 assert_eq!(h.backlight.get().await.unwrap(), 0.0);
1083 })
1084 .unwrap();
1085 }
1086
1087 #[test]
1088 fn error_in_ddic_power_off_does_not_affect_later_backlight_commands() {
1089 let power_off_delay_ms = 100;
1090 let power_on_delay_ms = 50;
1091
1092 let (mut exec, h) = Handles::new(
1093 power_off_delay_ms,
1094 power_on_delay_ms,
1095 BacklightCommand { backlight_on: true, brightness: 1.0 },
1096 );
1097
1098 exec.pin_and_run_until_stalled(async {
1099 h.fake_display_power_service
1100 .set_set_power_mode_response(Err(zx::Status::BAD_STATE.into_raw()))
1101 .await;
1102
1103 assert_eq!(h.backlight.get().await.unwrap(), 1.0);
1104 assert_eq!(h.fake_display_power_service.last_set_power_mode_value().await, None);
1105
1106 h.backlight.set(0.0).await.unwrap();
1107 assert_eq!(h.backlight.get().await.unwrap(), 0.0);
1108 assert_eq!(h.fake_display_power_service.last_set_power_mode_value().await, None);
1109 })
1110 .unwrap();
1111
1112 exec.set_fake_time(
1113 (zx::MonotonicInstant::ZERO
1114 + zx::MonotonicDuration::from_millis(power_off_delay_ms + 1))
1115 .into(),
1116 );
1117 assert_eq!(exec.wake_timers_and_run_until_stalled(), true);
1118
1119 exec.pin_and_run_until_stalled(async {
1120 assert_eq!(h.fake_display_power_service.last_set_power_mode_value().await, None);
1121
1122 h.backlight.set(0.5).await.unwrap();
1123 assert_eq!(h.backlight.get().await.unwrap(), 0.5);
1124 assert_eq!(h.fake_display_power_service.last_set_power_mode_value().await, None);
1125 })
1126 .unwrap();
1127 }
1128
1129 #[test]
1130 fn error_in_ddic_power_on_is_recoverable() {
1131 let power_off_delay_ms = 100;
1132 let power_on_delay_ms = 50;
1133
1134 let (mut exec, h) = Handles::new(
1135 power_off_delay_ms,
1136 power_on_delay_ms,
1137 BacklightCommand { backlight_on: false, brightness: MIN_REGULATED_BRIGHTNESS },
1138 );
1139
1140 exec.pin_and_run_until_stalled(async {
1141 h.fake_display_power_service
1142 .set_set_power_mode_response(Err(zx::Status::UNAVAILABLE.into_raw()))
1143 .await;
1144
1145 assert_matches!(h.backlight.set(0.5).await, Err(_));
1146 assert_eq!(h.fake_display_power_service.last_set_power_mode_value().await, None);
1147
1148 h.fake_display_power_service.set_set_power_mode_response(Ok(())).await;
1149 })
1150 .unwrap();
1151
1152 let mut retry_fut = Box::pin(h.backlight.set(0.7));
1153 assert_matches!(exec.run_until_stalled(&mut retry_fut), Poll::Pending);
1154
1155 exec.set_fake_time(
1156 (zx::MonotonicInstant::ZERO
1157 + zx::MonotonicDuration::from_millis(power_on_delay_ms + 1))
1158 .into(),
1159 );
1160 assert_eq!(exec.wake_timers_and_run_until_stalled(), true);
1161
1162 assert_matches!(exec.run_until_stalled(&mut retry_fut), Poll::Ready(Ok(())));
1163 }
1164
1165 #[test]
1166 fn ddic_does_not_power_off_if_backlight_fails_to_power_off() {
1167 let power_off_delay_ms = 100;
1168 let power_on_delay_ms = 50;
1169
1170 let (mut exec, h) = Handles::new(
1171 power_off_delay_ms,
1172 power_on_delay_ms,
1173 BacklightCommand { backlight_on: true, brightness: 0.5 },
1174 );
1175
1176 exec.pin_and_run_until_stalled(async {
1177 h.fake_backlight_service
1178 .set_set_state_normalized_response(Err(zx::Status::NO_RESOURCES.into_raw()))
1179 .await;
1180 assert_matches!(h.backlight.set(0.0).await, Err(_));
1181 })
1182 .unwrap();
1183
1184 exec.set_fake_time(
1185 (zx::MonotonicInstant::ZERO
1186 + zx::MonotonicDuration::from_millis(power_off_delay_ms + 1))
1187 .into(),
1188 );
1189 assert_eq!(exec.wake_timers_and_run_until_stalled(), false);
1190
1191 exec.pin_and_run_until_stalled(async {
1192 assert_eq!(h.fake_display_power_service.last_set_power_mode_value().await, None);
1193 })
1194 .unwrap();
1195 }
1196}