Skip to main content

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