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    HangingGetObserver, RequestType, ResponseType, UsagePublisher,
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    HangingGetObserver<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(info: &AccessibilityInfo, observer: SubscriberObject) -> bool {
77        let (usage_responder, responder) = observer.into_parts();
78        usage_responder.respond(format!("{info:?}"), ResponseType::OkSome);
79        if let Err(e) = responder.send(&AccessibilitySettings::from(*info)) {
80            log::warn!("Failed to respond to watch request: {e:?}");
81            return false;
82        }
83        true
84    }
85
86    pub fn handle_stream(&mut self, mut stream: AccessibilityRequestStream) {
87        let request_handler = RequestHandler {
88            subscriber: self.hanging_get.new_subscriber(),
89            controller_tx: self.controller_tx.clone(),
90            usage_publisher: self.usage_publisher.clone(),
91        };
92        fasync::Task::local(async move {
93            while let Some(Ok(request)) = stream.next().await {
94                request_handler.handle_request(request).await;
95            }
96        })
97        .detach();
98    }
99}
100
101#[derive(Debug)]
102enum HandlerError {
103    AlreadySubscribed,
104    ControllerStopped,
105    Controller(AccessibilityError),
106}
107
108impl From<&HandlerError> for ResponseType {
109    fn from(error: &HandlerError) -> Self {
110        match error {
111            HandlerError::AlreadySubscribed => ResponseType::AlreadySubscribed,
112            HandlerError::ControllerStopped => ResponseType::UnexpectedError,
113            HandlerError::Controller(e) => ResponseType::from(e),
114        }
115    }
116}
117
118struct RequestHandler {
119    subscriber: Subscriber,
120    controller_tx: UnboundedSender<Request>,
121    usage_publisher: UsagePublisher<AccessibilityInfo>,
122}
123
124impl RequestHandler {
125    async fn handle_request(&self, request: AccessibilityRequest) {
126        match request {
127            AccessibilityRequest::Watch { responder } => {
128                let usage_res = self.usage_publisher.request("Watch".to_string(), RequestType::Get);
129                let observer = HangingGetObserver::new(usage_res, responder);
130                if let Err(observer) = self.subscriber.register2(observer) {
131                    let e = HandlerError::AlreadySubscribed;
132                    let (usage_res, responder) = observer.into_parts();
133                    usage_res.respond(format!("Err({e:?})"), ResponseType::from(&e));
134                    drop(responder);
135                }
136            }
137            AccessibilityRequest::Set { settings, responder } => {
138                let usage_res = self
139                    .usage_publisher
140                    .request(format!("Set{{settings:{settings:?}}}"), RequestType::Set);
141                if let Err(e) = self.set(settings).await {
142                    usage_res.respond(format!("Err({e:?}"), ResponseType::from(&e));
143                    let _ = responder.send(Err(SettingsError::Failed));
144                } else {
145                    usage_res.respond("Ok(())".to_string(), ResponseType::OkNone);
146                    let _ = responder.send(Ok(()));
147                }
148            }
149        }
150    }
151
152    async fn set(&self, settings: AccessibilitySettings) -> Result<(), HandlerError> {
153        let (set_tx, set_rx) = oneshot::channel();
154        self.controller_tx
155            .unbounded_send(Request::Set(AccessibilityInfo::from(settings), set_tx))
156            .map_err(|_| HandlerError::ControllerStopped)?;
157        set_rx
158            .await
159            .map_err(|_| HandlerError::ControllerStopped)
160            .and_then(|res| res.map_err(HandlerError::Controller))
161    }
162}