1use crate::display_configuration::{
6 ConfigurationThemeMode, ConfigurationThemeType, DisplayConfiguration,
7};
8use crate::display_fidl_handler::Publisher;
9use crate::types::{
10 DisplayInfo, LowLightMode, SetDisplayInfo, Theme, ThemeBuilder, ThemeMode, ThemeType,
11};
12use anyhow::{Context, Error};
13use async_trait::async_trait;
14use fidl_fuchsia_ui_brightness::{
15 ControlMarker as BrightnessControlMarker, ControlProxy as BrightnessControlProxy,
16};
17use fuchsia_async as fasync;
18use futures::StreamExt;
19use futures::channel::mpsc::UnboundedReceiver;
20use futures::channel::oneshot::Sender;
21use serde::{Deserialize, Serialize};
22use settings_common::call;
23use settings_common::config::default_settings::DefaultSetting;
24use settings_common::inspect::event::{
25 ExternalEventPublisher, ResponseType, SettingValuePublisher,
26};
27use settings_common::service_context::{ExternalServiceProxy, ServiceContext};
28use settings_common::utils::Merge;
29use settings_storage::UpdateState;
30use settings_storage::device_storage::{DeviceStorage, DeviceStorageCompatible};
31use settings_storage::storage_factory::{DefaultLoader, NoneT, StorageAccess, StorageFactory};
32use std::rc::Rc;
33use std::sync::Mutex;
34
35pub(super) const DEFAULT_MANUAL_BRIGHTNESS_VALUE: f32 = 0.5;
36pub(super) const DEFAULT_AUTO_BRIGHTNESS_VALUE: f32 = 0.5;
37
38pub(crate) const DEFAULT_DISPLAY_INFO: DisplayInfo = DisplayInfo::new(
40 false, DEFAULT_MANUAL_BRIGHTNESS_VALUE, DEFAULT_AUTO_BRIGHTNESS_VALUE, true, LowLightMode::Disable, None, );
47
48pub struct DisplayInfoLoader {
52 display_configuration: Mutex<DefaultSetting<DisplayConfiguration, &'static str>>,
53}
54
55impl DisplayInfoLoader {
56 pub fn new(default_setting: DefaultSetting<DisplayConfiguration, &'static str>) -> Self {
57 Self { display_configuration: Mutex::new(default_setting) }
58 }
59}
60
61impl DefaultLoader for DisplayInfoLoader {
62 type Result = DisplayInfo;
63
64 fn default_value(&self) -> Self::Result {
65 let mut default_display_info = DEFAULT_DISPLAY_INFO;
66
67 if let Ok(Some(display_configuration)) =
68 self.display_configuration.lock().unwrap().get_cached_value()
69 {
70 default_display_info.theme = Some(Theme {
71 theme_type: Some(match display_configuration.theme.theme_type {
72 ConfigurationThemeType::Light => ThemeType::Light,
73 }),
74 theme_mode: if display_configuration
75 .theme
76 .theme_mode
77 .contains(&ConfigurationThemeMode::Auto)
78 {
79 ThemeMode::AUTO
80 } else {
81 ThemeMode::empty()
82 },
83 });
84 }
85
86 default_display_info
87 }
88}
89
90impl DeviceStorageCompatible for DisplayInfo {
91 type Loader = DisplayInfoLoader;
92 const KEY: &'static str = "display_info";
93
94 fn try_deserialize_from(value: &str) -> Result<Self, Error> {
95 Self::extract(value).or_else(|_| DisplayInfoV5::try_deserialize_from(value).map(Self::from))
96 }
97}
98
99impl From<DisplayInfoV5> for DisplayInfo {
100 fn from(v5: DisplayInfoV5) -> Self {
101 DisplayInfo {
102 auto_brightness: v5.auto_brightness,
103 auto_brightness_value: DEFAULT_AUTO_BRIGHTNESS_VALUE,
104 manual_brightness_value: v5.manual_brightness_value,
105 screen_enabled: v5.screen_enabled,
106 low_light_mode: v5.low_light_mode,
107 theme: v5.theme,
108 }
109 }
110}
111
112#[derive(thiserror::Error, Debug)]
113pub enum DisplayError {
114 #[error("Failed to initialize controller: {0:?}")]
115 InitFailure(Error),
116 #[error("Invalid argument: arg: {0:?}, value: {1:?}")]
117 InvalidArgument(&'static str, String),
118 #[error("External failure for Display: dependency: {0:?} request:{1:?} error:{2}")]
119 ExternalFailure(&'static str, &'static str, String),
120 #[error("Write failed for Display: {0:?}")]
121 WriteFailure(Error),
122}
123
124impl From<&DisplayError> for ResponseType {
125 fn from(error: &DisplayError) -> Self {
126 match error {
127 DisplayError::InitFailure(..) => ResponseType::InitFailure,
128 DisplayError::InvalidArgument(..) => ResponseType::InvalidArgument,
129 DisplayError::ExternalFailure(..) => ResponseType::ExternalFailure,
130 DisplayError::WriteFailure(..) => ResponseType::StorageFailure,
131 }
132 }
133}
134
135#[async_trait(?Send)]
136pub trait BrightnessManager: Sized {
137 async fn from_context(
138 service_context: &ServiceContext,
139 external_publisher: ExternalEventPublisher,
140 ) -> Result<Self, DisplayError>;
141 async fn update_brightness(
142 &self,
143 info: DisplayInfo,
144 store: &DeviceStorage,
145 always_send: bool,
148 ) -> Result<Option<DisplayInfo>, DisplayError>;
149}
150
151#[async_trait(?Send)]
152impl BrightnessManager for () {
153 async fn from_context(
154 _: &ServiceContext,
155 _: ExternalEventPublisher,
156 ) -> Result<Self, DisplayError> {
157 Ok(())
158 }
159
160 async fn update_brightness(
163 &self,
164 info: DisplayInfo,
165 store: &DeviceStorage,
166 _: bool,
167 ) -> Result<Option<DisplayInfo>, DisplayError> {
168 if !info.is_finite() {
169 return Err(DisplayError::InvalidArgument("display_info", format!("{info:?}")));
170 }
171 store
172 .write(&info)
173 .await
174 .map(|state| (UpdateState::Updated == state).then_some(info))
175 .context("updating display info")
176 .map_err(DisplayError::WriteFailure)
177 }
178}
179
180pub struct ExternalBrightnessControl {
181 brightness_service: ExternalServiceProxy<BrightnessControlProxy, ExternalEventPublisher>,
182}
183
184#[async_trait(?Send)]
185impl BrightnessManager for ExternalBrightnessControl {
186 async fn from_context(
187 service_context: &ServiceContext,
188 external_publisher: ExternalEventPublisher,
189 ) -> Result<Self, DisplayError> {
190 service_context
191 .connect_with_publisher::<BrightnessControlMarker, _>(external_publisher)
192 .await
193 .map(|brightness_service| Self { brightness_service })
194 .context("connecting to brightness service")
195 .map_err(DisplayError::InitFailure)
196 }
197
198 async fn update_brightness(
199 &self,
200 info: DisplayInfo,
201 store: &DeviceStorage,
202 always_send: bool,
203 ) -> Result<Option<DisplayInfo>, DisplayError> {
204 if !info.is_finite() {
205 return Err(DisplayError::InvalidArgument("display_info", format!("{info:?}")));
206 }
207 let new_info = store
208 .write(&info)
209 .await
210 .map(|state| (UpdateState::Updated == state).then_some(info))
211 .context("updating brightness")
212 .map_err(DisplayError::WriteFailure)?;
213 if new_info.is_none() && !always_send {
214 return Ok(None);
215 }
216
217 if info.auto_brightness {
218 call!(self.brightness_service => set_auto_brightness())
219 } else {
220 call!(self.brightness_service => set_manual_brightness(info.manual_brightness_value))
221 }
222 .map(|_| new_info)
223 .map_err(|e| {
224 DisplayError::ExternalFailure("brightness_service", "set_brightness", format!("{e:?}"))
225 })
226 }
227}
228
229pub(crate) enum Request {
230 Set(SetDisplayInfo, Sender<Result<(), DisplayError>>),
231}
232
233pub struct DisplayController<T = ()> {
234 brightness_manager: T,
235 store: Rc<DeviceStorage>,
236 publisher: Option<Publisher>,
237 setting_value_publisher: SettingValuePublisher<DisplayInfo>,
238}
239
240impl<T> StorageAccess for DisplayController<T> {
241 type Storage = DeviceStorage;
242 type Data = DisplayInfo;
243 const STORAGE_KEY: &'static str = DisplayInfo::KEY;
244}
245
246impl<T> DisplayController<T>
247where
248 T: BrightnessManager + 'static,
249{
250 pub(crate) async fn new<F>(
251 service_context: &ServiceContext,
252 storage_factory: Rc<F>,
253 setting_value_publisher: SettingValuePublisher<DisplayInfo>,
254 external_publisher: ExternalEventPublisher,
255 ) -> Result<DisplayController<T>, DisplayError>
256 where
257 F: StorageFactory<Storage = DeviceStorage>,
258 {
259 let brightness_manager =
260 <T as BrightnessManager>::from_context(service_context, external_publisher).await?;
261 Ok(Self {
262 brightness_manager,
263 store: storage_factory.get_store().await,
264 publisher: None,
265 setting_value_publisher,
266 })
267 }
268
269 pub(crate) async fn restore(&self) -> Result<DisplayInfo, DisplayError> {
270 let display_info = self.store.get::<DisplayInfo>().await;
271 assert!(display_info.is_finite());
272
273 self.brightness_manager
275 .update_brightness(display_info, &self.store, true)
276 .await
277 .map(|info| info.unwrap_or(display_info))
280 }
281
282 pub(crate) async fn handle(
283 self,
284 mut request_rx: UnboundedReceiver<Request>,
285 ) -> fasync::Task<()> {
286 fasync::Task::local(async move {
287 while let Some(request) = request_rx.next().await {
288 let Request::Set(mut set_display_info, tx) = request;
289 let display_info = self.store.get::<DisplayInfo>().await;
290 assert!(display_info.is_finite());
291
292 if let Some(theme) = set_display_info.theme {
293 set_display_info.theme = self.build_theme(theme, &display_info);
294 }
295 let res = self
296 .brightness_manager
297 .update_brightness(display_info.merge(set_display_info), &self.store, false)
298 .await
299 .map(|info| {
300 if let Some(info) = info {
301 self.publish(info);
302 }
303 });
304 let _ = tx.send(res);
305 }
306 })
307 }
308
309 fn build_theme(&self, incoming_theme: Theme, display_info: &DisplayInfo) -> Option<Theme> {
310 let existing_theme_type = display_info.theme.and_then(|theme| theme.theme_type);
311 let new_theme_type = incoming_theme.theme_type.or(existing_theme_type);
312
313 ThemeBuilder::new()
314 .set_theme_type(new_theme_type)
315 .set_theme_mode(incoming_theme.theme_mode)
316 .build()
317 }
318}
319
320impl<T> DisplayController<T> {
321 pub(crate) fn register_publisher(&mut self, publisher: Publisher) {
322 self.publisher = Some(publisher);
323 }
324
325 fn publish(&self, info: DisplayInfo) {
326 let _ = self.setting_value_publisher.publish(&info);
327 if let Some(publisher) = self.publisher.as_ref() {
328 publisher.set(info);
329 }
330 }
331}
332
333#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
336pub struct DisplayInfoV1 {
337 pub manual_brightness_value: f32,
339 pub auto_brightness: bool,
340 pub low_light_mode: LowLightMode,
341}
342
343impl DisplayInfoV1 {
344 const fn new(
345 auto_brightness: bool,
346 manual_brightness_value: f32,
347 low_light_mode: LowLightMode,
348 ) -> DisplayInfoV1 {
349 DisplayInfoV1 { manual_brightness_value, auto_brightness, low_light_mode }
350 }
351}
352
353impl DeviceStorageCompatible for DisplayInfoV1 {
354 type Loader = NoneT;
355 const KEY: &'static str = "display_infoV1";
356}
357
358impl Default for DisplayInfoV1 {
359 fn default() -> Self {
360 DisplayInfoV1::new(
361 false, DEFAULT_MANUAL_BRIGHTNESS_VALUE, LowLightMode::Disable, )
365 }
366}
367
368#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
371pub struct DisplayInfoV2 {
372 pub manual_brightness_value: f32,
373 pub auto_brightness: bool,
374 pub low_light_mode: LowLightMode,
375 pub theme_mode: ThemeModeV1,
376}
377
378impl DisplayInfoV2 {
379 const fn new(
380 auto_brightness: bool,
381 manual_brightness_value: f32,
382 low_light_mode: LowLightMode,
383 theme_mode: ThemeModeV1,
384 ) -> DisplayInfoV2 {
385 DisplayInfoV2 { manual_brightness_value, auto_brightness, low_light_mode, theme_mode }
386 }
387}
388
389impl DeviceStorageCompatible for DisplayInfoV2 {
390 type Loader = NoneT;
391 const KEY: &'static str = "display_infoV2";
392
393 fn try_deserialize_from(value: &str) -> Result<Self, Error> {
394 Self::extract(value).or_else(|_| DisplayInfoV1::try_deserialize_from(value).map(Self::from))
395 }
396}
397
398impl Default for DisplayInfoV2 {
399 fn default() -> Self {
400 DisplayInfoV2::new(
401 false, DEFAULT_MANUAL_BRIGHTNESS_VALUE, LowLightMode::Disable, ThemeModeV1::Unknown, )
406 }
407}
408
409impl From<DisplayInfoV1> for DisplayInfoV2 {
410 fn from(v1: DisplayInfoV1) -> Self {
411 DisplayInfoV2 {
412 auto_brightness: v1.auto_brightness,
413 manual_brightness_value: v1.manual_brightness_value,
414 low_light_mode: v1.low_light_mode,
415 theme_mode: ThemeModeV1::Unknown,
416 }
417 }
418}
419
420#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
421pub enum ThemeModeV1 {
422 Unknown,
423 Default,
424 Light,
425 Dark,
426 Auto,
428}
429
430impl From<ThemeModeV1> for ThemeType {
431 fn from(theme_mode_v1: ThemeModeV1) -> Self {
432 match theme_mode_v1 {
433 ThemeModeV1::Default => ThemeType::Default,
434 ThemeModeV1::Light => ThemeType::Light,
435 ThemeModeV1::Dark => ThemeType::Dark,
436 ThemeModeV1::Unknown | ThemeModeV1::Auto => ThemeType::Unknown,
438 }
439 }
440}
441
442#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
443pub struct DisplayInfoV3 {
444 pub manual_brightness_value: f32,
446 pub auto_brightness: bool,
447 pub screen_enabled: bool,
448 pub low_light_mode: LowLightMode,
449 pub theme_mode: ThemeModeV1,
450}
451
452impl DisplayInfoV3 {
453 const fn new(
454 auto_brightness: bool,
455 manual_brightness_value: f32,
456 screen_enabled: bool,
457 low_light_mode: LowLightMode,
458 theme_mode: ThemeModeV1,
459 ) -> DisplayInfoV3 {
460 DisplayInfoV3 {
461 manual_brightness_value,
462 auto_brightness,
463 screen_enabled,
464 low_light_mode,
465 theme_mode,
466 }
467 }
468}
469
470impl DeviceStorageCompatible for DisplayInfoV3 {
471 type Loader = NoneT;
472 const KEY: &'static str = "display_info";
473
474 fn try_deserialize_from(value: &str) -> Result<Self, Error> {
475 Self::extract(value).or_else(|_| DisplayInfoV2::try_deserialize_from(value).map(Self::from))
476 }
477}
478
479impl Default for DisplayInfoV3 {
480 fn default() -> Self {
481 DisplayInfoV3::new(
482 false, DEFAULT_MANUAL_BRIGHTNESS_VALUE, true, LowLightMode::Disable, ThemeModeV1::Unknown, )
488 }
489}
490
491impl From<DisplayInfoV2> for DisplayInfoV3 {
492 fn from(v2: DisplayInfoV2) -> Self {
493 DisplayInfoV3 {
494 auto_brightness: v2.auto_brightness,
495 manual_brightness_value: v2.manual_brightness_value,
496 screen_enabled: true,
497 low_light_mode: v2.low_light_mode,
498 theme_mode: v2.theme_mode,
499 }
500 }
501}
502
503#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
504pub struct DisplayInfoV4 {
505 pub manual_brightness_value: f32,
507 pub auto_brightness: bool,
508 pub screen_enabled: bool,
509 pub low_light_mode: LowLightMode,
510 pub theme_type: ThemeType,
511}
512
513impl DisplayInfoV4 {
514 const fn new(
515 auto_brightness: bool,
516 manual_brightness_value: f32,
517 screen_enabled: bool,
518 low_light_mode: LowLightMode,
519 theme_type: ThemeType,
520 ) -> DisplayInfoV4 {
521 DisplayInfoV4 {
522 manual_brightness_value,
523 auto_brightness,
524 screen_enabled,
525 low_light_mode,
526 theme_type,
527 }
528 }
529}
530
531impl From<DisplayInfoV3> for DisplayInfoV4 {
532 fn from(v3: DisplayInfoV3) -> Self {
533 DisplayInfoV4 {
534 auto_brightness: v3.auto_brightness,
535 manual_brightness_value: v3.manual_brightness_value,
536 screen_enabled: v3.screen_enabled,
537 low_light_mode: v3.low_light_mode,
538 theme_type: ThemeType::from(v3.theme_mode),
541 }
542 }
543}
544
545impl DeviceStorageCompatible for DisplayInfoV4 {
546 type Loader = NoneT;
547 const KEY: &'static str = "display_info";
548
549 fn try_deserialize_from(value: &str) -> Result<Self, Error> {
550 Self::extract(value).or_else(|_| DisplayInfoV3::try_deserialize_from(value).map(Self::from))
551 }
552}
553
554impl Default for DisplayInfoV4 {
555 fn default() -> Self {
556 DisplayInfoV4::new(
557 false, DEFAULT_MANUAL_BRIGHTNESS_VALUE, true, LowLightMode::Disable, ThemeType::Unknown, )
563 }
564}
565
566#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
567#[serde(deny_unknown_fields)]
568pub struct DisplayInfoV5 {
569 pub manual_brightness_value: f32,
571 pub auto_brightness: bool,
572 pub screen_enabled: bool,
573 pub low_light_mode: LowLightMode,
574 pub theme: Option<Theme>,
575}
576
577impl DisplayInfoV5 {
578 const fn new(
579 auto_brightness: bool,
580 manual_brightness_value: f32,
581 screen_enabled: bool,
582 low_light_mode: LowLightMode,
583 theme: Option<Theme>,
584 ) -> DisplayInfoV5 {
585 DisplayInfoV5 {
586 manual_brightness_value,
587 auto_brightness,
588 screen_enabled,
589 low_light_mode,
590 theme,
591 }
592 }
593}
594
595impl From<DisplayInfoV4> for DisplayInfoV5 {
596 fn from(v4: DisplayInfoV4) -> Self {
597 DisplayInfoV5 {
598 auto_brightness: v4.auto_brightness,
599 manual_brightness_value: v4.manual_brightness_value,
600 screen_enabled: v4.screen_enabled,
601 low_light_mode: v4.low_light_mode,
602 theme: Some(Theme::new(Some(v4.theme_type), ThemeMode::empty())),
604 }
605 }
606}
607
608impl DeviceStorageCompatible for DisplayInfoV5 {
609 type Loader = NoneT;
610 const KEY: &'static str = "display_info";
611
612 fn try_deserialize_from(value: &str) -> Result<Self, Error> {
613 Self::extract(value).or_else(|_| DisplayInfoV4::try_deserialize_from(value).map(Self::from))
614 }
615}
616
617impl Default for DisplayInfoV5 {
618 fn default() -> Self {
619 DisplayInfoV5::new(
620 false, DEFAULT_MANUAL_BRIGHTNESS_VALUE, true, LowLightMode::Disable, Some(Theme::new(Some(ThemeType::Unknown), ThemeMode::empty())), )
626 }
627}
628
629#[cfg(test)]
630mod tests {
631 use super::*;
632 use crate::build_display_default_settings;
633 use crate::test_fakes::brightness_service::BrightnessService;
634 use fuchsia_async::{Task, TestExecutor};
635 use fuchsia_inspect::component;
636 use futures::channel::mpsc;
637 use futures::future;
638 use futures::lock::Mutex;
639 use settings_common::inspect::config_logger::InspectConfigLogger;
640 use settings_test_common::fakes::service::ServiceRegistry;
641 use settings_test_common::storage::InMemoryStorageFactory;
642
643 #[fuchsia::test]
644 fn test_display_migration_v1_to_v2() {
645 let v1 = DisplayInfoV1 {
646 manual_brightness_value: 0.6,
647 auto_brightness: true,
648 low_light_mode: LowLightMode::Enable,
649 };
650
651 let serialized_v1 = v1.serialize_to();
652 let v2 = DisplayInfoV2::try_deserialize_from(&serialized_v1)
653 .expect("deserialization should succeed");
654
655 assert_eq!(
656 v2,
657 DisplayInfoV2 {
658 manual_brightness_value: v1.manual_brightness_value,
659 auto_brightness: v1.auto_brightness,
660 low_light_mode: v1.low_light_mode,
661 theme_mode: DisplayInfoV2::default().theme_mode,
662 }
663 );
664 }
665
666 #[fuchsia::test]
667 fn test_display_migration_v2_to_v3() {
668 let v2 = DisplayInfoV2 {
669 manual_brightness_value: 0.7,
670 auto_brightness: true,
671 low_light_mode: LowLightMode::Enable,
672 theme_mode: ThemeModeV1::Default,
673 };
674
675 let serialized_v2 = v2.serialize_to();
676 let v3 = DisplayInfoV3::try_deserialize_from(&serialized_v2)
677 .expect("deserialization should succeed");
678
679 assert_eq!(
680 v3,
681 DisplayInfoV3 {
682 manual_brightness_value: v2.manual_brightness_value,
683 auto_brightness: v2.auto_brightness,
684 screen_enabled: DisplayInfoV3::default().screen_enabled,
685 low_light_mode: v2.low_light_mode,
686 theme_mode: v2.theme_mode,
687 }
688 );
689 }
690
691 #[fuchsia::test]
692 fn test_display_migration_v3_to_v4() {
693 let v3 = DisplayInfoV3 {
694 manual_brightness_value: 0.7,
695 auto_brightness: true,
696 low_light_mode: LowLightMode::Enable,
697 theme_mode: ThemeModeV1::Light,
698 screen_enabled: false,
699 };
700
701 let serialized_v3 = v3.serialize_to();
702 let v4 = DisplayInfoV4::try_deserialize_from(&serialized_v3)
703 .expect("deserialization should succeed");
704
705 assert_eq!(
707 v4,
708 DisplayInfoV4 {
709 manual_brightness_value: v3.manual_brightness_value,
710 auto_brightness: v3.auto_brightness,
711 low_light_mode: v3.low_light_mode,
712 theme_type: ThemeType::Light,
713 screen_enabled: v3.screen_enabled,
714 }
715 );
716 }
717
718 #[fuchsia::test]
719 fn test_display_migration_v4_to_v5() {
720 let v4 = DisplayInfoV4 {
721 manual_brightness_value: 0.7,
722 auto_brightness: true,
723 low_light_mode: LowLightMode::Enable,
724 theme_type: ThemeType::Dark,
725 screen_enabled: false,
726 };
727
728 let serialized_v4 = v4.serialize_to();
729 let v5 = DisplayInfoV5::try_deserialize_from(&serialized_v4)
730 .expect("deserialization should succeed");
731
732 assert_eq!(
733 v5,
734 DisplayInfoV5 {
735 manual_brightness_value: v4.manual_brightness_value,
736 auto_brightness: v4.auto_brightness,
737 low_light_mode: v4.low_light_mode,
738 theme: Some(Theme::new(Some(v4.theme_type), ThemeMode::empty())),
739 screen_enabled: v4.screen_enabled,
740 }
741 );
742 }
743
744 #[fuchsia::test]
745 fn test_display_migration_v1_to_current() {
746 let v1 = DisplayInfoV1 {
747 manual_brightness_value: 0.6,
748 auto_brightness: true,
749 low_light_mode: LowLightMode::Enable,
750 };
751
752 let serialized_v1 = v1.serialize_to();
753 let current = DisplayInfo::try_deserialize_from(&serialized_v1)
754 .expect("deserialization should succeed");
755
756 assert_eq!(
757 current,
758 DisplayInfo {
759 manual_brightness_value: v1.manual_brightness_value,
760 auto_brightness: v1.auto_brightness,
761 low_light_mode: v1.low_light_mode,
762 theme: Some(Theme::new(Some(ThemeType::Unknown), ThemeMode::empty())),
763 screen_enabled: DisplayInfoV3::default().screen_enabled,
765 auto_brightness_value: DEFAULT_DISPLAY_INFO.auto_brightness_value,
766 }
767 );
768 }
769
770 #[fuchsia::test]
771 fn test_display_migration_v2_to_current() {
772 let v2 = DisplayInfoV2 {
773 manual_brightness_value: 0.6,
774 auto_brightness: true,
775 low_light_mode: LowLightMode::Enable,
776 theme_mode: ThemeModeV1::Light,
777 };
778
779 let serialized_v2 = v2.serialize_to();
780 let current = DisplayInfo::try_deserialize_from(&serialized_v2)
781 .expect("deserialization should succeed");
782
783 assert_eq!(
784 current,
785 DisplayInfo {
786 manual_brightness_value: v2.manual_brightness_value,
787 auto_brightness: v2.auto_brightness,
788 low_light_mode: v2.low_light_mode,
789 theme: Some(Theme::new(Some(ThemeType::Light), ThemeMode::empty())),
790 screen_enabled: DisplayInfoV3::default().screen_enabled,
792 auto_brightness_value: DEFAULT_DISPLAY_INFO.auto_brightness_value,
793 }
794 );
795 }
796
797 #[fuchsia::test]
798 fn test_display_migration_v3_to_current() {
799 let v3 = DisplayInfoV3 {
800 manual_brightness_value: 0.6,
801 auto_brightness: true,
802 low_light_mode: LowLightMode::Enable,
803 theme_mode: ThemeModeV1::Light,
804 screen_enabled: false,
805 };
806
807 let serialized_v3 = v3.serialize_to();
808 let current = DisplayInfo::try_deserialize_from(&serialized_v3)
809 .expect("deserialization should succeed");
810
811 assert_eq!(
812 current,
813 DisplayInfo {
814 manual_brightness_value: v3.manual_brightness_value,
815 auto_brightness: v3.auto_brightness,
816 low_light_mode: v3.low_light_mode,
817 theme: Some(Theme::new(Some(ThemeType::Light), ThemeMode::empty())),
818 screen_enabled: v3.screen_enabled,
820 auto_brightness_value: DEFAULT_DISPLAY_INFO.auto_brightness_value,
821 }
822 );
823 }
824
825 #[fuchsia::test]
826 fn test_display_migration_v4_to_current() {
827 let v4 = DisplayInfoV4 {
828 manual_brightness_value: 0.6,
829 auto_brightness: true,
830 low_light_mode: LowLightMode::Enable,
831 theme_type: ThemeType::Light,
832 screen_enabled: false,
833 };
834
835 let serialized_v4 = v4.serialize_to();
836 let current = DisplayInfo::try_deserialize_from(&serialized_v4)
837 .expect("deserialization should succeed");
838
839 assert_eq!(
840 current,
841 DisplayInfo {
842 manual_brightness_value: v4.manual_brightness_value,
843 auto_brightness: v4.auto_brightness,
844 low_light_mode: v4.low_light_mode,
845 theme: Some(Theme::new(Some(ThemeType::Light), ThemeMode::empty())),
846 screen_enabled: v4.screen_enabled,
847 auto_brightness_value: DEFAULT_DISPLAY_INFO.auto_brightness_value,
848 }
849 );
850 }
851
852 #[fuchsia::test]
853 fn test_display_migration_v5_to_current() {
854 let v5 = DisplayInfoV5 {
855 manual_brightness_value: 0.6,
856 auto_brightness: true,
857 low_light_mode: LowLightMode::Enable,
858 theme: Some(Theme::new(Some(ThemeType::Light), ThemeMode::AUTO)),
859 screen_enabled: false,
860 };
861
862 let serialized_v5 = v5.serialize_to();
863 let current = DisplayInfo::try_deserialize_from(&serialized_v5)
864 .expect("deserialization should succeed");
865
866 assert_eq!(
867 current,
868 DisplayInfo {
869 manual_brightness_value: v5.manual_brightness_value,
870 auto_brightness: v5.auto_brightness,
871 low_light_mode: v5.low_light_mode,
872 theme: Some(Theme::new(Some(ThemeType::Light), ThemeMode::AUTO)),
873 screen_enabled: v5.screen_enabled,
874 auto_brightness_value: DEFAULT_DISPLAY_INFO.auto_brightness_value,
875 }
876 );
877 }
878
879 const AUTO_BRIGHTNESS_LEVEL: f32 = 0.9;
880
881 fn default_settings() -> DefaultSetting<DisplayConfiguration, &'static str> {
882 let config_logger =
883 Rc::new(std::sync::Mutex::new(InspectConfigLogger::new(component::inspector().root())));
884 build_display_default_settings(config_logger)
885 }
886
887 #[fuchsia::test(allow_stalls = false)]
889 async fn test_display_restore_with_storage_controller() {
890 validate_restore_with_storage_controller(
892 0.7,
893 AUTO_BRIGHTNESS_LEVEL,
894 true,
895 true,
896 LowLightMode::Enable,
897 None,
898 )
899 .await;
900
901 validate_restore_with_storage_controller(
903 0.9,
904 AUTO_BRIGHTNESS_LEVEL,
905 false,
906 true,
907 LowLightMode::Disable,
908 None,
909 )
910 .await;
911 }
912
913 async fn validate_restore_with_storage_controller(
914 manual_brightness: f32,
915 auto_brightness_value: f32,
916 auto_brightness: bool,
917 screen_enabled: bool,
918 low_light_mode: LowLightMode,
919 theme: Option<Theme>,
920 ) {
921 let service_registry = ServiceRegistry::create();
922 let info = DisplayInfo {
923 manual_brightness_value: manual_brightness,
924 auto_brightness_value,
925 auto_brightness,
926 screen_enabled,
927 low_light_mode,
928 theme,
929 };
930 let storage_factory = InMemoryStorageFactory::with_initial_data(&info);
931 let (tx, _) = mpsc::unbounded();
932 let setting_value_publisher = SettingValuePublisher::new(tx);
933 let (tx, _) = mpsc::unbounded();
934 let external_publisher = ExternalEventPublisher::new(tx);
935
936 storage_factory
937 .initialize_with_loader::<DisplayController, _>(DisplayInfoLoader::new(
938 default_settings(),
939 ))
940 .await
941 .expect("initializing display storage");
942
943 let display_controller = DisplayController::<()>::new(
944 &ServiceContext::new(Some(Box::new(ServiceRegistry::serve(service_registry)))),
945 Rc::new(storage_factory),
946 setting_value_publisher,
947 external_publisher,
948 )
949 .await
950 .expect("constructing display controller");
951
952 let info = display_controller.restore().await.expect("restore completed");
953 let settings = fidl_fuchsia_settings::DisplaySettings::from(info);
954
955 if auto_brightness {
956 assert_eq!(settings.auto_brightness, Some(auto_brightness));
957 assert_eq!(settings.adjusted_auto_brightness, Some(auto_brightness_value));
958 } else {
959 assert_eq!(settings.brightness_value, Some(manual_brightness));
960 }
961 }
962
963 #[fuchsia::test]
965 fn test_display_restore_with_brightness_controller() {
966 let mut exec = TestExecutor::new();
967
968 validate_restore_with_brightness_controller(
970 &mut exec,
971 0.7,
972 AUTO_BRIGHTNESS_LEVEL,
973 true,
974 true,
975 LowLightMode::Enable,
976 None,
977 );
978
979 validate_restore_with_brightness_controller(
981 &mut exec,
982 0.9,
983 AUTO_BRIGHTNESS_LEVEL,
984 false,
985 true,
986 LowLightMode::Disable,
987 None,
988 );
989 }
990
991 #[allow(clippy::float_cmp)]
993 fn validate_restore_with_brightness_controller(
994 exec: &mut TestExecutor,
995 manual_brightness: f32,
996 auto_brightness_value: f32,
997 auto_brightness: bool,
998 screen_enabled: bool,
999 low_light_mode: LowLightMode,
1000 theme: Option<Theme>,
1001 ) {
1002 let brightness_service_handle = BrightnessService::create();
1003 let brightness_service_handle_clone = brightness_service_handle.clone();
1004
1005 let _task = Task::local(async move {
1006 let service_registry = ServiceRegistry::create();
1007 service_registry
1008 .lock()
1009 .await
1010 .register_service(Rc::new(Mutex::new(brightness_service_handle_clone)));
1011 let info = DisplayInfo {
1012 manual_brightness_value: manual_brightness,
1013 auto_brightness_value,
1014 auto_brightness,
1015 screen_enabled,
1016 low_light_mode,
1017 theme,
1018 };
1019 let storage_factory = InMemoryStorageFactory::with_initial_data(&info);
1020 let (tx, _) = mpsc::unbounded();
1021 let setting_value_publisher = SettingValuePublisher::new(tx);
1022 let (tx, _) = mpsc::unbounded();
1023 let external_publisher = ExternalEventPublisher::new(tx);
1024
1025 storage_factory
1026 .initialize_with_loader::<DisplayController, _>(DisplayInfoLoader::new(
1027 default_settings(),
1028 ))
1029 .await
1030 .expect("initializing display storage");
1031
1032 let display_controller = DisplayController::<ExternalBrightnessControl>::new(
1033 &ServiceContext::new(Some(Box::new(ServiceRegistry::serve(service_registry)))),
1034 Rc::new(storage_factory),
1035 setting_value_publisher,
1036 external_publisher,
1037 )
1038 .await
1039 .expect("constructing display controller");
1040
1041 let _ = display_controller.restore().await.expect("restore completed");
1042 });
1043
1044 let _ = exec.run_until_stalled(&mut future::pending::<()>());
1045
1046 exec.run_singlethreaded(async {
1047 if auto_brightness {
1048 let service_auto_brightness =
1049 brightness_service_handle.get_auto_brightness().lock().await.unwrap();
1050 assert_eq!(service_auto_brightness, auto_brightness);
1051 } else {
1052 let service_manual_brightness =
1053 brightness_service_handle.get_manual_brightness().lock().await.unwrap();
1054 assert_eq!(service_manual_brightness, manual_brightness);
1055 }
1056 });
1057 }
1058}