settings/display/
display_fidl_handler.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::DisplayController;
6use super::display_controller::Request;
7use super::types::DisplayInfo;
8use crate::display::types::{LowLightMode, SetDisplayInfo, Theme, ThemeMode, ThemeType};
9use crate::handler::setting_handler::ControllerError;
10use anyhow::{Error, anyhow};
11use async_utils::hanging_get::server;
12use fidl_fuchsia_settings::{
13    DisplayRequest, DisplayRequestStream, DisplaySettings, DisplayWatchResponder,
14    Error as SettingsError, LowLightMode as FidlLowLightMode, Theme as FidlTheme,
15    ThemeMode as FidlThemeMode, ThemeType as FidlThemeType,
16};
17use fuchsia_async as fasync;
18use futures::StreamExt;
19use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender};
20use futures::channel::oneshot;
21use settings_common::inspect::event::{
22    RequestType, ResponseType, UsagePublisher, UsageResponsePublisher,
23};
24
25impl From<FidlThemeMode> for ThemeMode {
26    fn from(fidl: FidlThemeMode) -> Self {
27        ThemeMode::from_bits(FidlThemeMode::bits(&fidl))
28            .expect("failed to convert FidlThemeMode to ThemeMode")
29    }
30}
31
32impl From<ThemeMode> for FidlThemeMode {
33    fn from(fidl: ThemeMode) -> Self {
34        FidlThemeMode::from_bits(ThemeMode::bits(&fidl))
35            .expect("failed to convert ThemeMode to FidlThemeMode")
36    }
37}
38
39impl From<FidlLowLightMode> for LowLightMode {
40    fn from(fidl_low_light_mode: FidlLowLightMode) -> Self {
41        match fidl_low_light_mode {
42            FidlLowLightMode::Disable => LowLightMode::Disable,
43            FidlLowLightMode::DisableImmediately => LowLightMode::DisableImmediately,
44            FidlLowLightMode::Enable => LowLightMode::Enable,
45        }
46    }
47}
48
49impl From<FidlThemeType> for ThemeType {
50    fn from(fidl_theme_type: FidlThemeType) -> Self {
51        match fidl_theme_type {
52            FidlThemeType::Default => ThemeType::Default,
53            FidlThemeType::Light => ThemeType::Light,
54            FidlThemeType::Dark => ThemeType::Dark,
55        }
56    }
57}
58
59impl From<FidlTheme> for Theme {
60    fn from(fidl_theme: FidlTheme) -> Self {
61        Self {
62            theme_type: fidl_theme.theme_type.map(Into::into),
63            theme_mode: fidl_theme.theme_mode.map(Into::into).unwrap_or_else(ThemeMode::empty),
64        }
65    }
66}
67
68impl From<DisplayInfo> for DisplaySettings {
69    fn from(info: DisplayInfo) -> Self {
70        fidl_fuchsia_settings::DisplaySettings {
71            auto_brightness: Some(info.auto_brightness),
72            adjusted_auto_brightness: Some(info.auto_brightness_value),
73            brightness_value: Some(info.manual_brightness_value),
74            screen_enabled: Some(info.screen_enabled),
75            low_light_mode: Some(match info.low_light_mode {
76                LowLightMode::Enable => FidlLowLightMode::Enable,
77                LowLightMode::Disable => FidlLowLightMode::Disable,
78                LowLightMode::DisableImmediately => FidlLowLightMode::DisableImmediately,
79            }),
80            theme: Some(FidlTheme {
81                theme_type: match info.theme {
82                    Some(Theme { theme_type: Some(theme_type), .. }) => match theme_type {
83                        ThemeType::Unknown => None,
84                        ThemeType::Default => Some(FidlThemeType::Default),
85                        ThemeType::Light => Some(FidlThemeType::Light),
86                        ThemeType::Dark => Some(FidlThemeType::Dark),
87                    },
88                    _ => None,
89                },
90                theme_mode: match info.theme {
91                    Some(Theme { theme_mode, .. }) if !theme_mode.is_empty() => {
92                        Some(FidlThemeMode::from(theme_mode))
93                    }
94                    _ => None,
95                },
96                ..Default::default()
97            }),
98            ..Default::default()
99        }
100    }
101}
102
103fn to_request(settings: DisplaySettings) -> Result<SetDisplayInfo, Error> {
104    let set_display_info = SetDisplayInfo {
105        manual_brightness_value: settings.brightness_value,
106        auto_brightness_value: settings.adjusted_auto_brightness,
107        auto_brightness: settings.auto_brightness,
108        screen_enabled: settings.screen_enabled,
109        low_light_mode: settings.low_light_mode.map(Into::into),
110        theme: settings.theme.map(Into::into),
111    };
112    match set_display_info {
113        // No values being set is invalid
114        SetDisplayInfo {
115            manual_brightness_value: None,
116            auto_brightness_value: None,
117            auto_brightness: None,
118            screen_enabled: None,
119            low_light_mode: None,
120            theme: None,
121        } => Err(anyhow!("No values set")),
122        _ => Ok(set_display_info),
123    }
124}
125
126pub(crate) type SubscriberObject = (UsageResponsePublisher<DisplayInfo>, DisplayWatchResponder);
127type HangingGetFn = fn(&DisplayInfo, SubscriberObject) -> bool;
128pub(crate) type HangingGet = server::HangingGet<DisplayInfo, SubscriberObject, HangingGetFn>;
129pub(crate) type Publisher = server::Publisher<DisplayInfo, SubscriberObject, HangingGetFn>;
130pub(crate) type Subscriber = server::Subscriber<DisplayInfo, SubscriberObject, HangingGetFn>;
131
132pub struct DisplayFidlHandler {
133    hanging_get: HangingGet,
134    controller_tx: UnboundedSender<Request>,
135    usage_publisher: UsagePublisher<DisplayInfo>,
136}
137
138impl DisplayFidlHandler {
139    pub(crate) fn new<T>(
140        display_controller: &mut DisplayController<T>,
141        usage_publisher: UsagePublisher<DisplayInfo>,
142        initial_value: DisplayInfo,
143    ) -> (Self, UnboundedReceiver<Request>) {
144        let hanging_get = HangingGet::new(initial_value, Self::hanging_get);
145        display_controller.register_publisher(hanging_get.new_publisher());
146        let (controller_tx, controller_rx) = mpsc::unbounded();
147        (Self { hanging_get, controller_tx, usage_publisher }, controller_rx)
148    }
149
150    fn hanging_get(info: &DisplayInfo, (usage_responder, responder): SubscriberObject) -> bool {
151        usage_responder.respond(format!("{info:?}"), ResponseType::OkSome);
152        if let Err(e) = responder.send(&DisplaySettings::from(*info)) {
153            log::warn!("Failed to respond to watch request: {e:?}");
154            return false;
155        }
156        true
157    }
158
159    pub fn handle_stream(&mut self, mut stream: DisplayRequestStream) {
160        let request_handler = RequestHandler {
161            subscriber: self.hanging_get.new_subscriber(),
162            controller_tx: self.controller_tx.clone(),
163            usage_publisher: self.usage_publisher.clone(),
164        };
165        fasync::Task::local(async move {
166            while let Some(Ok(request)) = stream.next().await {
167                request_handler.handle_request(request).await;
168            }
169        })
170        .detach();
171    }
172}
173
174#[derive(Debug)]
175enum HandlerError {
176    AlreadySubscribed,
177    InvalidArgument(
178        // Error used by Debug impl for inspect logs.
179        #[allow(dead_code)] Error,
180    ),
181    ControllerStopped,
182    Controller(ControllerError),
183}
184
185impl From<&HandlerError> for ResponseType {
186    fn from(error: &HandlerError) -> Self {
187        match error {
188            HandlerError::AlreadySubscribed => ResponseType::AlreadySubscribed,
189            HandlerError::InvalidArgument(_) => ResponseType::InvalidArgument,
190            HandlerError::ControllerStopped => ResponseType::UnexpectedError,
191            HandlerError::Controller(e) => ResponseType::from(e.clone()),
192        }
193    }
194}
195
196struct RequestHandler {
197    subscriber: Subscriber,
198    controller_tx: UnboundedSender<Request>,
199    usage_publisher: UsagePublisher<DisplayInfo>,
200}
201
202impl RequestHandler {
203    async fn handle_request(&self, request: DisplayRequest) {
204        match request {
205            DisplayRequest::Watch { responder } => {
206                let usage_res = self.usage_publisher.request("Watch".to_string(), RequestType::Get);
207                if let Err((usage_res, responder)) =
208                    self.subscriber.register2((usage_res, responder))
209                {
210                    let e = HandlerError::AlreadySubscribed;
211                    usage_res.respond(format!("Err({e:?})"), ResponseType::from(&e));
212                    drop(responder);
213                }
214            }
215            DisplayRequest::Set { settings, responder } => {
216                let usage_res = self
217                    .usage_publisher
218                    .request(format!("Set{{settings:{settings:?}}}"), RequestType::Set);
219                if let Err(e) = self.set(settings).await {
220                    usage_res.respond(format!("Err({e:?}"), ResponseType::from(&e));
221                    let _ = responder.send(Err(SettingsError::Failed));
222                } else {
223                    usage_res.respond("Ok(())".to_string(), ResponseType::OkNone);
224                    let _ = responder.send(Ok(()));
225                }
226            }
227        }
228    }
229
230    async fn set(&self, settings: DisplaySettings) -> Result<(), HandlerError> {
231        let (set_tx, set_rx) = oneshot::channel();
232        let info = to_request(settings).map_err(|e| HandlerError::InvalidArgument(e))?;
233        self.controller_tx
234            .unbounded_send(Request::Set(info, set_tx))
235            .map_err(|_| HandlerError::ControllerStopped)?;
236        set_rx
237            .await
238            .map_err(|_| HandlerError::ControllerStopped)
239            .and_then(|res| res.map_err(HandlerError::Controller))
240    }
241}