Skip to main content

lib/
backlight.rs

1// Copyright 2019 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 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
19/// The minimum brightness value that can be sent to the backlight service.
20const MIN_REGULATED_BRIGHTNESS: f64 = 0.0004;
21/// The maximum brightness that can be sent to the backlight service.
22const 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/// Possible combinations of backlight and display power states.
44///
45/// When powering down, the backlight must always be turned off with a delay before the DDIC is
46/// turned off.
47///
48/// When powering up, the DDIC must always be turned on with a delay before the backlight is turned
49/// on.
50#[derive(Derivative)]
51#[derivative(Debug)]
52enum PowerState {
53    /// This state should only be used as a temporary placeholder while swapping values inside
54    /// containers (e.g. Mutex).
55    Indeterminate,
56    BothOn,
57    /// The backlight is off and the DDIC is scheduled to turn off.
58    BacklightOffDisplayPoweringDown(#[derivative(Debug = "ignore")] fasync::Task<()>),
59    BothOff,
60    /// The DDIC is on and the backlight is scheduled to turn on. A sequence of backlight changes
61    /// may be queued.
62    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/// A backlight command that is queued to be invoked after the power on delay elapses.
75#[derive(Debug)]
76struct PendingBacklightCommand {
77    command: BacklightCommand,
78    /// Will be resolved when the command is invoked (or cancelled if the command is dropped due to
79    /// a state change).
80    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    /// Creates a simple `Backlight` control, for devices on which DDIC power cannot be switched
91    /// off and on.
92    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    /// Creates a `Backlight` control that manages both the backlight brightness/power and the power
102    /// state of the DDIC.
103    #[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        // TODO(https://fxbug.dev/42111816): Handle error here as well, similar to get_brightness above. Might involve
156        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        // Note that `power_state` MUST be `drop()`ped before yielding an async value, or there will
176        // be a deadlock. Rewriting this `match` expression to not use `.await`, and hence to be
177        // able to drop the guard implicitly when it goes out of scope, would be too messy.
178        let mut power_state = power_state_arc.lock().await;
179        match &mut *power_state {
180            PowerState::BothOn => {
181                if backlight_on {
182                    // See below
183                } 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                    // Cancel the scheduled display shutdown.
198                    *power_state = PowerState::BothOn;
199                    drop(power_state);
200                    self.set_backlight_state_normalized(regulated_value, backlight_on).await
201                } else {
202                    // No-op. Already scheduled to turn off.
203                    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                    // Cancel scheduled backlight power on.
235                    *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    /// Creates a pending command that can be queued in a
261    /// [`PowerState::DisplayOnBacklightPoweringUp`] state.
262    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    /// Process scheduled updates to the power state.
275    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                            // Don't get stuck in an indeterminate state, nor start a retry loop.
294                            // Subsequent calls changes to the backlight state should work normally.
295                            *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                            // Even if a backlight command fails for some reason, we need to treat
310                            // the backlight as on. Subsequent commands should still work.
311                            *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/// Wrapper around [`DisplayPowerProxy`], with state management and configuration values.
352#[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        // There is no direct way to retrieve the power state of the DDIC, so we infer it from the
368        // backlight's state on startup.
369        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        // Setup
493        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        // Act
500        let get_fut = mock.get();
501        let (brightness, _) = future::join(get_fut, backlight_fut).await;
502
503        // Assert
504        assert_eq!(brightness.unwrap(), 0.0);
505    }
506
507    #[fuchsia::test]
508    async fn test_brightness_returns_non_zero_if_backlight_on() {
509        // Setup
510        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        // Act
517        let get_fut = mock.get();
518        let (brightness, _) = future::join(get_fut, backlight_fut).await;
519
520        // Assert
521        assert_eq!(brightness.unwrap(), 0.04);
522    }
523
524    #[fuchsia::test]
525    async fn test_zero_brightness_turns_backlight_off() {
526        // Setup
527        let (mock, backlight_stream) = mock_backlight();
528        let backlight_fut = mock_device_set(backlight_stream);
529
530        // Act
531        let set_fut = mock.set(0.0);
532        let (_, backlight_command) = futures::join!(set_fut, backlight_fut);
533
534        // Assert
535        assert_eq!(backlight_command.backlight_on, false);
536    }
537
538    #[fuchsia::test]
539    async fn test_negative_brightness_turns_backlight_off() {
540        // Setup
541        let (mock, backlight_stream) = mock_backlight();
542        let backlight_fut = mock_device_set(backlight_stream);
543
544        // Act
545        let set_fut = mock.set(-0.01);
546        let (_, backlight_command) = join!(set_fut, backlight_fut);
547
548        // Assert
549        assert_eq!(backlight_command.backlight_on, false);
550    }
551
552    #[fuchsia::test]
553    async fn test_brightness_turns_backlight_on() {
554        // Setup
555        let (mock, backlight_stream) = mock_backlight();
556        let backlight_fut = mock_device_set(backlight_stream);
557
558        // Act
559        let set_fut = mock.set(0.55);
560        let (_, backlight_command) = join!(set_fut, backlight_fut);
561
562        // Assert
563        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        // Setup
570        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        // Act
577        let mock_fut = mock.get_max_absolute_brightness();
578        let (max_brightness, _) = future::join(mock_fut, backlight_fut).await;
579
580        // Assert
581        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        /// Wakes expired timers and runs any existing spawned tasks until they stall. Returns
739        /// `true` if one or more timers were awoken.
740        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        /// Note that callers need to declare a variable for the executor _before_ the handles, or
770        /// else the executor will cause a panic when it's dropped before its futures.
771        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        // Right before the power-off delay
867        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        // Right after the power-off delay.
880        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        // Right before the power-off delay
998        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        // Right after the power-off delay.
1018        exec.set_fake_time(
1019            (zx::MonotonicInstant::ZERO
1020                + zx::MonotonicDuration::from_millis(power_off_delay_ms + 10))
1021            .into(),
1022        );
1023        // The timer task should have been canceled (dropped).
1024        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            // The futures that would have turned on the backlight should be cancelled.
1061            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}