settings/display/
display_controller.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::Error;
6use serde::{Deserialize, Serialize};
7
8use crate::base::{Merge, SettingInfo, SettingType};
9use crate::display::display_configuration::{
10    ConfigurationThemeMode, ConfigurationThemeType, DisplayConfiguration,
11};
12use crate::display::types::{DisplayInfo, LowLightMode, Theme, ThemeBuilder, ThemeMode, ThemeType};
13use crate::handler::base::Request;
14use crate::handler::setting_handler::persist::{controller as data_controller, ClientProxy};
15use crate::handler::setting_handler::{controller, ControllerError, SettingHandlerResult};
16use crate::service_context::ExternalServiceProxy;
17use async_trait::async_trait;
18use fidl_fuchsia_ui_brightness::{
19    ControlMarker as BrightnessControlMarker, ControlProxy as BrightnessControlProxy,
20};
21use fuchsia_trace as ftrace;
22use settings_common::call;
23use settings_common::config::default_settings::DefaultSetting;
24use settings_storage::device_storage::{DeviceStorage, DeviceStorageCompatible};
25use settings_storage::storage_factory::{DefaultLoader, NoneT, StorageAccess, StorageFactory};
26use settings_storage::UpdateState;
27use std::marker::PhantomData;
28use std::rc::Rc;
29use std::sync::Mutex;
30
31pub(super) const DEFAULT_MANUAL_BRIGHTNESS_VALUE: f32 = 0.5;
32pub(super) const DEFAULT_AUTO_BRIGHTNESS_VALUE: f32 = 0.5;
33
34/// Default display used if no configuration is available.
35pub(crate) const DEFAULT_DISPLAY_INFO: DisplayInfo = DisplayInfo::new(
36    false,                           /*auto_brightness_enabled*/
37    DEFAULT_MANUAL_BRIGHTNESS_VALUE, /*manual_brightness_value*/
38    DEFAULT_AUTO_BRIGHTNESS_VALUE,   /*auto_brightness_value*/
39    true,                            /*screen_enabled*/
40    LowLightMode::Disable,           /*low_light_mode*/
41    None,                            /*theme*/
42);
43
44/// Returns a default display [`DisplayInfo`] that is derived from
45/// [`DEFAULT_DISPLAY_INFO`] with any fields specified in the
46/// display configuration set.
47pub struct DisplayInfoLoader {
48    display_configuration: Mutex<DefaultSetting<DisplayConfiguration, &'static str>>,
49}
50
51impl DisplayInfoLoader {
52    pub(crate) fn new(default_setting: DefaultSetting<DisplayConfiguration, &'static str>) -> Self {
53        Self { display_configuration: Mutex::new(default_setting) }
54    }
55}
56
57impl DefaultLoader for DisplayInfoLoader {
58    type Result = DisplayInfo;
59
60    fn default_value(&self) -> Self::Result {
61        let mut default_display_info = DEFAULT_DISPLAY_INFO;
62
63        if let Ok(Some(display_configuration)) =
64            self.display_configuration.lock().unwrap().get_cached_value()
65        {
66            default_display_info.theme = Some(Theme {
67                theme_type: Some(match display_configuration.theme.theme_type {
68                    ConfigurationThemeType::Light => ThemeType::Light,
69                }),
70                theme_mode: if display_configuration
71                    .theme
72                    .theme_mode
73                    .contains(&ConfigurationThemeMode::Auto)
74                {
75                    ThemeMode::AUTO
76                } else {
77                    ThemeMode::empty()
78                },
79            });
80        }
81
82        default_display_info
83    }
84}
85
86impl DeviceStorageCompatible for DisplayInfo {
87    type Loader = DisplayInfoLoader;
88    const KEY: &'static str = "display_info";
89
90    fn try_deserialize_from(value: &str) -> Result<Self, Error> {
91        Self::extract(value).or_else(|_| DisplayInfoV5::try_deserialize_from(value).map(Self::from))
92    }
93}
94
95impl From<DisplayInfo> for SettingInfo {
96    fn from(info: DisplayInfo) -> SettingInfo {
97        SettingInfo::Brightness(info)
98    }
99}
100
101impl From<&DisplayInfo> for SettingType {
102    fn from(_: &DisplayInfo) -> SettingType {
103        SettingType::Display
104    }
105}
106
107impl From<DisplayInfoV5> for DisplayInfo {
108    fn from(v5: DisplayInfoV5) -> Self {
109        DisplayInfo {
110            auto_brightness: v5.auto_brightness,
111            auto_brightness_value: DEFAULT_AUTO_BRIGHTNESS_VALUE,
112            manual_brightness_value: v5.manual_brightness_value,
113            screen_enabled: v5.screen_enabled,
114            low_light_mode: v5.low_light_mode,
115            theme: v5.theme,
116        }
117    }
118}
119
120#[async_trait(?Send)]
121pub(crate) trait BrightnessManager: Sized {
122    async fn from_client(client: &ClientProxy) -> Result<Self, ControllerError>;
123    async fn update_brightness(
124        &self,
125        info: DisplayInfo,
126        client: &ClientProxy,
127        store: &DeviceStorage,
128        // Allows overriding of the check for whether info has changed. This is necessary for
129        // the initial restore call.
130        always_send: bool,
131    ) -> SettingHandlerResult;
132}
133
134#[async_trait(?Send)]
135impl BrightnessManager for () {
136    async fn from_client(_: &ClientProxy) -> Result<Self, ControllerError> {
137        Ok(())
138    }
139
140    // This does not send the brightness value on anywhere, it simply stores it.
141    // External services will pick up the value and set it on the brightness manager.
142    async fn update_brightness(
143        &self,
144        info: DisplayInfo,
145        client: &ClientProxy,
146        store: &DeviceStorage,
147        _: bool,
148    ) -> SettingHandlerResult {
149        let id = ftrace::Id::new();
150        if !info.is_finite() {
151            return Err(ControllerError::InvalidArgument(
152                SettingType::Display,
153                "display_info".into(),
154                format!("{info:?}").into(),
155            ));
156        }
157        client.storage_write(&store, info, id).await.map(|_| None).map_err(|e| {
158            log::error!("Failed to update display info {e:?}");
159            ControllerError::WriteFailure(SettingType::Display)
160        })
161    }
162}
163
164pub(crate) struct ExternalBrightnessControl {
165    brightness_service: ExternalServiceProxy<BrightnessControlProxy>,
166}
167
168#[async_trait(?Send)]
169impl BrightnessManager for ExternalBrightnessControl {
170    async fn from_client(client: &ClientProxy) -> Result<Self, ControllerError> {
171        client
172            .get_service_context()
173            .connect::<BrightnessControlMarker>()
174            .await
175            .map(|brightness_service| Self { brightness_service })
176            .map_err(|_| {
177                ControllerError::InitFailure("could not connect to brightness service".into())
178            })
179    }
180
181    async fn update_brightness(
182        &self,
183        info: DisplayInfo,
184        client: &ClientProxy,
185        store: &DeviceStorage,
186        always_send: bool,
187    ) -> SettingHandlerResult {
188        let id = ftrace::Id::new();
189        if !info.is_finite() {
190            return Err(ControllerError::InvalidArgument(
191                SettingType::Display,
192                "display_info".into(),
193                format!("{info:?}").into(),
194            ));
195        }
196        let update_state =
197            client.storage_write::<DisplayInfo>(&store, info.clone(), id).await.map_err(|e| {
198                log::error!("Failed to update display info {e:?}");
199                ControllerError::WriteFailure(SettingType::Display)
200            })?;
201        if update_state == UpdateState::Unchanged && !always_send {
202            return Ok(None);
203        }
204
205        if info.auto_brightness {
206            call!(self.brightness_service => set_auto_brightness())
207        } else {
208            call!(self.brightness_service => set_manual_brightness(info.manual_brightness_value))
209        }
210        .map(|_| None)
211        .map_err(|e| {
212            ControllerError::ExternalFailure(
213                SettingType::Display,
214                "brightness_service".into(),
215                "set_brightness".into(),
216                format!("{e:?}").into(),
217            )
218        })
219    }
220}
221
222pub(crate) struct DisplayController<F, T = ()>
223where
224    T: BrightnessManager,
225{
226    brightness_manager: T,
227    client: ClientProxy,
228    store: Rc<DeviceStorage>,
229    _phantom: PhantomData<F>,
230}
231
232impl<F, T> StorageAccess for DisplayController<F, T>
233where
234    T: BrightnessManager,
235{
236    type Storage = DeviceStorage;
237    type Data = DisplayInfo;
238    const STORAGE_KEY: &'static str = DisplayInfo::KEY;
239}
240
241#[async_trait(?Send)]
242impl<F, T> data_controller::CreateWithAsync for DisplayController<F, T>
243where
244    T: BrightnessManager,
245    F: StorageFactory<Storage = DeviceStorage>,
246{
247    type Data = Rc<F>;
248    async fn create_with(client: ClientProxy, data: Self::Data) -> Result<Self, ControllerError> {
249        let brightness_manager = <T as BrightnessManager>::from_client(&client).await?;
250        let store = data.get_store().await;
251        Ok(Self { brightness_manager, client, store, _phantom: PhantomData })
252    }
253}
254
255#[async_trait(?Send)]
256impl<F, T> controller::Handle for DisplayController<F, T>
257where
258    T: BrightnessManager,
259{
260    async fn handle(&self, request: Request) -> Option<SettingHandlerResult> {
261        match request {
262            Request::Restore => {
263                let display_info = self.store.get::<DisplayInfo>().await;
264                assert!(display_info.is_finite());
265
266                // Load and set value.
267                Some(
268                    self.brightness_manager
269                        .update_brightness(display_info, &self.client, &self.store, true)
270                        .await,
271                )
272            }
273            Request::SetDisplayInfo(mut set_display_info) => {
274                let display_info = self.store.get::<DisplayInfo>().await;
275                assert!(display_info.is_finite());
276
277                if let Some(theme) = set_display_info.theme {
278                    set_display_info.theme = self.build_theme(theme, &display_info);
279                }
280
281                Some(
282                    self.brightness_manager
283                        .update_brightness(
284                            display_info.merge(set_display_info),
285                            &self.client,
286                            &self.store,
287                            false,
288                        )
289                        .await,
290                )
291            }
292            Request::Get => {
293                Some(Ok(Some(SettingInfo::Brightness(self.store.get::<DisplayInfo>().await))))
294            }
295            _ => None,
296        }
297    }
298}
299
300impl<F, T> DisplayController<F, T>
301where
302    T: BrightnessManager,
303{
304    fn build_theme(&self, incoming_theme: Theme, display_info: &DisplayInfo) -> Option<Theme> {
305        let existing_theme_type = display_info.theme.and_then(|theme| theme.theme_type);
306        let new_theme_type = incoming_theme.theme_type.or(existing_theme_type);
307
308        ThemeBuilder::new()
309            .set_theme_type(new_theme_type)
310            .set_theme_mode(incoming_theme.theme_mode)
311            .build()
312    }
313}
314
315/// The following struct should never be modified. It represents an old
316/// version of the display settings.
317#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
318pub struct DisplayInfoV1 {
319    /// The last brightness value that was manually set.
320    pub manual_brightness_value: f32,
321    pub auto_brightness: bool,
322    pub low_light_mode: LowLightMode,
323}
324
325impl DisplayInfoV1 {
326    const fn new(
327        auto_brightness: bool,
328        manual_brightness_value: f32,
329        low_light_mode: LowLightMode,
330    ) -> DisplayInfoV1 {
331        DisplayInfoV1 { manual_brightness_value, auto_brightness, low_light_mode }
332    }
333}
334
335impl DeviceStorageCompatible for DisplayInfoV1 {
336    type Loader = NoneT;
337    const KEY: &'static str = "display_infoV1";
338}
339
340impl Default for DisplayInfoV1 {
341    fn default() -> Self {
342        DisplayInfoV1::new(
343            false,                           /*auto_brightness_enabled*/
344            DEFAULT_MANUAL_BRIGHTNESS_VALUE, /*brightness_value*/
345            LowLightMode::Disable,           /*low_light_mode*/
346        )
347    }
348}
349
350/// The following struct should never be modified.  It represents an old
351/// version of the display settings.
352#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
353pub struct DisplayInfoV2 {
354    pub manual_brightness_value: f32,
355    pub auto_brightness: bool,
356    pub low_light_mode: LowLightMode,
357    pub theme_mode: ThemeModeV1,
358}
359
360impl DisplayInfoV2 {
361    const fn new(
362        auto_brightness: bool,
363        manual_brightness_value: f32,
364        low_light_mode: LowLightMode,
365        theme_mode: ThemeModeV1,
366    ) -> DisplayInfoV2 {
367        DisplayInfoV2 { manual_brightness_value, auto_brightness, low_light_mode, theme_mode }
368    }
369}
370
371impl DeviceStorageCompatible for DisplayInfoV2 {
372    type Loader = NoneT;
373    const KEY: &'static str = "display_infoV2";
374
375    fn try_deserialize_from(value: &str) -> Result<Self, Error> {
376        Self::extract(value).or_else(|_| DisplayInfoV1::try_deserialize_from(value).map(Self::from))
377    }
378}
379
380impl Default for DisplayInfoV2 {
381    fn default() -> Self {
382        DisplayInfoV2::new(
383            false,                           /*auto_brightness_enabled*/
384            DEFAULT_MANUAL_BRIGHTNESS_VALUE, /*brightness_value*/
385            LowLightMode::Disable,           /*low_light_mode*/
386            ThemeModeV1::Unknown,            /*theme_mode*/
387        )
388    }
389}
390
391impl From<DisplayInfoV1> for DisplayInfoV2 {
392    fn from(v1: DisplayInfoV1) -> Self {
393        DisplayInfoV2 {
394            auto_brightness: v1.auto_brightness,
395            manual_brightness_value: v1.manual_brightness_value,
396            low_light_mode: v1.low_light_mode,
397            theme_mode: ThemeModeV1::Unknown,
398        }
399    }
400}
401
402#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
403pub enum ThemeModeV1 {
404    Unknown,
405    Default,
406    Light,
407    Dark,
408    /// Product can choose a theme based on ambient cues.
409    Auto,
410}
411
412impl From<ThemeModeV1> for ThemeType {
413    fn from(theme_mode_v1: ThemeModeV1) -> Self {
414        match theme_mode_v1 {
415            ThemeModeV1::Default => ThemeType::Default,
416            ThemeModeV1::Light => ThemeType::Light,
417            ThemeModeV1::Dark => ThemeType::Dark,
418            // ThemeType has removed Auto field, see https://fxbug.dev/42143417
419            ThemeModeV1::Unknown | ThemeModeV1::Auto => ThemeType::Unknown,
420        }
421    }
422}
423
424#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
425pub struct DisplayInfoV3 {
426    /// The last brightness value that was manually set.
427    pub manual_brightness_value: f32,
428    pub auto_brightness: bool,
429    pub screen_enabled: bool,
430    pub low_light_mode: LowLightMode,
431    pub theme_mode: ThemeModeV1,
432}
433
434impl DisplayInfoV3 {
435    const fn new(
436        auto_brightness: bool,
437        manual_brightness_value: f32,
438        screen_enabled: bool,
439        low_light_mode: LowLightMode,
440        theme_mode: ThemeModeV1,
441    ) -> DisplayInfoV3 {
442        DisplayInfoV3 {
443            manual_brightness_value,
444            auto_brightness,
445            screen_enabled,
446            low_light_mode,
447            theme_mode,
448        }
449    }
450}
451
452impl DeviceStorageCompatible for DisplayInfoV3 {
453    type Loader = NoneT;
454    const KEY: &'static str = "display_info";
455
456    fn try_deserialize_from(value: &str) -> Result<Self, Error> {
457        Self::extract(value).or_else(|_| DisplayInfoV2::try_deserialize_from(value).map(Self::from))
458    }
459}
460
461impl Default for DisplayInfoV3 {
462    fn default() -> Self {
463        DisplayInfoV3::new(
464            false,                           /*auto_brightness_enabled*/
465            DEFAULT_MANUAL_BRIGHTNESS_VALUE, /*brightness_value*/
466            true,                            /*screen_enabled*/
467            LowLightMode::Disable,           /*low_light_mode*/
468            ThemeModeV1::Unknown,            /*theme_mode*/
469        )
470    }
471}
472
473impl From<DisplayInfoV2> for DisplayInfoV3 {
474    fn from(v2: DisplayInfoV2) -> Self {
475        DisplayInfoV3 {
476            auto_brightness: v2.auto_brightness,
477            manual_brightness_value: v2.manual_brightness_value,
478            screen_enabled: true,
479            low_light_mode: v2.low_light_mode,
480            theme_mode: v2.theme_mode,
481        }
482    }
483}
484
485#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
486pub struct DisplayInfoV4 {
487    /// The last brightness value that was manually set.
488    pub manual_brightness_value: f32,
489    pub auto_brightness: bool,
490    pub screen_enabled: bool,
491    pub low_light_mode: LowLightMode,
492    pub theme_type: ThemeType,
493}
494
495impl DisplayInfoV4 {
496    const fn new(
497        auto_brightness: bool,
498        manual_brightness_value: f32,
499        screen_enabled: bool,
500        low_light_mode: LowLightMode,
501        theme_type: ThemeType,
502    ) -> DisplayInfoV4 {
503        DisplayInfoV4 {
504            manual_brightness_value,
505            auto_brightness,
506            screen_enabled,
507            low_light_mode,
508            theme_type,
509        }
510    }
511}
512
513impl From<DisplayInfoV3> for DisplayInfoV4 {
514    fn from(v3: DisplayInfoV3) -> Self {
515        DisplayInfoV4 {
516            auto_brightness: v3.auto_brightness,
517            manual_brightness_value: v3.manual_brightness_value,
518            screen_enabled: v3.screen_enabled,
519            low_light_mode: v3.low_light_mode,
520            // In v4, the field formerly known as theme_mode was renamed to
521            // theme_type.
522            theme_type: ThemeType::from(v3.theme_mode),
523        }
524    }
525}
526
527impl DeviceStorageCompatible for DisplayInfoV4 {
528    type Loader = NoneT;
529    const KEY: &'static str = "display_info";
530
531    fn try_deserialize_from(value: &str) -> Result<Self, Error> {
532        Self::extract(value).or_else(|_| DisplayInfoV3::try_deserialize_from(value).map(Self::from))
533    }
534}
535
536impl Default for DisplayInfoV4 {
537    fn default() -> Self {
538        DisplayInfoV4::new(
539            false,                           /*auto_brightness_enabled*/
540            DEFAULT_MANUAL_BRIGHTNESS_VALUE, /*brightness_value*/
541            true,                            /*screen_enabled*/
542            LowLightMode::Disable,           /*low_light_mode*/
543            ThemeType::Unknown,              /*theme_type*/
544        )
545    }
546}
547
548#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
549#[serde(deny_unknown_fields)]
550pub struct DisplayInfoV5 {
551    /// The last brightness value that was manually set.
552    pub manual_brightness_value: f32,
553    pub auto_brightness: bool,
554    pub screen_enabled: bool,
555    pub low_light_mode: LowLightMode,
556    pub theme: Option<Theme>,
557}
558
559impl DisplayInfoV5 {
560    const fn new(
561        auto_brightness: bool,
562        manual_brightness_value: f32,
563        screen_enabled: bool,
564        low_light_mode: LowLightMode,
565        theme: Option<Theme>,
566    ) -> DisplayInfoV5 {
567        DisplayInfoV5 {
568            manual_brightness_value,
569            auto_brightness,
570            screen_enabled,
571            low_light_mode,
572            theme,
573        }
574    }
575}
576
577impl From<DisplayInfoV4> for DisplayInfoV5 {
578    fn from(v4: DisplayInfoV4) -> Self {
579        DisplayInfoV5 {
580            auto_brightness: v4.auto_brightness,
581            manual_brightness_value: v4.manual_brightness_value,
582            screen_enabled: v4.screen_enabled,
583            low_light_mode: v4.low_light_mode,
584            // Clients has migrated off auto theme_type, we should not get theme_type as Auto
585            theme: Some(Theme::new(Some(v4.theme_type), ThemeMode::empty())),
586        }
587    }
588}
589
590impl DeviceStorageCompatible for DisplayInfoV5 {
591    type Loader = NoneT;
592    const KEY: &'static str = "display_info";
593
594    fn try_deserialize_from(value: &str) -> Result<Self, Error> {
595        Self::extract(value).or_else(|_| DisplayInfoV4::try_deserialize_from(value).map(Self::from))
596    }
597}
598
599impl Default for DisplayInfoV5 {
600    fn default() -> Self {
601        DisplayInfoV5::new(
602            false,                                                          /*auto_brightness_enabled*/
603            DEFAULT_MANUAL_BRIGHTNESS_VALUE,                                /*brightness_value*/
604            true,                                                           /*screen_enabled*/
605            LowLightMode::Disable,                                          /*low_light_mode*/
606            Some(Theme::new(Some(ThemeType::Unknown), ThemeMode::empty())), /*theme_type*/
607        )
608    }
609}
610
611#[cfg(test)]
612mod tests {
613    use super::*;
614
615    #[fuchsia::test]
616    fn test_display_migration_v1_to_v2() {
617        let v1 = DisplayInfoV1 {
618            manual_brightness_value: 0.6,
619            auto_brightness: true,
620            low_light_mode: LowLightMode::Enable,
621        };
622
623        let serialized_v1 = v1.serialize_to();
624        let v2 = DisplayInfoV2::try_deserialize_from(&serialized_v1)
625            .expect("deserialization should succeed");
626
627        assert_eq!(
628            v2,
629            DisplayInfoV2 {
630                manual_brightness_value: v1.manual_brightness_value,
631                auto_brightness: v1.auto_brightness,
632                low_light_mode: v1.low_light_mode,
633                theme_mode: DisplayInfoV2::default().theme_mode,
634            }
635        );
636    }
637
638    #[fuchsia::test]
639    fn test_display_migration_v2_to_v3() {
640        let v2 = DisplayInfoV2 {
641            manual_brightness_value: 0.7,
642            auto_brightness: true,
643            low_light_mode: LowLightMode::Enable,
644            theme_mode: ThemeModeV1::Default,
645        };
646
647        let serialized_v2 = v2.serialize_to();
648        let v3 = DisplayInfoV3::try_deserialize_from(&serialized_v2)
649            .expect("deserialization should succeed");
650
651        assert_eq!(
652            v3,
653            DisplayInfoV3 {
654                manual_brightness_value: v2.manual_brightness_value,
655                auto_brightness: v2.auto_brightness,
656                screen_enabled: DisplayInfoV3::default().screen_enabled,
657                low_light_mode: v2.low_light_mode,
658                theme_mode: v2.theme_mode,
659            }
660        );
661    }
662
663    #[fuchsia::test]
664    fn test_display_migration_v3_to_v4() {
665        let v3 = DisplayInfoV3 {
666            manual_brightness_value: 0.7,
667            auto_brightness: true,
668            low_light_mode: LowLightMode::Enable,
669            theme_mode: ThemeModeV1::Light,
670            screen_enabled: false,
671        };
672
673        let serialized_v3 = v3.serialize_to();
674        let v4 = DisplayInfoV4::try_deserialize_from(&serialized_v3)
675            .expect("deserialization should succeed");
676
677        // In v4, the field formally known as theme_mode is theme_type.
678        assert_eq!(
679            v4,
680            DisplayInfoV4 {
681                manual_brightness_value: v3.manual_brightness_value,
682                auto_brightness: v3.auto_brightness,
683                low_light_mode: v3.low_light_mode,
684                theme_type: ThemeType::Light,
685                screen_enabled: v3.screen_enabled,
686            }
687        );
688    }
689
690    #[fuchsia::test]
691    fn test_display_migration_v4_to_v5() {
692        let v4 = DisplayInfoV4 {
693            manual_brightness_value: 0.7,
694            auto_brightness: true,
695            low_light_mode: LowLightMode::Enable,
696            theme_type: ThemeType::Dark,
697            screen_enabled: false,
698        };
699
700        let serialized_v4 = v4.serialize_to();
701        let v5 = DisplayInfoV5::try_deserialize_from(&serialized_v4)
702            .expect("deserialization should succeed");
703
704        assert_eq!(
705            v5,
706            DisplayInfoV5 {
707                manual_brightness_value: v4.manual_brightness_value,
708                auto_brightness: v4.auto_brightness,
709                low_light_mode: v4.low_light_mode,
710                theme: Some(Theme::new(Some(v4.theme_type), ThemeMode::empty())),
711                screen_enabled: v4.screen_enabled,
712            }
713        );
714    }
715
716    #[fuchsia::test]
717    fn test_display_migration_v1_to_current() {
718        let v1 = DisplayInfoV1 {
719            manual_brightness_value: 0.6,
720            auto_brightness: true,
721            low_light_mode: LowLightMode::Enable,
722        };
723
724        let serialized_v1 = v1.serialize_to();
725        let current = DisplayInfo::try_deserialize_from(&serialized_v1)
726            .expect("deserialization should succeed");
727
728        assert_eq!(
729            current,
730            DisplayInfo {
731                manual_brightness_value: v1.manual_brightness_value,
732                auto_brightness: v1.auto_brightness,
733                low_light_mode: v1.low_light_mode,
734                theme: Some(Theme::new(Some(ThemeType::Unknown), ThemeMode::empty())),
735                // screen_enabled was added in v3.
736                screen_enabled: DisplayInfoV3::default().screen_enabled,
737                auto_brightness_value: DEFAULT_DISPLAY_INFO.auto_brightness_value,
738            }
739        );
740    }
741
742    #[fuchsia::test]
743    fn test_display_migration_v2_to_current() {
744        let v2 = DisplayInfoV2 {
745            manual_brightness_value: 0.6,
746            auto_brightness: true,
747            low_light_mode: LowLightMode::Enable,
748            theme_mode: ThemeModeV1::Light,
749        };
750
751        let serialized_v2 = v2.serialize_to();
752        let current = DisplayInfo::try_deserialize_from(&serialized_v2)
753            .expect("deserialization should succeed");
754
755        assert_eq!(
756            current,
757            DisplayInfo {
758                manual_brightness_value: v2.manual_brightness_value,
759                auto_brightness: v2.auto_brightness,
760                low_light_mode: v2.low_light_mode,
761                theme: Some(Theme::new(Some(ThemeType::Light), ThemeMode::empty())),
762                // screen_enabled was added in v3.
763                screen_enabled: DisplayInfoV3::default().screen_enabled,
764                auto_brightness_value: DEFAULT_DISPLAY_INFO.auto_brightness_value,
765            }
766        );
767    }
768
769    #[fuchsia::test]
770    fn test_display_migration_v3_to_current() {
771        let v3 = DisplayInfoV3 {
772            manual_brightness_value: 0.6,
773            auto_brightness: true,
774            low_light_mode: LowLightMode::Enable,
775            theme_mode: ThemeModeV1::Light,
776            screen_enabled: false,
777        };
778
779        let serialized_v3 = v3.serialize_to();
780        let current = DisplayInfo::try_deserialize_from(&serialized_v3)
781            .expect("deserialization should succeed");
782
783        assert_eq!(
784            current,
785            DisplayInfo {
786                manual_brightness_value: v3.manual_brightness_value,
787                auto_brightness: v3.auto_brightness,
788                low_light_mode: v3.low_light_mode,
789                theme: Some(Theme::new(Some(ThemeType::Light), ThemeMode::empty())),
790                // screen_enabled was added in v3.
791                screen_enabled: v3.screen_enabled,
792                auto_brightness_value: DEFAULT_DISPLAY_INFO.auto_brightness_value,
793            }
794        );
795    }
796
797    #[fuchsia::test]
798    fn test_display_migration_v4_to_current() {
799        let v4 = DisplayInfoV4 {
800            manual_brightness_value: 0.6,
801            auto_brightness: true,
802            low_light_mode: LowLightMode::Enable,
803            theme_type: ThemeType::Light,
804            screen_enabled: false,
805        };
806
807        let serialized_v4 = v4.serialize_to();
808        let current = DisplayInfo::try_deserialize_from(&serialized_v4)
809            .expect("deserialization should succeed");
810
811        assert_eq!(
812            current,
813            DisplayInfo {
814                manual_brightness_value: v4.manual_brightness_value,
815                auto_brightness: v4.auto_brightness,
816                low_light_mode: v4.low_light_mode,
817                theme: Some(Theme::new(Some(ThemeType::Light), ThemeMode::empty())),
818                screen_enabled: v4.screen_enabled,
819                auto_brightness_value: DEFAULT_DISPLAY_INFO.auto_brightness_value,
820            }
821        );
822    }
823
824    #[fuchsia::test]
825    fn test_display_migration_v5_to_current() {
826        let v5 = DisplayInfoV5 {
827            manual_brightness_value: 0.6,
828            auto_brightness: true,
829            low_light_mode: LowLightMode::Enable,
830            theme: Some(Theme::new(Some(ThemeType::Light), ThemeMode::AUTO)),
831            screen_enabled: false,
832        };
833
834        let serialized_v5 = v5.serialize_to();
835        let current = DisplayInfo::try_deserialize_from(&serialized_v5)
836            .expect("deserialization should succeed");
837
838        assert_eq!(
839            current,
840            DisplayInfo {
841                manual_brightness_value: v5.manual_brightness_value,
842                auto_brightness: v5.auto_brightness,
843                low_light_mode: v5.low_light_mode,
844                theme: Some(Theme::new(Some(ThemeType::Light), ThemeMode::AUTO)),
845                screen_enabled: v5.screen_enabled,
846                auto_brightness_value: DEFAULT_DISPLAY_INFO.auto_brightness_value,
847            }
848        );
849    }
850}