settings/accessibility/
accessibility_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::AccessibilityController;
6use super::accessibility_controller::{AccessibilityError, Request};
7use crate::accessibility::types::{AccessibilityInfo, CaptionsSettings, ColorBlindnessType};
8use async_utils::hanging_get::server;
9use fidl_fuchsia_settings::{
10    AccessibilityRequest, AccessibilityRequestStream, AccessibilitySettings,
11    AccessibilityWatchResponder, Error as SettingsError,
12};
13use fuchsia_async as fasync;
14use futures::StreamExt;
15use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender};
16use futures::channel::oneshot;
17use settings_common::inspect::event::{
18    RequestType, ResponseType, UsagePublisher, UsageResponsePublisher,
19};
20
21impl From<AccessibilityInfo> for AccessibilitySettings {
22    fn from(info: AccessibilityInfo) -> Self {
23        return AccessibilitySettings {
24            audio_description: info.audio_description,
25            screen_reader: info.screen_reader,
26            color_inversion: info.color_inversion,
27            enable_magnification: info.enable_magnification,
28            color_correction: info.color_correction.map(ColorBlindnessType::into),
29            captions_settings: info.captions_settings.map(CaptionsSettings::into),
30            ..Default::default()
31        };
32    }
33}
34
35impl From<AccessibilitySettings> for AccessibilityInfo {
36    fn from(settings: AccessibilitySettings) -> Self {
37        AccessibilityInfo {
38            audio_description: settings.audio_description,
39            screen_reader: settings.screen_reader,
40            color_inversion: settings.color_inversion,
41            enable_magnification: settings.enable_magnification,
42            color_correction: settings
43                .color_correction
44                .map(fidl_fuchsia_settings::ColorBlindnessType::into),
45            captions_settings: settings
46                .captions_settings
47                .map(fidl_fuchsia_settings::CaptionsSettings::into),
48        }
49    }
50}
51
52pub(crate) type SubscriberObject =
53    (UsageResponsePublisher<AccessibilityInfo>, AccessibilityWatchResponder);
54type HangingGetFn = fn(&AccessibilityInfo, SubscriberObject) -> bool;
55pub(crate) type HangingGet = server::HangingGet<AccessibilityInfo, SubscriberObject, HangingGetFn>;
56pub(crate) type Publisher = server::Publisher<AccessibilityInfo, SubscriberObject, HangingGetFn>;
57pub(crate) type Subscriber = server::Subscriber<AccessibilityInfo, SubscriberObject, HangingGetFn>;
58
59pub struct AccessibilityFidlHandler {
60    hanging_get: HangingGet,
61    controller_tx: UnboundedSender<Request>,
62    usage_publisher: UsagePublisher<AccessibilityInfo>,
63}
64
65impl AccessibilityFidlHandler {
66    pub(crate) fn new(
67        accessibility_controller: &mut AccessibilityController,
68        usage_publisher: UsagePublisher<AccessibilityInfo>,
69        initial_value: AccessibilityInfo,
70    ) -> (Self, UnboundedReceiver<Request>) {
71        let hanging_get = HangingGet::new(initial_value, Self::hanging_get);
72        accessibility_controller.register_publisher(hanging_get.new_publisher());
73        let (controller_tx, controller_rx) = mpsc::unbounded();
74        (Self { hanging_get, controller_tx, usage_publisher }, controller_rx)
75    }
76
77    fn hanging_get(
78        info: &AccessibilityInfo,
79        (usage_responder, responder): SubscriberObject,
80    ) -> bool {
81        usage_responder.respond(format!("{info:?}"), ResponseType::OkSome);
82        if let Err(e) = responder.send(&AccessibilitySettings::from(*info)) {
83            log::warn!("Failed to respond to watch request: {e:?}");
84            return false;
85        }
86        true
87    }
88
89    pub fn handle_stream(&mut self, mut stream: AccessibilityRequestStream) {
90        let request_handler = RequestHandler {
91            subscriber: self.hanging_get.new_subscriber(),
92            controller_tx: self.controller_tx.clone(),
93            usage_publisher: self.usage_publisher.clone(),
94        };
95        fasync::Task::local(async move {
96            while let Some(Ok(request)) = stream.next().await {
97                request_handler.handle_request(request).await;
98            }
99        })
100        .detach();
101    }
102}
103
104#[derive(Debug)]
105enum HandlerError {
106    AlreadySubscribed,
107    ControllerStopped,
108    Controller(AccessibilityError),
109}
110
111impl From<&HandlerError> for ResponseType {
112    fn from(error: &HandlerError) -> Self {
113        match error {
114            HandlerError::AlreadySubscribed => ResponseType::AlreadySubscribed,
115            HandlerError::ControllerStopped => ResponseType::UnexpectedError,
116            HandlerError::Controller(e) => ResponseType::from(e),
117        }
118    }
119}
120
121struct RequestHandler {
122    subscriber: Subscriber,
123    controller_tx: UnboundedSender<Request>,
124    usage_publisher: UsagePublisher<AccessibilityInfo>,
125}
126
127impl RequestHandler {
128    async fn handle_request(&self, request: AccessibilityRequest) {
129        match request {
130            AccessibilityRequest::Watch { responder } => {
131                let usage_res = self.usage_publisher.request("Watch".to_string(), RequestType::Get);
132                if let Err((usage_res, responder)) =
133                    self.subscriber.register2((usage_res, responder))
134                {
135                    let e = HandlerError::AlreadySubscribed;
136                    usage_res.respond(format!("Err({e:?})"), ResponseType::from(&e));
137                    drop(responder);
138                }
139            }
140            AccessibilityRequest::Set { settings, responder } => {
141                let usage_res = self
142                    .usage_publisher
143                    .request(format!("Set{{settings:{settings:?}}}"), RequestType::Set);
144                if let Err(e) = self.set(settings).await {
145                    usage_res.respond(format!("Err({e:?}"), ResponseType::from(&e));
146                    let _ = responder.send(Err(SettingsError::Failed));
147                } else {
148                    usage_res.respond("Ok(())".to_string(), ResponseType::OkNone);
149                    let _ = responder.send(Ok(()));
150                }
151            }
152        }
153    }
154
155    async fn set(&self, settings: AccessibilitySettings) -> Result<(), HandlerError> {
156        let (set_tx, set_rx) = oneshot::channel();
157        self.controller_tx
158            .unbounded_send(Request::Set(AccessibilityInfo::from(settings), set_tx))
159            .map_err(|_| HandlerError::ControllerStopped)?;
160        set_rx
161            .await
162            .map_err(|_| HandlerError::ControllerStopped)
163            .and_then(|res| res.map_err(HandlerError::Controller))
164    }
165}