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