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