settings/ingress/
fidl.rs

1// Copyright 2021 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::base::{Dependency, Entity, SettingType};
6use crate::handler::base::Error;
7use crate::ingress::registration::{Registrant, Registrar};
8use crate::job::source::Seeder;
9use fidl_fuchsia_settings::{
10    AccessibilityRequestStream, AudioRequestStream, DisplayRequestStream,
11    DoNotDisturbRequestStream, FactoryResetRequestStream, InputRequestStream,
12};
13use fuchsia_component::server::{ServiceFsDir, ServiceObjLocal};
14use serde::Deserialize;
15
16impl From<Error> for zx::Status {
17    fn from(error: Error) -> zx::Status {
18        match error {
19            Error::UnhandledType(_) => zx::Status::UNAVAILABLE,
20            _ => zx::Status::INTERNAL,
21        }
22    }
23}
24/// [Interface] defines the FIDL interfaces supported by the settings service.
25#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
26pub enum Interface {
27    Accessibility,
28    Audio,
29    Display(display::InterfaceFlags),
30    DoNotDisturb,
31    FactoryReset,
32    Input,
33    Intl,
34    Keyboard,
35    Light,
36    NightMode,
37    Privacy,
38    Setup,
39}
40
41/// [InterfaceSpec] is the serializable type that defines the configuration for FIDL interfaces
42/// supported by the settings service. It's read in from configuration files to modify what
43/// interfaces the settings service provides.
44#[derive(Clone, Deserialize, PartialEq, Eq, Hash, Debug)]
45pub enum InterfaceSpec {
46    Accessibility,
47    Audio,
48    // Should ideally be a HashSet, but HashSet does not impl Hash.
49    Display(Vec<display::InterfaceSpec>),
50    DoNotDisturb,
51    FactoryReset,
52    Input,
53    Intl,
54    Keyboard,
55    Light,
56    NightMode,
57    Privacy,
58    Setup,
59}
60
61impl From<InterfaceSpec> for Interface {
62    fn from(spec: InterfaceSpec) -> Self {
63        match spec {
64            InterfaceSpec::Audio => Interface::Audio,
65            InterfaceSpec::Accessibility => Interface::Accessibility,
66            InterfaceSpec::Display(variants) => Interface::Display(variants.into()),
67            InterfaceSpec::DoNotDisturb => Interface::DoNotDisturb,
68            InterfaceSpec::FactoryReset => Interface::FactoryReset,
69            InterfaceSpec::Input => Interface::Input,
70            InterfaceSpec::Intl => Interface::Intl,
71            InterfaceSpec::Keyboard => Interface::Keyboard,
72            InterfaceSpec::Light => Interface::Light,
73            InterfaceSpec::NightMode => Interface::NightMode,
74            InterfaceSpec::Privacy => Interface::Privacy,
75            InterfaceSpec::Setup => Interface::Setup,
76        }
77    }
78}
79
80pub mod display {
81    use bitflags::bitflags;
82    use serde::Deserialize;
83
84    bitflags! {
85        /// The Display interface covers a number of feature spaces, each handled by a different
86        /// entity dependency. The flags below allow the scope of these features to be specified by
87        /// the interface.
88        #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
89        pub struct InterfaceFlags: u64 {
90            const BASE = 1 << 0;
91        }
92    }
93
94    #[derive(Copy, Clone, Deserialize, PartialEq, Eq, Hash, Debug)]
95    pub enum InterfaceSpec {
96        Base,
97    }
98
99    impl From<Vec<InterfaceSpec>> for InterfaceFlags {
100        fn from(variants: Vec<InterfaceSpec>) -> Self {
101            variants.into_iter().fold(InterfaceFlags::empty(), |flags, variant| {
102                flags
103                    | match variant {
104                        InterfaceSpec::Base => InterfaceFlags::BASE,
105                    }
106            })
107        }
108    }
109}
110
111/// [Register] defines the closure implemented for interfaces to bring up support. Each interface
112/// handler is given access to the MessageHub [Delegate] for communication within the service and
113/// [ServiceFsDir] to register as the designated handler for the interface.
114pub(crate) type Register =
115    Box<dyn for<'a> FnOnce(&Seeder, &mut ServiceFsDir<'_, ServiceObjLocal<'a, ()>>)>;
116
117impl Interface {
118    /// Returns the list of [Dependencies](Dependency) that are necessary to provide this Interface.
119    fn dependencies(self) -> Vec<Dependency> {
120        match self {
121            Interface::Accessibility => {
122                vec![Dependency::Entity(Entity::Handler(SettingType::Accessibility))]
123            }
124            Interface::Audio => {
125                vec![Dependency::Entity(Entity::Handler(SettingType::Audio))]
126            }
127            Interface::Display(interfaces) => {
128                let mut dependencies = Vec::new();
129
130                if interfaces.contains(display::InterfaceFlags::BASE) {
131                    dependencies.push(Dependency::Entity(Entity::Handler(SettingType::Display)));
132                }
133
134                if dependencies.is_empty() {
135                    panic!("A valid interface flag must be specified with Interface::Display");
136                }
137
138                dependencies
139            }
140            Interface::DoNotDisturb => {
141                vec![Dependency::Entity(Entity::Handler(SettingType::DoNotDisturb))]
142            }
143            Interface::FactoryReset => {
144                vec![Dependency::Entity(Entity::Handler(SettingType::FactoryReset))]
145            }
146            Interface::Input => {
147                vec![Dependency::Entity(Entity::Handler(SettingType::Input))]
148            }
149            Interface::Intl => {
150                vec![Dependency::Entity(Entity::Handler(SettingType::Intl))]
151            }
152            Interface::Keyboard => {
153                vec![Dependency::Entity(Entity::Handler(SettingType::Keyboard))]
154            }
155            Interface::Light => {
156                vec![Dependency::Entity(Entity::Handler(SettingType::Light))]
157            }
158            Interface::NightMode => {
159                vec![Dependency::Entity(Entity::Handler(SettingType::NightMode))]
160            }
161            Interface::Privacy => {
162                vec![Dependency::Entity(Entity::Handler(SettingType::Privacy))]
163            }
164            Interface::Setup => {
165                vec![Dependency::Entity(Entity::Handler(SettingType::Setup))]
166            }
167        }
168    }
169
170    /// Converts an [Interface] into the closure to bring up the interface in the service environment
171    /// as defined by [Register].
172    fn registration_fn(self) -> Register {
173        Box::new(
174            move |seeder: &Seeder, service_dir: &mut ServiceFsDir<'_, ServiceObjLocal<'_, ()>>| {
175                match self {
176                    Interface::Audio => {
177                        let seeder = seeder.clone();
178                        let _ = service_dir.add_fidl_service(move |stream: AudioRequestStream| {
179                            seeder.seed(stream);
180                        });
181                    }
182                    Interface::Accessibility => {
183                        let seeder = seeder.clone();
184                        let _ = service_dir.add_fidl_service(
185                            move |stream: AccessibilityRequestStream| {
186                                seeder.seed(stream);
187                            },
188                        );
189                    }
190                    Interface::Display(_) => {
191                        let seeder = seeder.clone();
192                        let _ =
193                            service_dir.add_fidl_service(move |stream: DisplayRequestStream| {
194                                seeder.seed(stream);
195                            });
196                    }
197                    Interface::DoNotDisturb => {
198                        let seeder = seeder.clone();
199                        let _ = service_dir.add_fidl_service(
200                            move |stream: DoNotDisturbRequestStream| {
201                                seeder.seed(stream);
202                            },
203                        );
204                    }
205                    Interface::FactoryReset => {
206                        let seeder = seeder.clone();
207                        let _ = service_dir.add_fidl_service(
208                            move |stream: FactoryResetRequestStream| {
209                                seeder.seed(stream);
210                            },
211                        );
212                    }
213                    Interface::Input => {
214                        let seeder = seeder.clone();
215                        let _ = service_dir.add_fidl_service(move |stream: InputRequestStream| {
216                            seeder.seed(stream);
217                        });
218                    }
219                    Interface::Intl => {}      // Handled in lib.rs
220                    Interface::Keyboard => {}  // Handled in lib.rs
221                    Interface::Light => {}     // Handled in lib.rs
222                    Interface::NightMode => {} // Handled in lib.rs
223                    Interface::Privacy => {}   // Handled in lib.rs
224                    Interface::Setup => {}     // Handled in lib.rs
225                }
226            },
227        )
228    }
229
230    /// Derives a [Registrant] from this [Interface]. This is used convert a list of Interfaces
231    /// specified in a configuration into actionable Registrants that can be used in the setting
232    /// service.
233    pub(crate) fn registrant(self) -> Registrant {
234        Registrant::new(
235            format!("{self:?}"),
236            Registrar::Fidl(self.registration_fn()),
237            self.dependencies().into_iter().collect(),
238        )
239    }
240}
241
242#[cfg(test)]
243mod tests {
244    use fidl_fuchsia_settings::AccessibilityMarker;
245    use fuchsia_async as fasync;
246    use fuchsia_component::server::ServiceFs;
247    use futures::StreamExt;
248
249    use assert_matches::assert_matches;
250
251    use crate::base::{Dependency, Entity, SettingType};
252    use crate::handler::base::{Payload, Request};
253    use crate::ingress::registration::Registrant;
254    use crate::job::manager::Manager;
255    use crate::job::source::Seeder;
256    use crate::message::base::MessengerType;
257    use crate::service;
258
259    use super::Interface;
260
261    #[fuchsia::test(allow_stalls = false)]
262    async fn test_fidl_seeder_bringup() {
263        let mut fs = ServiceFs::new_local();
264        let delegate = service::MessageHub::create_hub();
265        let job_manager_signature = Manager::spawn(&delegate).await;
266        let job_seeder = Seeder::new(&delegate, job_manager_signature).await;
267
268        // Using privacy since it uses a seeder for its fidl registration.
269        let setting_type = SettingType::Accessibility;
270
271        let registrant: Registrant = Interface::Accessibility.registrant();
272
273        // Verify dependencies properly derived from the interface.
274        assert!(
275            registrant
276                .get_dependencies()
277                .contains(&Dependency::Entity(Entity::Handler(setting_type)))
278        );
279
280        // Create handler to intercept messages.
281        let mut rx = delegate
282            .create(MessengerType::Addressable(service::Address::Handler(setting_type)))
283            .await
284            .expect("messenger should be created")
285            .1;
286
287        // Register and consume Registrant.
288        registrant.register(&job_seeder, &mut fs.root_dir());
289
290        // Spawn nested environment.
291        let connector = fs.create_protocol_connector().expect("should create connector");
292        fasync::Task::local(fs.collect()).detach();
293
294        // Connect to the Accessibility interface and request watching.
295        let privacy_proxy = connector
296            .connect_to_protocol::<AccessibilityMarker>()
297            .expect("should connect to protocol");
298        fasync::Task::local(async move {
299            let _ = privacy_proxy.watch().await;
300        })
301        .detach();
302
303        // Ensure handler receives request.
304        assert_matches!(rx.next_of::<Payload>().await, Ok((Payload::Request(Request::Listen), _)));
305    }
306}