1use 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#[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#[derive(Clone, Deserialize, PartialEq, Eq, Hash, Debug)]
45pub enum InterfaceSpec {
46 Accessibility,
47 Audio,
48 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 #[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
111pub(crate) type Register =
115 Box<dyn for<'a> FnOnce(&Seeder, &mut ServiceFsDir<'_, ServiceObjLocal<'a, ()>>)>;
116
117impl Interface {
118 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 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 => {} Interface::Keyboard => {} Interface::Light => {} Interface::NightMode => {} Interface::Privacy => {} Interface::Setup => {} }
226 },
227 )
228 }
229
230 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 let setting_type = SettingType::Accessibility;
270
271 let registrant: Registrant = Interface::Accessibility.registrant();
272
273 assert!(
275 registrant
276 .get_dependencies()
277 .contains(&Dependency::Entity(Entity::Handler(setting_type)))
278 );
279
280 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 registrant.register(&job_seeder, &mut fs.root_dir());
289
290 let connector = fs.create_protocol_connector().expect("should create connector");
292 fasync::Task::local(fs.collect()).detach();
293
294 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 assert_matches!(rx.next_of::<Payload>().await, Ok((Payload::Request(Request::Listen), _)));
305 }
306}