settings/
lib.rs

1// Copyright 2019 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
5// TODO(https://github.com/rust-lang/rust-clippy/issues/13885) Upstream issue
6// Clippy lints on the license comment above.
7#![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
75/// A common trigger for exiting.
76pub type ExitSender = futures::channel::mpsc::UnboundedSender<()>;
77
78/// Runtime defines where the environment will exist. Service is meant for
79/// production environments and will hydrate components to be discoverable as
80/// an environment service. Nested creates a service only usable in the scope
81/// of a test.
82#[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/// Environment is handed back when an environment is spawned from the
139/// EnvironmentBuilder. A nested environment (if available) is returned,
140/// along with a receiver to be notified when initialization/setup is
141/// complete.
142#[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
173/// The [EnvironmentBuilder] aggregates the parameters surrounding an [environment](Environment) and
174/// ultimately spawns an environment based on them.
175pub 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    /// Construct a new [EnvironmentBuilder] using `storage_factory` to construct the storage for
190    /// the future [Environment].
191    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    /// A service generator to be used as an overlay on the ServiceContext.
207    pub fn service(mut self, generate_service: GenerateService) -> Self {
208        self.generate_service = Some(generate_service);
209        self
210    }
211
212    /// A preset configuration to load preset parameters as a base. Note that this will override
213    /// any configuration modifications made by [EnvironmentBuilder::fidl_interface],
214    /// [EnvironmentBuilder::policies], and [EnvironmentBuilder::flags].
215    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    /// Will override all fidl interfaces in the [ServiceConfiguration].
253    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    /// Sets the inspect node for setting proxy inspect information and any required
266    /// inspect loggers.
267    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    /// Prepares an environment so that it may be spawned. This ensures that all necessary
286    /// components are spawned and ready to handle events and FIDL requests.
287    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                        // We always respond healthy because the health check can only be served
306                        // if the environment is able to spawn which in turn guarantees that no agents
307                        // have returned an error.
308                        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    /// Spawn an [Environment] on the supplied [fasync::LocalExecutor] so that it may process
393    /// incoming FIDL requests.
394    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    /// Spawn a nested [Environment] so that it can be used for tests.
409    #[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    /// Spawns a nested environment and returns the associated
422    /// ProtocolConnector. Note that this is a helper function that provides a
423    /// shortcut for calling EnvironmentBuilder::name() and
424    /// EnvironmentBuilder::spawn().
425    #[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        // Start handlers for all components.
608        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    // Route general inspect requests to specific inspect agents.
889    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;