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