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