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