1#![allow(clippy::literal_string_with_formatting_args)]
8
9use std::collections::HashSet;
10use std::rc::Rc;
11
12#[cfg(test)]
13use anyhow::format_err;
14use anyhow::{Context, Error};
15use fidl_fuchsia_io::DirectoryProxy;
16use fidl_fuchsia_settings::{
17 AccessibilityRequestStream, AudioRequestStream, DisplayRequestStream,
18 DoNotDisturbRequestStream, FactoryResetRequestStream, InputRequestStream, IntlRequestStream,
19 KeyboardRequestStream, LightRequestStream, NightModeRequestStream, PrivacyRequestStream,
20 SetupRequestStream,
21};
22use fidl_fuchsia_stash::StoreProxy;
23use fuchsia_component::client::connect_to_protocol;
24#[cfg(test)]
25use fuchsia_component::server::ProtocolConnector;
26use fuchsia_component::server::{ServiceFs, ServiceFsDir, ServiceObjLocal};
27use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender};
28use futures::{StreamExt, TryStreamExt};
29#[cfg(test)]
30use log as _;
31use serde::Deserialize;
32use settings_accessibility::accessibility_controller::AccessibilityController;
33use settings_audio::AudioInfoLoader;
34use settings_audio::audio_controller::{AudioController, Request as AudioRequest};
35use settings_audio::types::AudioInfo;
36use settings_common::config::default_settings::DefaultSetting;
37use settings_common::config::{AgentType, ControllerFlag};
38use settings_common::inspect::event::{
39 ExternalEventPublisher, SettingValuePublisher, UsageEvent, UsagePublisher,
40};
41use settings_common::inspect::listener_logger::ListenerInspectLogger;
42use settings_common::service_context::{ExternalServiceEvent, GenerateService, ServiceContext};
43use settings_display::display_controller::{
44 DisplayController, DisplayInfoLoader, ExternalBrightnessControl,
45};
46use settings_do_not_disturb::do_not_disturb_controller::DoNotDisturbController;
47use settings_factory_reset::factory_reset_controller::FactoryResetController;
48use settings_input::input_controller::InputController;
49use settings_intl::intl_controller::IntlController;
50use settings_keyboard::keyboard_controller::KeyboardController;
51use settings_light::light_controller::LightController;
52use settings_night_mode::night_mode_controller::NightModeController;
53use settings_privacy::privacy_controller::PrivacyController;
54use settings_setup::setup_controller::SetupController;
55use settings_storage::device_storage::DeviceStorage;
56use settings_storage::fidl_storage::FidlStorage;
57use settings_storage::storage_factory::{FidlStorageFactory, StorageFactory};
58use {fidl_fuchsia_update_verify as fupdate, fuchsia_async as fasync};
59
60pub use settings_display::display_configuration::DisplayConfiguration;
61pub use settings_input::input_device_configuration::InputConfiguration;
62pub use settings_light::light_hardware_configuration::LightHardwareConfiguration;
63
64use crate::base::SettingType;
65use crate::ingress::fidl;
66
67mod clock;
68mod storage_migrations;
69
70pub mod agent;
71pub mod base;
72pub mod ingress;
73pub(crate) mod migration;
74
75pub type ExitSender = futures::channel::mpsc::UnboundedSender<()>;
77
78#[derive(PartialEq)]
83enum Runtime {
84 Service,
85 #[cfg(test)]
86 Nested(&'static str),
87}
88
89#[derive(Debug, Default, Clone, Deserialize)]
90pub struct AgentConfiguration {
91 pub agent_types: HashSet<AgentType>,
92}
93
94#[derive(PartialEq, Debug, Clone, Deserialize)]
95pub struct EnabledInterfacesConfiguration {
96 pub interfaces: HashSet<fidl::InterfaceSpec>,
97}
98
99impl EnabledInterfacesConfiguration {
100 pub fn with_interfaces(interfaces: HashSet<fidl::InterfaceSpec>) -> Self {
101 Self { interfaces }
102 }
103}
104
105#[derive(Default, Debug, Clone, Deserialize)]
106pub struct ServiceFlags {
107 pub controller_flags: HashSet<ControllerFlag>,
108}
109
110#[derive(PartialEq, Debug, Default, Clone)]
111pub struct ServiceConfiguration {
112 agent_types: HashSet<AgentType>,
113 fidl_interfaces: HashSet<fidl::Interface>,
114 controller_flags: HashSet<ControllerFlag>,
115}
116
117impl ServiceConfiguration {
118 pub fn from(
119 agent_types: AgentConfiguration,
120 interfaces: EnabledInterfacesConfiguration,
121 flags: ServiceFlags,
122 ) -> Self {
123 let fidl_interfaces: HashSet<fidl::Interface> =
124 interfaces.interfaces.into_iter().map(|x| x.into()).collect();
125
126 Self {
127 agent_types: agent_types.agent_types,
128 fidl_interfaces,
129 controller_flags: flags.controller_flags,
130 }
131 }
132
133 fn set_fidl_interfaces(&mut self, interfaces: HashSet<fidl::Interface>) {
134 self.fidl_interfaces = interfaces;
135 }
136}
137
138#[cfg(test)]
143pub struct Environment {
144 pub connector: Option<ProtocolConnector>,
145 pub settings: HashSet<SettingType>,
146}
147
148#[cfg(test)]
149impl Environment {
150 pub fn new(
151 connector: Option<ProtocolConnector>,
152 settings: HashSet<SettingType>,
153 ) -> Environment {
154 Environment { connector, settings }
155 }
156}
157
158#[cfg(test)]
159fn init_storage_dir() -> DirectoryProxy {
160 let tempdir = tempfile::tempdir().expect("failed to create tempdir");
161 fuchsia_fs::directory::open_in_namespace(
162 tempdir.path().to_str().expect("tempdir path is not valid UTF-8"),
163 fuchsia_fs::PERM_READABLE | fuchsia_fs::PERM_WRITABLE,
164 )
165 .expect("failed to open connection to tempdir")
166}
167
168#[cfg(not(test))]
169fn init_storage_dir() -> DirectoryProxy {
170 panic!("migration dir must be specified");
171}
172
173pub struct EnvironmentBuilder<T: StorageFactory<Storage = DeviceStorage>> {
176 configuration: Option<ServiceConfiguration>,
177 storage_factory: Rc<T>,
178 generate_service: Option<GenerateService>,
179 active_listener_inspect_logger: Option<Rc<ListenerInspectLogger>>,
180 storage_dir: Option<DirectoryProxy>,
181 store_proxy: Option<StoreProxy>,
182 display_configuration: Option<DefaultSetting<DisplayConfiguration, &'static str>>,
183 audio_configuration: Option<DefaultSetting<AudioInfo, &'static str>>,
184 input_configuration: Option<DefaultSetting<InputConfiguration, &'static str>>,
185 light_configuration: Option<DefaultSetting<LightHardwareConfiguration, &'static str>>,
186}
187
188impl<T: StorageFactory<Storage = DeviceStorage> + 'static> EnvironmentBuilder<T> {
189 pub fn new(storage_factory: Rc<T>) -> Self {
192 EnvironmentBuilder {
193 configuration: None,
194 storage_factory,
195 generate_service: None,
196 active_listener_inspect_logger: None,
197 storage_dir: None,
198 store_proxy: None,
199 display_configuration: None,
200 audio_configuration: None,
201 input_configuration: None,
202 light_configuration: None,
203 }
204 }
205
206 pub fn service(mut self, generate_service: GenerateService) -> Self {
208 self.generate_service = Some(generate_service);
209 self
210 }
211
212 pub fn configuration(mut self, configuration: ServiceConfiguration) -> Self {
216 self.configuration = Some(configuration);
217 self
218 }
219
220 pub fn display_configuration(
221 mut self,
222 display_configuration: DefaultSetting<DisplayConfiguration, &'static str>,
223 ) -> Self {
224 self.display_configuration = Some(display_configuration);
225 self
226 }
227
228 pub fn audio_configuration(
229 mut self,
230 audio_configuration: DefaultSetting<AudioInfo, &'static str>,
231 ) -> Self {
232 self.audio_configuration = Some(audio_configuration);
233 self
234 }
235
236 pub fn input_configuration(
237 mut self,
238 input_configuration: DefaultSetting<InputConfiguration, &'static str>,
239 ) -> Self {
240 self.input_configuration = Some(input_configuration);
241 self
242 }
243
244 pub fn light_configuration(
245 mut self,
246 light_configuration: DefaultSetting<LightHardwareConfiguration, &'static str>,
247 ) -> Self {
248 self.light_configuration = Some(light_configuration);
249 self
250 }
251
252 pub fn fidl_interfaces(mut self, interfaces: &[fidl::Interface]) -> Self {
254 if self.configuration.is_none() {
255 self.configuration = Some(ServiceConfiguration::default());
256 }
257
258 if let Some(c) = self.configuration.as_mut() {
259 c.set_fidl_interfaces(interfaces.iter().copied().collect());
260 }
261
262 self
263 }
264
265 pub fn listener_inspect_logger(
268 mut self,
269 active_listener_inspect_logger: Rc<ListenerInspectLogger>,
270 ) -> Self {
271 self.active_listener_inspect_logger = Some(active_listener_inspect_logger);
272 self
273 }
274
275 pub fn storage_dir(mut self, storage_dir: DirectoryProxy) -> Self {
276 self.storage_dir = Some(storage_dir);
277 self
278 }
279
280 pub fn store_proxy(mut self, store_proxy: StoreProxy) -> Self {
281 self.store_proxy = Some(store_proxy);
282 self
283 }
284
285 async fn prepare_env(
288 self,
289 mut fs: ServiceFs<ServiceObjLocal<'_, ()>>,
290 runtime: Runtime,
291 ) -> Result<(ServiceFs<ServiceObjLocal<'_, ()>>, HashSet<SettingType>), Error> {
292 let mut service_dir = match runtime {
293 Runtime::Service => fs.dir("svc"),
294 #[cfg(test)]
295 Runtime::Nested(_) => fs.root_dir(),
296 };
297
298 let _ = service_dir.add_fidl_service(
299 move |mut stream: fupdate::ComponentOtaHealthCheckRequestStream| {
300 fasync::Task::local(async move {
301 while let Some(fupdate::ComponentOtaHealthCheckRequest::GetHealthStatus {
302 responder,
303 }) = stream.try_next().await.expect("error running health check service")
304 {
305 responder
309 .send(fupdate::HealthStatus::Healthy)
310 .expect("failed to send health status");
311 }
312 })
313 .detach();
314 },
315 );
316
317 let (agent_types, fidl_interfaces, flags) = match self.configuration {
318 Some(configuration) => (
319 configuration.agent_types,
320 configuration.fidl_interfaces,
321 configuration.controller_flags,
322 ),
323 _ => (HashSet::new(), HashSet::new(), HashSet::new()),
324 };
325
326 let settings: HashSet<_> = fidl_interfaces.into_iter().map(SettingType::from).collect();
327 let fidl_storage_factory = Rc::new(
328 Self::initialize_fidl_storage_factory(&settings, self.storage_dir, self.store_proxy)
329 .await
330 .context("initializing fidl storage factory")?,
331 );
332 let service_context = Rc::new(ServiceContext::new(self.generate_service));
333 let audio_info_loader = self.audio_configuration.map(AudioInfoLoader::new);
334 Self::initialize_storage(
335 &settings,
336 &fidl_storage_factory,
337 &*self.storage_factory,
338 audio_info_loader.clone(),
339 self.display_configuration.map(DisplayInfoLoader::new),
340 )
341 .await;
342
343 let (external_event_tx, external_event_rx) = mpsc::unbounded();
344 let external_publisher = ExternalEventPublisher::new(external_event_tx);
345
346 let listener_logger = self
347 .active_listener_inspect_logger
348 .unwrap_or_else(|| Rc::new(ListenerInspectLogger::new()));
349
350 let RegistrationResult {
351 camera_watcher_event_txs,
352 media_buttons_event_txs,
353 setting_value_rx,
354 usage_event_rx,
355 audio_request_tx,
356 tasks,
357 } = Self::register_controllers(
358 &settings,
359 Rc::clone(&service_context),
360 fidl_storage_factory,
361 self.storage_factory,
362 &flags,
363 audio_info_loader,
364 self.input_configuration,
365 self.light_configuration,
366 &mut service_dir,
367 Rc::clone(&listener_logger),
368 external_publisher.clone(),
369 )
370 .await;
371 for task in tasks {
372 task.detach();
373 }
374
375 let agent_result = create_agents(
376 &settings,
377 agent_types,
378 camera_watcher_event_txs,
379 media_buttons_event_txs,
380 setting_value_rx,
381 external_event_rx,
382 external_publisher,
383 usage_event_rx,
384 audio_request_tx,
385 );
386
387 run_agents(agent_result, service_context).await;
388
389 Ok((fs, settings))
390 }
391
392 pub fn spawn(
395 self,
396 mut executor: fasync::LocalExecutor,
397 fs: ServiceFs<ServiceObjLocal<'_, ()>>,
398 ) -> Result<(), Error> {
399 let (mut fs, ..) = executor
400 .run_singlethreaded(self.prepare_env(fs, Runtime::Service))
401 .context("Failed to prepare env")?;
402
403 let _ = fs.take_and_serve_directory_handle().expect("could not service directory handle");
404 executor.run_singlethreaded(fs.collect::<()>());
405 Ok(())
406 }
407
408 #[cfg(test)]
410 pub async fn spawn_nested(self, env_name: &'static str) -> Result<Environment, Error> {
411 let (mut fs, entities) = self
412 .prepare_env(ServiceFs::new_local(), Runtime::Nested(env_name))
413 .await
414 .context("Failed to prepare env")?;
415 let connector = Some(fs.create_protocol_connector()?);
416 fasync::Task::local(fs.collect()).detach();
417
418 Ok(Environment::new(connector, entities))
419 }
420
421 #[cfg(test)]
426 pub async fn spawn_and_get_protocol_connector(
427 self,
428 env_name: &'static str,
429 ) -> Result<ProtocolConnector, Error> {
430 let environment = self.spawn_nested(env_name).await?;
431
432 environment.connector.ok_or_else(|| format_err!("connector not created"))
433 }
434
435 async fn initialize_fidl_storage_factory(
436 settings: &HashSet<SettingType>,
437 storage_dir: Option<DirectoryProxy>,
438 store_proxy: Option<StoreProxy>,
439 ) -> Result<FidlStorageFactory, Error> {
440 let (migration_id, storage_dir) = if let Some(storage_dir) = storage_dir {
441 let store_proxy = store_proxy.unwrap_or_else(|| {
442 let store_proxy = connect_to_protocol::<fidl_fuchsia_stash::StoreMarker>()
443 .expect("failed to connect to stash");
444 store_proxy
445 .identify("setting_service")
446 .expect("should be able to identify to stash");
447 store_proxy
448 });
449
450 let migration_manager = storage_migrations::register_migrations(
451 settings,
452 Clone::clone(&storage_dir),
453 store_proxy,
454 )
455 .context("failed to register migrations")?;
456 let migration_id = match migration_manager.run_migrations().await {
457 Ok(id) => {
458 log::info!("migrated storage to {id:?}");
459 id
460 }
461 Err((id, e)) => {
462 log::error!("Settings migration failed: {e:?}");
463 id
464 }
465 };
466 let migration_id = migration_id.map(|migration| migration.migration_id);
467 (migration_id, storage_dir)
468 } else {
469 (None, init_storage_dir())
470 };
471 Ok(FidlStorageFactory::new(migration_id.unwrap_or(0), storage_dir))
472 }
473}
474
475struct RegistrationResult {
476 camera_watcher_event_txs: Vec<UnboundedSender<bool>>,
477 media_buttons_event_txs: Vec<UnboundedSender<settings_media_buttons::Event>>,
478 setting_value_rx: UnboundedReceiver<(&'static str, String)>,
479 usage_event_rx: UnboundedReceiver<UsageEvent>,
480 audio_request_tx: Option<UnboundedSender<AudioRequest>>,
481 tasks: Vec<fasync::Task<()>>,
482}
483
484impl<T: StorageFactory<Storage = DeviceStorage> + 'static> EnvironmentBuilder<T> {
485 async fn initialize_storage<D>(
486 components: &HashSet<SettingType>,
487 fidl_storage_factory: &FidlStorageFactory,
488 device_storage_factory: &D,
489 audio_info_loader: Option<AudioInfoLoader>,
490 display_loader: Option<DisplayInfoLoader>,
491 ) where
492 D: StorageFactory<Storage = DeviceStorage>,
493 {
494 if components.contains(&SettingType::Accessibility) {
495 device_storage_factory
496 .initialize::<AccessibilityController>()
497 .await
498 .expect("storage should still be initializing");
499 }
500
501 if components.contains(&SettingType::Audio) {
502 device_storage_factory
503 .initialize_with_loader::<AudioController, _>(
504 audio_info_loader.expect("Audio storage requires audio configuration"),
505 )
506 .await
507 .expect("storage should still be initializing");
508 }
509
510 if components.contains(&SettingType::Display) {
511 device_storage_factory
512 .initialize_with_loader::<DisplayController, _>(
513 display_loader.expect("Display storage requires display configuration"),
514 )
515 .await
516 .expect("storage should still be initializing");
517 }
518
519 if components.contains(&SettingType::DoNotDisturb) {
520 device_storage_factory
521 .initialize::<DoNotDisturbController>()
522 .await
523 .expect("storage should still be initializing");
524 }
525
526 if components.contains(&SettingType::FactoryReset) {
527 device_storage_factory
528 .initialize::<FactoryResetController>()
529 .await
530 .expect("storage should still be initializing");
531 }
532
533 if components.contains(&SettingType::Input) {
534 device_storage_factory
535 .initialize::<InputController>()
536 .await
537 .expect("storage should still be initializing");
538 }
539
540 if components.contains(&SettingType::Intl) {
541 device_storage_factory
542 .initialize::<IntlController>()
543 .await
544 .expect("storage should still be initializing");
545 }
546
547 if components.contains(&SettingType::Keyboard) {
548 device_storage_factory
549 .initialize::<KeyboardController>()
550 .await
551 .expect("storage should still be initializing");
552 }
553
554 if components.contains(&SettingType::Light) {
555 fidl_storage_factory
556 .initialize::<LightController>()
557 .await
558 .expect("storage should still be initializing");
559 }
560
561 if components.contains(&SettingType::NightMode) {
562 device_storage_factory
563 .initialize::<NightModeController>()
564 .await
565 .expect("storage should still be initializing");
566 }
567
568 if components.contains(&SettingType::Privacy) {
569 device_storage_factory
570 .initialize::<PrivacyController>()
571 .await
572 .expect("storage should still be initializing");
573 }
574
575 if components.contains(&SettingType::Setup) {
576 device_storage_factory
577 .initialize::<SetupController>()
578 .await
579 .expect("storage should still be initializing");
580 }
581 }
582
583 #[allow(clippy::too_many_arguments)]
584 async fn register_controllers<F, D>(
585 components: &HashSet<SettingType>,
586 service_context: Rc<ServiceContext>,
587 fidl_storage_factory: Rc<F>,
588 device_storage_factory: Rc<D>,
589 controller_flags: &HashSet<ControllerFlag>,
590 audio_info_loader: Option<AudioInfoLoader>,
591 input_configuration: Option<DefaultSetting<InputConfiguration, &'static str>>,
592 light_configuration: Option<DefaultSetting<LightHardwareConfiguration, &'static str>>,
593 service_dir: &mut ServiceFsDir<'_, ServiceObjLocal<'_, ()>>,
594 listener_logger: Rc<ListenerInspectLogger>,
595 external_publisher: ExternalEventPublisher,
596 ) -> RegistrationResult
597 where
598 F: StorageFactory<Storage = FidlStorage>,
599 D: StorageFactory<Storage = DeviceStorage>,
600 {
601 let (setting_value_tx, setting_value_rx) = mpsc::unbounded();
602 let (usage_event_tx, usage_event_rx) = mpsc::unbounded();
603 let mut camera_watcher_event_txs = vec![];
604 let mut media_buttons_event_txs = vec![];
605 let mut tasks = vec![];
606
607 if components.contains(&SettingType::Accessibility) {
609 let settings_accessibility::SetupResult { mut accessibility_fidl_handler, task } =
610 settings_accessibility::setup_accessibility_api(
611 Rc::clone(&device_storage_factory),
612 SettingValuePublisher::new(setting_value_tx.clone()),
613 UsagePublisher::new(usage_event_tx.clone(), Rc::clone(&listener_logger)),
614 )
615 .await;
616 tasks.push(task);
617 let _ = service_dir.add_fidl_service(move |stream: AccessibilityRequestStream| {
618 accessibility_fidl_handler.handle_stream(stream)
619 });
620 }
621
622 let audio_request_tx = if components.contains(&SettingType::Audio) {
623 let settings_audio::SetupResult {
624 mut audio_fidl_handler,
625 request_tx: audio_request_tx,
626 task,
627 } = settings_audio::setup_audio_api(
628 Rc::clone(&service_context),
629 audio_info_loader.expect("Audio controller requires audio configuration"),
630 Rc::clone(&device_storage_factory),
631 SettingValuePublisher::new(setting_value_tx.clone()),
632 UsagePublisher::new(usage_event_tx.clone(), Rc::clone(&listener_logger)),
633 external_publisher.clone(),
634 )
635 .await;
636 tasks.push(task);
637 let _ = service_dir.add_fidl_service(move |stream: AudioRequestStream| {
638 audio_fidl_handler.handle_stream(stream)
639 });
640 Some(audio_request_tx)
641 } else {
642 None
643 };
644
645 if components.contains(&SettingType::Display) {
646 let result = if controller_flags.contains(&ControllerFlag::ExternalBrightnessControl) {
647 settings_display::setup_display_api::<D, ExternalBrightnessControl>(
648 &service_context,
649 Rc::clone(&device_storage_factory),
650 SettingValuePublisher::new(setting_value_tx.clone()),
651 UsagePublisher::new(usage_event_tx.clone(), Rc::clone(&listener_logger)),
652 external_publisher.clone(),
653 )
654 .await
655 } else {
656 settings_display::setup_display_api::<D, ()>(
657 &service_context,
658 Rc::clone(&device_storage_factory),
659 SettingValuePublisher::new(setting_value_tx.clone()),
660 UsagePublisher::new(usage_event_tx.clone(), Rc::clone(&listener_logger)),
661 external_publisher.clone(),
662 )
663 .await
664 };
665 match result {
666 Ok(settings_display::SetupResult { mut display_fidl_handler, task }) => {
667 tasks.push(task);
668 let _ = service_dir.add_fidl_service(move |stream: DisplayRequestStream| {
669 display_fidl_handler.handle_stream(stream)
670 });
671 }
672 Err(e) => {
673 log::error!("Failed to setup display api: {e:?}");
674 }
675 }
676 }
677
678 if components.contains(&SettingType::DoNotDisturb) {
679 let settings_do_not_disturb::SetupResult { mut do_not_disturb_fidl_handler, task } =
680 settings_do_not_disturb::setup_do_not_disturb_api(
681 Rc::clone(&device_storage_factory),
682 SettingValuePublisher::new(setting_value_tx.clone()),
683 UsagePublisher::new(usage_event_tx.clone(), Rc::clone(&listener_logger)),
684 )
685 .await;
686 tasks.push(task);
687 let _ = service_dir.add_fidl_service(move |stream: DoNotDisturbRequestStream| {
688 do_not_disturb_fidl_handler.handle_stream(stream)
689 });
690 }
691
692 if components.contains(&SettingType::FactoryReset) {
693 match settings_factory_reset::setup_factory_reset_api(
694 &service_context,
695 Rc::clone(&device_storage_factory),
696 SettingValuePublisher::new(setting_value_tx.clone()),
697 UsagePublisher::new(usage_event_tx.clone(), Rc::clone(&listener_logger)),
698 external_publisher.clone(),
699 )
700 .await
701 {
702 Ok(settings_factory_reset::SetupResult {
703 mut factory_reset_fidl_handler,
704 task,
705 }) => {
706 tasks.push(task);
707 let _ =
708 service_dir.add_fidl_service(move |stream: FactoryResetRequestStream| {
709 factory_reset_fidl_handler.handle_stream(stream)
710 });
711 }
712 Err(e) => {
713 log::error!("Failed to setup factory reset api: {e:?}");
714 }
715 }
716 }
717
718 if components.contains(&SettingType::Input) {
719 let mut input_configuration =
720 input_configuration.expect("Input controller requires an input configuration");
721 match settings_input::setup_input_api(
722 Rc::clone(&service_context),
723 &mut input_configuration,
724 Rc::clone(&device_storage_factory),
725 SettingValuePublisher::new(setting_value_tx.clone()),
726 UsagePublisher::new(usage_event_tx.clone(), Rc::clone(&listener_logger)),
727 external_publisher.clone(),
728 )
729 .await
730 {
731 Ok(settings_input::SetupResult {
732 mut input_fidl_handler,
733 camera_watcher_event_tx,
734 media_buttons_event_tx,
735 task,
736 }) => {
737 camera_watcher_event_txs.push(camera_watcher_event_tx);
738 media_buttons_event_txs.push(media_buttons_event_tx);
739 tasks.push(task);
740 let _ = service_dir.add_fidl_service(move |stream: InputRequestStream| {
741 input_fidl_handler.handle_stream(stream)
742 });
743 }
744 Err(e) => {
745 log::error!("Failed to setup input api: {e:?}");
746 }
747 }
748 }
749
750 if components.contains(&SettingType::Light) {
751 let mut light_configuration =
752 light_configuration.expect("Light controller requires a light configuration");
753 match settings_light::setup_light_api(
754 Rc::clone(&service_context),
755 &mut light_configuration,
756 fidl_storage_factory,
757 SettingValuePublisher::new(setting_value_tx.clone()),
758 UsagePublisher::new(usage_event_tx.clone(), Rc::clone(&listener_logger)),
759 external_publisher.clone(),
760 )
761 .await
762 {
763 Ok(settings_light::SetupResult {
764 mut light_fidl_handler,
765 media_buttons_event_tx,
766 task,
767 }) => {
768 media_buttons_event_txs.push(media_buttons_event_tx);
769 tasks.push(task);
770 let _ = service_dir.add_fidl_service(move |stream: LightRequestStream| {
771 light_fidl_handler.handle_stream(stream)
772 });
773 }
774 Err(e) => {
775 log::error!("Failed to setup light api: {e:?}");
776 }
777 }
778 }
779
780 if components.contains(&SettingType::Intl) {
781 let settings_intl::SetupResult { mut intl_fidl_handler, task } =
782 settings_intl::setup_intl_api(
783 Rc::clone(&device_storage_factory),
784 SettingValuePublisher::new(setting_value_tx.clone()),
785 UsagePublisher::new(usage_event_tx.clone(), Rc::clone(&listener_logger)),
786 )
787 .await;
788 tasks.push(task);
789 let _ = service_dir.add_fidl_service(move |stream: IntlRequestStream| {
790 intl_fidl_handler.handle_stream(stream)
791 });
792 }
793
794 if components.contains(&SettingType::Keyboard) {
795 let settings_keyboard::SetupResult { mut keyboard_fidl_handler, task } =
796 settings_keyboard::setup_keyboard_api(
797 Rc::clone(&device_storage_factory),
798 SettingValuePublisher::new(setting_value_tx.clone()),
799 UsagePublisher::new(usage_event_tx.clone(), Rc::clone(&listener_logger)),
800 )
801 .await;
802 tasks.push(task);
803 let _ = service_dir.add_fidl_service(move |stream: KeyboardRequestStream| {
804 keyboard_fidl_handler.handle_stream(stream)
805 });
806 }
807
808 if components.contains(&SettingType::NightMode) {
809 let settings_night_mode::SetupResult { mut night_mode_fidl_handler, task } =
810 settings_night_mode::setup_night_mode_api(
811 Rc::clone(&device_storage_factory),
812 SettingValuePublisher::new(setting_value_tx.clone()),
813 UsagePublisher::new(usage_event_tx.clone(), Rc::clone(&listener_logger)),
814 )
815 .await;
816 tasks.push(task);
817 let _ = service_dir.add_fidl_service(move |stream: NightModeRequestStream| {
818 night_mode_fidl_handler.handle_stream(stream)
819 });
820 }
821
822 if components.contains(&SettingType::Privacy) {
823 let settings_privacy::SetupResult { mut privacy_fidl_handler, task } =
824 settings_privacy::setup_privacy_api(
825 Rc::clone(&device_storage_factory),
826 SettingValuePublisher::new(setting_value_tx.clone()),
827 UsagePublisher::new(usage_event_tx.clone(), Rc::clone(&listener_logger)),
828 )
829 .await;
830 tasks.push(task);
831 let _ = service_dir.add_fidl_service(move |stream: PrivacyRequestStream| {
832 privacy_fidl_handler.handle_stream(stream)
833 });
834 }
835
836 if components.contains(&SettingType::Setup) {
837 let settings_setup::SetupResult { mut setup_fidl_handler, task } =
838 settings_setup::setup_setup_api(
839 service_context,
840 device_storage_factory,
841 SettingValuePublisher::new(setting_value_tx),
842 UsagePublisher::new(usage_event_tx, listener_logger),
843 external_publisher,
844 )
845 .await;
846 tasks.push(task);
847 let _ = service_dir.add_fidl_service(move |stream: SetupRequestStream| {
848 setup_fidl_handler.handle_stream(stream)
849 });
850 }
851
852 RegistrationResult {
853 camera_watcher_event_txs,
854 media_buttons_event_txs,
855 setting_value_rx,
856 usage_event_rx,
857 audio_request_tx,
858 tasks,
859 }
860 }
861}
862
863struct AgentResult {
864 earcons_agent: Option<agent::earcons::agent::Agent>,
865 camera_watcher_agent: Option<settings_camera_watcher_agent::CameraWatcherAgent>,
866 media_buttons_agent: Option<settings_media_buttons_agent::MediaButtonsAgent>,
867 inspect_settings_values_agent: Option<agent::inspect::setting_values::AgentSetup>,
868 inspect_external_apis_agent: Option<agent::inspect::external_apis::ExternalApiInspectAgent>,
869 inspect_setting_proxy_agent: Option<agent::inspect::setting_proxy::SettingProxyInspectAgent>,
870 inspect_usages_agent: Option<agent::inspect::usage_counts::SettingTypeUsageInspectAgent>,
871}
872
873#[allow(clippy::too_many_arguments)]
874fn create_agents(
875 settings: &HashSet<SettingType>,
876 agent_types: HashSet<AgentType>,
877 camera_watcher_event_txs: Vec<UnboundedSender<bool>>,
878 media_buttons_event_txs: Vec<UnboundedSender<settings_media_buttons::Event>>,
879 setting_value_rx: UnboundedReceiver<(&'static str, String)>,
880 external_event_rx: UnboundedReceiver<ExternalServiceEvent>,
881 external_publisher: ExternalEventPublisher,
882 mut usage_router_rx: UnboundedReceiver<UsageEvent>,
883 audio_request_tx: Option<UnboundedSender<AudioRequest>>,
884) -> AgentResult {
885 let (proxy_event_tx, proxy_event_rx) = mpsc::unbounded();
886 let (usage_event_tx, usage_event_rx) = mpsc::unbounded();
887
888 fasync::Task::local(async move {
890 while let Some(usage_event) = usage_router_rx.next().await {
891 let _ = proxy_event_tx.unbounded_send(usage_event.clone());
892 let _ = usage_event_tx.unbounded_send(usage_event);
893 }
894 })
895 .detach();
896 let earcons_agent = agent_types
897 .contains(&AgentType::Earcons)
898 .then(|| agent::earcons::agent::Agent::new(audio_request_tx, external_publisher.clone()));
899 let camera_watcher_agent = agent_types.contains(&AgentType::CameraWatcher).then(|| {
900 settings_camera_watcher_agent::CameraWatcherAgent::new(
901 camera_watcher_event_txs,
902 external_publisher.clone(),
903 )
904 });
905 let media_buttons_agent = agent_types.contains(&AgentType::MediaButtons).then(|| {
906 settings_media_buttons_agent::MediaButtonsAgent::new(
907 media_buttons_event_txs,
908 external_publisher,
909 )
910 });
911 let inspect_settings_values_agent = agent_types
912 .contains(&AgentType::InspectSettingValues)
913 .then(|| {
914 agent::inspect::setting_values::SettingValuesInspectAgent::new(
915 settings.iter().map(|setting| format!("{setting:?}")).collect(),
916 setting_value_rx,
917 )
918 })
919 .and_then(|opt| opt);
920 let inspect_external_apis_agent = agent_types
921 .contains(&AgentType::InspectExternalApis)
922 .then(|| agent::inspect::external_apis::ExternalApiInspectAgent::new(external_event_rx));
923 let inspect_setting_proxy_agent = agent_types
924 .contains(&AgentType::InspectSettingProxy)
925 .then(|| agent::inspect::setting_proxy::SettingProxyInspectAgent::new(proxy_event_rx));
926 let inspect_usages_agent = agent_types
927 .contains(&AgentType::InspectSettingTypeUsage)
928 .then(|| agent::inspect::usage_counts::SettingTypeUsageInspectAgent::new(usage_event_rx));
929
930 AgentResult {
931 earcons_agent,
932 camera_watcher_agent,
933 media_buttons_agent,
934 inspect_settings_values_agent,
935 inspect_external_apis_agent,
936 inspect_setting_proxy_agent,
937 inspect_usages_agent,
938 }
939}
940
941async fn run_agents(agent_result: AgentResult, service_context: Rc<ServiceContext>) {
942 if let Some(earcons_agent) = agent_result.earcons_agent {
943 earcons_agent.initialize(Rc::clone(&service_context)).await;
944 }
945
946 if let Some(inspect_settings_values_agent) = agent_result.inspect_settings_values_agent {
947 inspect_settings_values_agent.initialize(
948 #[cfg(test)]
949 None,
950 );
951 }
952
953 if let Some(inspect_external_apis_agent) = agent_result.inspect_external_apis_agent {
954 inspect_external_apis_agent.initialize();
955 }
956
957 if let Some(inspect_setting_proxy_agent) = agent_result.inspect_setting_proxy_agent {
958 inspect_setting_proxy_agent.initialize();
959 }
960
961 if let Some(inspect_usages_agent) = agent_result.inspect_usages_agent {
962 inspect_usages_agent.initialize();
963 }
964
965 if let Some(camera_watcher_agent) = agent_result.camera_watcher_agent
966 && let Err(e) = camera_watcher_agent.spawn(&service_context).await
967 {
968 log::error!("Failed to spawn camera watcher agent: {e:?}");
969 }
970
971 if let Some(media_buttons_agent) = agent_result.media_buttons_agent
972 && let Err(e) = media_buttons_agent.spawn(&service_context).await
973 {
974 log::error!("Failed to spawn media buttons agent: {e:?}");
975 }
976}
977
978#[cfg(test)]
979mod tests;