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};
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
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, 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                    // 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_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        // Setup
494        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        // Act
501        let get_fut = mock.get();
502        let (brightness, _) = future::join(get_fut, backlight_fut).await;
503
504        // Assert
505        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        // Setup
511        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        // Act
518        let get_fut = mock.get();
519        let (brightness, _) = future::join(get_fut, backlight_fut).await;
520
521        // Assert
522        assert_eq!(brightness.unwrap(), 0.04);
523    }
524
525    #[fasync::run_singlethreaded(test)]
526    async fn test_zero_brightness_turns_backlight_off() {
527        // Setup
528        let (mock, backlight_stream) = mock_backlight();
529        let backlight_fut = mock_device_set(backlight_stream);
530
531        // Act
532        let set_fut = mock.set(0.0);
533        let (_, backlight_command) = futures::join!(set_fut, backlight_fut);
534
535        // Assert
536        assert_eq!(backlight_command.backlight_on, false);
537    }
538
539    #[fasync::run_singlethreaded(test)]
540    async fn test_negative_brightness_turns_backlight_off() {
541        // Setup
542        let (mock, backlight_stream) = mock_backlight();
543        let backlight_fut = mock_device_set(backlight_stream);
544
545        // Act
546        let set_fut = mock.set(-0.01);
547        let (_, backlight_command) = join!(set_fut, backlight_fut);
548
549        // Assert
550        assert_eq!(backlight_command.backlight_on, false);
551    }
552
553    #[fasync::run_singlethreaded(test)]
554    async fn test_brightness_turns_backlight_on() {
555        // Setup
556        let (mock, backlight_stream) = mock_backlight();
557        let backlight_fut = mock_device_set(backlight_stream);
558
559        // Act
560        let set_fut = mock.set(0.55);
561        let (_, backlight_command) = join!(set_fut, backlight_fut);
562
563        // Assert
564        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        // Setup
571        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        // Act
578        let mock_fut = mock.get_max_absolute_brightness();
579        let (max_brightness, _) = future::join(mock_fut, backlight_fut).await;
580
581        // Assert
582        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        /// Wakes expired timers and runs any existing spawned tasks until they stall. Returns
738        /// `true` if one or more timers were awoken.
739        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        /// Note that callers need to declare a variable for the executor _before_ the handles, or
769        /// else the executor will cause a panic when it's dropped before its futures.
770        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        // Right before the power-off delay
866        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        // Right after the power-off delay.
879        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        // Right before the power-off delay
997        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        // Right after the power-off delay.
1017        exec.set_fake_time(
1018            (zx::MonotonicInstant::ZERO
1019                + zx::MonotonicDuration::from_millis(power_off_delay_ms + 10))
1020            .into(),
1021        );
1022        // The timer task should have been canceled (dropped).
1023        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            // The futures that would have turned on the backlight should be cancelled.
1060            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}