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