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