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
5use std::collections::{HashMap, HashSet};
6use std::rc::Rc;
7use std::sync::atomic::AtomicU64;
8
9#[cfg(test)]
10use anyhow::format_err;
11use anyhow::{Context, Error};
12use audio::types::AudioInfo;
13use audio::AudioInfoLoader;
14use display::display_controller::DisplayInfoLoader;
15use fidl_fuchsia_io::DirectoryProxy;
16use fidl_fuchsia_settings::LightRequestStream;
17use fidl_fuchsia_stash::StoreProxy;
18use fuchsia_component::client::connect_to_protocol;
19#[cfg(test)]
20use fuchsia_component::server::ProtocolConnector;
21use fuchsia_component::server::{ServiceFs, ServiceFsDir, ServiceObjLocal};
22use fuchsia_inspect::component;
23use futures::channel::mpsc::{self, UnboundedReceiver, UnboundedSender};
24use futures::lock::Mutex;
25use futures::{StreamExt, TryStreamExt};
26#[cfg(test)]
27use log as _;
28use settings_common::config::default_settings::DefaultSetting;
29use settings_common::config::{AgentType, ControllerFlag};
30use settings_common::inspect::event::{
31    ExternalEventPublisher, SettingValuePublisher, UsageEvent, UsagePublisher,
32};
33use settings_common::inspect::listener_logger::ListenerInspectLogger;
34use settings_common::service_context::{
35    ExternalServiceEvent, GenerateService, ServiceContext as CommonServiceContext,
36};
37use settings_light::light_controller::LightController;
38use settings_storage::device_storage::DeviceStorage;
39use settings_storage::fidl_storage::FidlStorage;
40use settings_storage::storage_factory::{FidlStorageFactory, StorageFactory};
41use zx::MonotonicDuration;
42use {fidl_fuchsia_update_verify as fupdate, fuchsia_async as fasync};
43
44pub use display::display_configuration::DisplayConfiguration;
45pub use handler::setting_proxy_inspect_info::SettingProxyInspectInfo;
46pub use input::input_device_configuration::InputConfiguration;
47use serde::Deserialize;
48pub use service::{Address, Payload};
49pub use settings_light::light_hardware_configuration::LightHardwareConfiguration;
50
51use crate::accessibility::accessibility_controller::AccessibilityController;
52use crate::agent::authority::Authority;
53use crate::agent::{AgentCreator, Lifespan};
54use crate::audio::audio_controller::AudioController;
55use crate::base::{Dependency, Entity, SettingType};
56use crate::display::display_controller::{DisplayController, ExternalBrightnessControl};
57use crate::do_not_disturb::do_not_disturb_controller::DoNotDisturbController;
58use crate::factory_reset::factory_reset_controller::FactoryResetController;
59use crate::handler::base::GenerateHandler;
60use crate::handler::setting_handler::persist::Handler as DataHandler;
61use crate::handler::setting_handler_factory_impl::SettingHandlerFactoryImpl;
62use crate::handler::setting_proxy::SettingProxy;
63use crate::ingress::fidl;
64use crate::ingress::registration::Registrant;
65use crate::input::input_controller::InputController;
66use crate::intl::intl_controller::IntlController;
67use crate::job::manager::Manager;
68use crate::job::source::Seeder;
69use crate::keyboard::keyboard_controller::KeyboardController;
70use crate::night_mode::night_mode_controller::NightModeController;
71use crate::privacy::privacy_controller::PrivacyController;
72use crate::service::message::Delegate;
73use crate::service_context::ServiceContext;
74use crate::setup::setup_controller::SetupController;
75
76mod accessibility;
77pub mod audio;
78mod clock;
79pub mod display;
80mod do_not_disturb;
81mod event;
82mod factory_reset;
83pub mod input;
84mod intl;
85mod job;
86mod keyboard;
87mod night_mode;
88mod privacy;
89mod service;
90mod setup;
91mod storage_migrations;
92
93pub mod agent;
94pub mod base;
95pub mod config;
96pub mod handler;
97pub mod ingress;
98pub mod inspect;
99pub mod message;
100pub(crate) mod migration;
101pub mod service_context;
102pub mod storage;
103pub mod trace;
104
105/// This value represents the duration the proxy will wait after the last request
106/// before initiating the teardown of the controller. If a request is received
107/// before the timeout triggers, then the timeout will be canceled.
108// The value of 5 seconds was chosen arbitrarily to allow some time between manual
109// button presses that occurs for some settings.
110pub(crate) const DEFAULT_TEARDOWN_TIMEOUT: MonotonicDuration = MonotonicDuration::from_seconds(5);
111const DEFAULT_SETTING_PROXY_MAX_ATTEMPTS: u64 = 3;
112const DEFAULT_SETTING_PROXY_RESPONSE_TIMEOUT_MS: i64 = 10_000;
113
114/// A common trigger for exiting.
115pub type ExitSender = futures::channel::mpsc::UnboundedSender<()>;
116
117/// Runtime defines where the environment will exist. Service is meant for
118/// production environments and will hydrate components to be discoverable as
119/// an environment service. Nested creates a service only usable in the scope
120/// of a test.
121#[derive(PartialEq)]
122enum Runtime {
123    Service,
124    #[cfg(test)]
125    Nested(&'static str),
126}
127
128#[derive(Debug, Default, Clone, Deserialize)]
129pub struct AgentConfiguration {
130    pub agent_types: HashSet<AgentType>,
131}
132
133#[derive(PartialEq, Debug, Clone, Deserialize)]
134pub struct EnabledInterfacesConfiguration {
135    pub interfaces: HashSet<fidl::InterfaceSpec>,
136}
137
138impl EnabledInterfacesConfiguration {
139    pub fn with_interfaces(interfaces: HashSet<fidl::InterfaceSpec>) -> Self {
140        Self { interfaces }
141    }
142}
143
144#[derive(Default, Debug, Clone, Deserialize)]
145pub struct ServiceFlags {
146    pub controller_flags: HashSet<ControllerFlag>,
147}
148
149#[derive(PartialEq, Debug, Default, Clone)]
150pub struct ServiceConfiguration {
151    agent_types: HashSet<AgentType>,
152    fidl_interfaces: HashSet<fidl::Interface>,
153    controller_flags: HashSet<ControllerFlag>,
154}
155
156impl ServiceConfiguration {
157    pub fn from(
158        agent_types: AgentConfiguration,
159        interfaces: EnabledInterfacesConfiguration,
160        flags: ServiceFlags,
161    ) -> Self {
162        let fidl_interfaces: HashSet<fidl::Interface> =
163            interfaces.interfaces.into_iter().map(|x| x.into()).collect();
164
165        Self {
166            agent_types: agent_types.agent_types,
167            fidl_interfaces,
168            controller_flags: flags.controller_flags,
169        }
170    }
171
172    fn set_fidl_interfaces(&mut self, interfaces: HashSet<fidl::Interface>) {
173        self.fidl_interfaces = interfaces;
174    }
175
176    fn set_controller_flags(&mut self, controller_flags: HashSet<ControllerFlag>) {
177        self.controller_flags = controller_flags;
178    }
179}
180
181/// Environment is handed back when an environment is spawned from the
182/// EnvironmentBuilder. A nested environment (if available) is returned,
183/// along with a receiver to be notified when initialization/setup is
184/// complete.
185#[cfg(test)]
186pub struct Environment {
187    pub connector: Option<ProtocolConnector>,
188    pub delegate: Delegate,
189    pub entities: HashSet<Entity>,
190    pub job_seeder: Seeder,
191}
192
193#[cfg(test)]
194impl Environment {
195    pub fn new(
196        connector: Option<ProtocolConnector>,
197        delegate: Delegate,
198        job_seeder: Seeder,
199        entities: HashSet<Entity>,
200    ) -> Environment {
201        Environment { connector, delegate, job_seeder, entities }
202    }
203}
204
205#[cfg(test)]
206fn init_storage_dir() -> DirectoryProxy {
207    let tempdir = tempfile::tempdir().expect("failed to create tempdir");
208    fuchsia_fs::directory::open_in_namespace(
209        tempdir.path().to_str().expect("tempdir path is not valid UTF-8"),
210        fuchsia_fs::PERM_READABLE | fuchsia_fs::PERM_WRITABLE,
211    )
212    .expect("failed to open connection to tempdir")
213}
214
215#[cfg(not(test))]
216fn init_storage_dir() -> DirectoryProxy {
217    panic!("migration dir must be specified");
218}
219
220/// The [EnvironmentBuilder] aggregates the parameters surrounding an [environment](Environment) and
221/// ultimately spawns an environment based on them.
222pub struct EnvironmentBuilder<'a, T: StorageFactory<Storage = DeviceStorage>> {
223    configuration: Option<ServiceConfiguration>,
224    agent_blueprints: Vec<AgentCreator>,
225    event_subscriber_blueprints: Vec<event::subscriber::BlueprintHandle>,
226    storage_factory: Rc<T>,
227    generate_service: Option<GenerateService>,
228    registrants: Vec<Registrant>,
229    settings: Vec<SettingType>,
230    handlers: HashMap<SettingType, GenerateHandler>,
231    setting_proxy_inspect_info: Option<&'a fuchsia_inspect::Node>,
232    active_listener_inspect_logger: Option<Rc<ListenerInspectLogger>>,
233    storage_dir: Option<DirectoryProxy>,
234    store_proxy: Option<StoreProxy>,
235    fidl_storage_factory: Option<Rc<FidlStorageFactory>>,
236    display_configuration: Option<DefaultSetting<DisplayConfiguration, &'static str>>,
237    audio_configuration: Option<DefaultSetting<AudioInfo, &'static str>>,
238    input_configuration: Option<DefaultSetting<InputConfiguration, &'static str>>,
239    light_configuration: Option<DefaultSetting<LightHardwareConfiguration, &'static str>>,
240    media_buttons_event_txs: Vec<UnboundedSender<settings_media_buttons::Event>>,
241}
242
243impl<'a, T: StorageFactory<Storage = DeviceStorage> + 'static> EnvironmentBuilder<'a, T> {
244    /// Construct a new [EnvironmentBuilder] using `storage_factory` to construct the storage for
245    /// the future [Environment].
246    pub fn new(storage_factory: Rc<T>) -> Self {
247        EnvironmentBuilder {
248            configuration: None,
249            agent_blueprints: vec![],
250            event_subscriber_blueprints: vec![],
251            storage_factory,
252            generate_service: None,
253            handlers: HashMap::new(),
254            registrants: vec![],
255            settings: vec![],
256            setting_proxy_inspect_info: None,
257            active_listener_inspect_logger: None,
258            storage_dir: None,
259            store_proxy: None,
260            fidl_storage_factory: None,
261            display_configuration: None,
262            audio_configuration: None,
263            input_configuration: None,
264            light_configuration: None,
265            media_buttons_event_txs: vec![],
266        }
267    }
268
269    /// Overrides the default [GenerateHandler] for a specific [SettingType].
270    pub fn handler(mut self, setting_type: SettingType, generate_handler: GenerateHandler) -> Self {
271        // Ignore the old handler result.
272        let _ = self.handlers.insert(setting_type, generate_handler);
273        self
274    }
275
276    /// A service generator to be used as an overlay on the ServiceContext.
277    pub fn service(mut self, generate_service: GenerateService) -> Self {
278        self.generate_service = Some(generate_service);
279        self
280    }
281
282    /// A preset configuration to load preset parameters as a base. Note that this will override
283    /// any configuration modifications made by [EnvironmentBuilder::fidl_interface],
284    /// [EnvironmentBuilder::policies], and [EnvironmentBuilder::flags].
285    pub fn configuration(mut self, configuration: ServiceConfiguration) -> Self {
286        self.configuration = Some(configuration);
287        self
288    }
289
290    pub fn display_configuration(
291        mut self,
292        display_configuration: DefaultSetting<DisplayConfiguration, &'static str>,
293    ) -> Self {
294        self.display_configuration = Some(display_configuration);
295        self
296    }
297
298    pub fn audio_configuration(
299        mut self,
300        audio_configuration: DefaultSetting<AudioInfo, &'static str>,
301    ) -> Self {
302        self.audio_configuration = Some(audio_configuration);
303        self
304    }
305
306    pub fn input_configuration(
307        mut self,
308        input_configuration: DefaultSetting<InputConfiguration, &'static str>,
309    ) -> Self {
310        self.input_configuration = Some(input_configuration);
311        self
312    }
313
314    pub fn light_configuration(
315        mut self,
316        light_configuration: DefaultSetting<LightHardwareConfiguration, &'static str>,
317    ) -> Self {
318        self.light_configuration = Some(light_configuration);
319        self
320    }
321
322    /// Will override all fidl interfaces in the [ServiceConfiguration].
323    pub fn fidl_interfaces(mut self, interfaces: &[fidl::Interface]) -> Self {
324        if self.configuration.is_none() {
325            self.configuration = Some(ServiceConfiguration::default());
326        }
327
328        if let Some(c) = self.configuration.as_mut() {
329            c.set_fidl_interfaces(interfaces.iter().copied().collect());
330        }
331
332        self
333    }
334
335    /// Appends the [Registrant]s to the list of registrants already configured.
336    pub fn registrants(mut self, mut registrants: Vec<Registrant>) -> Self {
337        self.registrants.append(&mut registrants);
338
339        self
340    }
341
342    /// Setting types to participate.
343    pub fn settings(mut self, settings: &[SettingType]) -> Self {
344        self.settings.extend(settings);
345
346        self
347    }
348
349    /// Setting types to participate with customized controllers.
350    pub fn flags(mut self, controller_flags: &[ControllerFlag]) -> Self {
351        if self.configuration.is_none() {
352            self.configuration = Some(ServiceConfiguration::default());
353        }
354
355        if let Some(c) = self.configuration.as_mut() {
356            c.set_controller_flags(controller_flags.iter().copied().collect());
357        }
358
359        self
360    }
361
362    /// Appends the supplied [AgentRegistrar]s to the list of agent registrars.
363    pub fn agents(mut self, mut registrars: Vec<AgentCreator>) -> Self {
364        self.agent_blueprints.append(&mut registrars);
365        self
366    }
367
368    /// Event subscribers to participate
369    pub fn event_subscribers(mut self, subscribers: &[event::subscriber::BlueprintHandle]) -> Self {
370        self.event_subscriber_blueprints.append(&mut subscribers.to_vec());
371        self
372    }
373
374    /// Sets the inspect node for setting proxy inspect information and any required
375    /// inspect loggers.
376    pub fn setting_proxy_inspect_info(
377        mut self,
378        setting_proxy_inspect_info: &'a fuchsia_inspect::Node,
379        active_listener_inspect_logger: Rc<ListenerInspectLogger>,
380    ) -> Self {
381        self.setting_proxy_inspect_info = Some(setting_proxy_inspect_info);
382        self.active_listener_inspect_logger = Some(active_listener_inspect_logger);
383        self
384    }
385
386    pub fn storage_dir(mut self, storage_dir: DirectoryProxy) -> Self {
387        self.storage_dir = Some(storage_dir);
388        self
389    }
390
391    pub fn store_proxy(mut self, store_proxy: StoreProxy) -> Self {
392        self.store_proxy = Some(store_proxy);
393        self
394    }
395
396    pub fn fidl_storage_factory(mut self, fidl_storage_factory: Rc<FidlStorageFactory>) -> Self {
397        self.fidl_storage_factory = Some(fidl_storage_factory);
398        self
399    }
400
401    pub fn media_buttons_event_txs(
402        mut self,
403        media_buttons_event_txs: Vec<UnboundedSender<settings_media_buttons::Event>>,
404    ) -> Self {
405        self.media_buttons_event_txs.extend(media_buttons_event_txs);
406        self
407    }
408
409    /// Prepares an environment so that it may be spawned. This ensures that all necessary
410    /// components are spawned and ready to handle events and FIDL requests.
411    async fn prepare_env(
412        mut self,
413        mut fs: ServiceFs<ServiceObjLocal<'_, ()>>,
414        runtime: Runtime,
415    ) -> Result<(ServiceFs<ServiceObjLocal<'_, ()>>, Delegate, Seeder, HashSet<Entity>), Error>
416    {
417        let mut service_dir = match runtime {
418            Runtime::Service => fs.dir("svc"),
419            #[cfg(test)]
420            Runtime::Nested(_) => fs.root_dir(),
421        };
422
423        let _ = service_dir.add_fidl_service(
424            move |mut stream: fupdate::ComponentOtaHealthCheckRequestStream| {
425                fasync::Task::local(async move {
426                    while let Some(fupdate::ComponentOtaHealthCheckRequest::GetHealthStatus {
427                        responder,
428                    }) = stream.try_next().await.expect("error running health check service")
429                    {
430                        // We always respond healthy because the health check can only be served
431                        // if the environment is able to spawn which in turn guarantees that no agents
432                        // have returned an error.
433                        responder
434                            .send(fupdate::HealthStatus::Healthy)
435                            .expect("failed to send health status");
436                    }
437                })
438                .detach();
439            },
440        );
441
442        // Define top level MessageHub for service communication.
443        let delegate = service::MessageHub::create_hub();
444
445        let (agent_types, fidl_interfaces, flags) = match self.configuration {
446            Some(configuration) => (
447                configuration.agent_types,
448                configuration.fidl_interfaces,
449                configuration.controller_flags,
450            ),
451            _ => (HashSet::new(), HashSet::new(), HashSet::new()),
452        };
453
454        self.registrants.extend(fidl_interfaces.into_iter().map(|x| x.registrant()));
455
456        let mut settings = HashSet::new();
457        settings.extend(self.settings);
458
459        for registrant in &self.registrants {
460            for dependency in registrant.get_dependencies() {
461                match dependency {
462                    Dependency::Entity(Entity::Handler(setting_type)) => {
463                        let _ = settings.insert(*setting_type);
464                    }
465                }
466            }
467        }
468
469        let fidl_storage_factory = if let Some(factory) = self.fidl_storage_factory {
470            factory
471        } else {
472            let (migration_id, storage_dir) = if let Some(storage_dir) = self.storage_dir {
473                let store_proxy = self.store_proxy.unwrap_or_else(|| {
474                    let store_proxy = connect_to_protocol::<fidl_fuchsia_stash::StoreMarker>()
475                        .expect("failed to connect to stash");
476                    store_proxy
477                        .identify("setting_service")
478                        .expect("should be able to identify to stash");
479                    store_proxy
480                });
481
482                let migration_manager = storage_migrations::register_migrations(
483                    &settings,
484                    Clone::clone(&storage_dir),
485                    store_proxy,
486                )
487                .context("failed to register migrations")?;
488                let migration_id = match migration_manager.run_migrations().await {
489                    Ok(id) => {
490                        log::info!("migrated storage to {id:?}");
491                        id
492                    }
493                    Err((id, e)) => {
494                        log::error!("Settings migration failed: {e:?}");
495                        id
496                    }
497                };
498                let migration_id = migration_id.map(|migration| migration.migration_id);
499                (migration_id, storage_dir)
500            } else {
501                (None, init_storage_dir())
502            };
503
504            Rc::new(FidlStorageFactory::new(migration_id.unwrap_or(0), storage_dir))
505        };
506
507        let common_service_context = Rc::new(CommonServiceContext::new(self.generate_service));
508        let service_context = Rc::new(ServiceContext::new_from_common(
509            Rc::clone(&common_service_context),
510            Some(delegate.clone()),
511        ));
512
513        let context_id_counter = Rc::new(AtomicU64::new(1));
514
515        let mut handler_factory = SettingHandlerFactoryImpl::new(
516            settings.clone(),
517            Rc::clone(&service_context),
518            context_id_counter.clone(),
519        );
520
521        let listener_logger = self
522            .active_listener_inspect_logger
523            .unwrap_or_else(|| Rc::new(ListenerInspectLogger::new()));
524
525        let RegistrationResult {
526            media_buttons_event_txs,
527            setting_value_rx,
528            external_event_rx,
529            usage_event_rx,
530            tasks,
531        } = Self::register_controllers(
532            &settings,
533            Rc::clone(&common_service_context),
534            fidl_storage_factory,
535            self.light_configuration,
536            &mut service_dir,
537            Rc::clone(&listener_logger),
538        )
539        .await;
540        for task in tasks {
541            task.detach();
542        }
543
544        self.media_buttons_event_txs.extend(media_buttons_event_txs);
545
546        EnvironmentBuilder::register_setting_handlers(
547            &settings,
548            Rc::clone(&self.storage_factory),
549            &flags,
550            self.display_configuration.map(DisplayInfoLoader::new),
551            self.audio_configuration.map(AudioInfoLoader::new),
552            self.input_configuration,
553            &mut handler_factory,
554        )
555        .await;
556
557        // Override the configuration handlers with any custom handlers specified
558        // in the environment.
559        for (setting_type, handler) in self.handlers {
560            handler_factory.register(setting_type, handler);
561        }
562
563        let agent_blueprints = create_agent_blueprints(
564            agent_types,
565            self.agent_blueprints,
566            self.media_buttons_event_txs,
567            setting_value_rx,
568            external_event_rx,
569            usage_event_rx,
570        );
571
572        let job_manager_signature = Manager::spawn(&delegate).await;
573        let job_seeder = Seeder::new(&delegate, job_manager_signature).await;
574
575        let entities = create_environment(
576            service_dir,
577            delegate.clone(),
578            job_seeder.clone(),
579            settings,
580            self.registrants,
581            agent_blueprints,
582            self.event_subscriber_blueprints,
583            service_context,
584            Rc::new(Mutex::new(handler_factory)),
585            self.storage_factory,
586            self.setting_proxy_inspect_info.unwrap_or_else(|| component::inspector().root()),
587            listener_logger,
588        )
589        .await
590        .context("Could not create environment")?;
591
592        Ok((fs, delegate, job_seeder, entities))
593    }
594
595    /// Spawn an [Environment] on the supplied [fasync::LocalExecutor] so that it may process
596    /// incoming FIDL requests.
597    pub fn spawn(
598        self,
599        mut executor: fasync::LocalExecutor,
600        fs: ServiceFs<ServiceObjLocal<'_, ()>>,
601    ) -> Result<(), Error> {
602        let (mut fs, ..) = executor
603            .run_singlethreaded(self.prepare_env(fs, Runtime::Service))
604            .context("Failed to prepare env")?;
605
606        let _ = fs.take_and_serve_directory_handle().expect("could not service directory handle");
607        executor.run_singlethreaded(fs.collect::<()>());
608        Ok(())
609    }
610
611    /// Spawn a nested [Environment] so that it can be used for tests.
612    #[cfg(test)]
613    pub async fn spawn_nested(self, env_name: &'static str) -> Result<Environment, Error> {
614        let (mut fs, delegate, job_seeder, entities) = self
615            .prepare_env(ServiceFs::new_local(), Runtime::Nested(env_name))
616            .await
617            .context("Failed to prepare env")?;
618        let connector = Some(fs.create_protocol_connector()?);
619        fasync::Task::local(fs.collect()).detach();
620
621        Ok(Environment::new(connector, delegate, job_seeder, entities))
622    }
623
624    /// Spawns a nested environment and returns the associated
625    /// ProtocolConnector. Note that this is a helper function that provides a
626    /// shortcut for calling EnvironmentBuilder::name() and
627    /// EnvironmentBuilder::spawn().
628    #[cfg(test)]
629    pub async fn spawn_and_get_protocol_connector(
630        self,
631        env_name: &'static str,
632    ) -> Result<ProtocolConnector, Error> {
633        let environment = self.spawn_nested(env_name).await?;
634
635        environment.connector.ok_or_else(|| format_err!("connector not created"))
636    }
637}
638
639struct RegistrationResult {
640    media_buttons_event_txs: Vec<UnboundedSender<settings_media_buttons::Event>>,
641    setting_value_rx: UnboundedReceiver<(&'static str, String)>,
642    external_event_rx: UnboundedReceiver<ExternalServiceEvent>,
643    usage_event_rx: UnboundedReceiver<UsageEvent>,
644    tasks: Vec<fasync::Task<()>>,
645}
646
647impl<'a, T: StorageFactory<Storage = DeviceStorage> + 'static> EnvironmentBuilder<'a, T> {
648    async fn register_controllers<F>(
649        components: &HashSet<SettingType>,
650        service_context: Rc<CommonServiceContext>,
651        fidl_storage_factory: Rc<F>,
652        light_configuration: Option<DefaultSetting<LightHardwareConfiguration, &'static str>>,
653        service_dir: &mut ServiceFsDir<'_, ServiceObjLocal<'_, ()>>,
654        listener_logger: Rc<ListenerInspectLogger>,
655    ) -> RegistrationResult
656    where
657        F: StorageFactory<Storage = FidlStorage>,
658    {
659        let (setting_value_tx, setting_value_rx) = mpsc::unbounded();
660        let (external_event_tx, external_event_rx) = mpsc::unbounded();
661        let (usage_event_tx, usage_event_rx) = mpsc::unbounded();
662        let external_publisher = ExternalEventPublisher::new(external_event_tx);
663        let mut media_buttons_event_txs = vec![];
664        let mut tasks = vec![];
665        // Initialize storage for all components.
666        if components.contains(&SettingType::Light) {
667            fidl_storage_factory
668                .initialize::<LightController>()
669                .await
670                .expect("storage should still be initializing");
671        }
672
673        // Start handlers for all components.
674        if components.contains(&SettingType::Light) {
675            let mut light_configuration =
676                light_configuration.expect("Light controller requires a light configuration");
677            match settings_light::setup_light_api(
678                service_context,
679                &mut light_configuration,
680                fidl_storage_factory,
681                SettingValuePublisher::new(setting_value_tx),
682                UsagePublisher::new(usage_event_tx, listener_logger),
683                external_publisher,
684            )
685            .await
686            {
687                Ok(settings_light::SetupResult {
688                    mut light_fidl_handler,
689                    media_buttons_event_tx,
690                    task,
691                }) => {
692                    media_buttons_event_txs.push(media_buttons_event_tx);
693                    tasks.push(task);
694                    let _ = service_dir.add_fidl_service(move |stream: LightRequestStream| {
695                        light_fidl_handler.handle_stream(stream)
696                    });
697                }
698                Err(e) => {
699                    log::error!("Failed to setup light api: {e:?}");
700                }
701            }
702        }
703
704        RegistrationResult {
705            media_buttons_event_txs,
706            setting_value_rx,
707            external_event_rx,
708            usage_event_rx,
709            tasks,
710        }
711    }
712
713    /// Initializes storage and registers handler generation functions for the configured setting
714    /// types.
715    async fn register_setting_handlers(
716        components: &HashSet<SettingType>,
717        device_storage_factory: Rc<T>,
718        controller_flags: &HashSet<ControllerFlag>,
719        display_loader: Option<DisplayInfoLoader>,
720        audio_loader: Option<AudioInfoLoader>,
721        input_configuration: Option<DefaultSetting<InputConfiguration, &'static str>>,
722        factory_handle: &mut SettingHandlerFactoryImpl,
723    ) {
724        // Accessibility
725        if components.contains(&SettingType::Accessibility) {
726            device_storage_factory
727                .initialize::<AccessibilityController<T>>()
728                .await
729                .expect("storage should still be initializing");
730            let device_storage_factory = Rc::clone(&device_storage_factory);
731            factory_handle.register(
732                SettingType::Accessibility,
733                Box::new(move |context| {
734                    DataHandler::<AccessibilityController<T>>::spawn_with_async(
735                        context,
736                        Rc::clone(&device_storage_factory),
737                    )
738                }),
739            );
740        }
741
742        // Audio
743        if components.contains(&SettingType::Audio) {
744            let audio_loader = audio_loader.expect("Audio storage requires audio loader");
745            device_storage_factory
746                .initialize_with_loader::<AudioController<T>, _>(audio_loader.clone())
747                .await
748                .expect("storage should still be initializing");
749            let device_storage_factory = Rc::clone(&device_storage_factory);
750            factory_handle.register(
751                SettingType::Audio,
752                Box::new(move |context| {
753                    DataHandler::<AudioController<T>>::spawn_with_async(
754                        context,
755                        (Rc::clone(&device_storage_factory), audio_loader.clone()),
756                    )
757                }),
758            );
759        }
760
761        // Display
762        if components.contains(&SettingType::Display) {
763            device_storage_factory
764                .initialize_with_loader::<DisplayController<T>, _>(
765                    display_loader.expect("Display storage requires display loader"),
766                )
767                .await
768                .expect("storage should still be initializing");
769            let device_storage_factory = Rc::clone(&device_storage_factory);
770            let external_brightness_control =
771                controller_flags.contains(&ControllerFlag::ExternalBrightnessControl);
772            factory_handle.register(
773                SettingType::Display,
774                Box::new(
775                    move |context| {
776                        if external_brightness_control {
777                            DataHandler::<DisplayController<T, ExternalBrightnessControl>>::spawn_with_async(
778                                context,
779                                Rc::clone(&device_storage_factory))
780                        } else {
781                            DataHandler::<DisplayController<T>>::spawn_with_async(
782                                context,
783                                Rc::clone(&device_storage_factory))
784                        }
785                    }
786                ),
787            );
788        }
789
790        // Input
791        if components.contains(&SettingType::Input) {
792            let input_configuration = Rc::new(std::sync::Mutex::new(
793                input_configuration.expect("Input controller requires an input configuration"),
794            ));
795            device_storage_factory
796                .initialize::<InputController<T>>()
797                .await
798                .expect("storage should still be initializing");
799            let device_storage_factory = Rc::clone(&device_storage_factory);
800            factory_handle.register(
801                SettingType::Input,
802                Box::new(move |context| {
803                    DataHandler::<InputController<T>>::spawn_with_async(
804                        context,
805                        (Rc::clone(&device_storage_factory), Rc::clone(&input_configuration)),
806                    )
807                }),
808            );
809        }
810
811        // Intl
812        if components.contains(&SettingType::Intl) {
813            device_storage_factory
814                .initialize::<IntlController<T>>()
815                .await
816                .expect("storage should still be initializing");
817            let device_storage_factory = Rc::clone(&device_storage_factory);
818            factory_handle.register(
819                SettingType::Intl,
820                Box::new(move |context| {
821                    DataHandler::<IntlController<T>>::spawn_with_async(
822                        context,
823                        Rc::clone(&device_storage_factory),
824                    )
825                }),
826            );
827        }
828
829        // Keyboard
830        if components.contains(&SettingType::Keyboard) {
831            device_storage_factory
832                .initialize::<KeyboardController>()
833                .await
834                .expect("storage should still be initializing");
835            factory_handle.register(
836                SettingType::Keyboard,
837                Box::new(DataHandler::<KeyboardController>::spawn),
838            );
839        }
840
841        // Do not disturb
842        if components.contains(&SettingType::DoNotDisturb) {
843            device_storage_factory
844                .initialize::<DoNotDisturbController<T>>()
845                .await
846                .expect("storage should still be initializing");
847            let device_storage_factory = Rc::clone(&device_storage_factory);
848            factory_handle.register(
849                SettingType::DoNotDisturb,
850                Box::new(move |context| {
851                    DataHandler::<DoNotDisturbController<T>>::spawn_with_async(
852                        context,
853                        Rc::clone(&device_storage_factory),
854                    )
855                }),
856            );
857        }
858
859        // Factory Reset
860        if components.contains(&SettingType::FactoryReset) {
861            device_storage_factory
862                .initialize::<FactoryResetController<T>>()
863                .await
864                .expect("storage should still be initializing");
865            let device_storage_factory = Rc::clone(&device_storage_factory);
866            factory_handle.register(
867                SettingType::FactoryReset,
868                Box::new(move |context| {
869                    DataHandler::<FactoryResetController<T>>::spawn_with_async(
870                        context,
871                        Rc::clone(&device_storage_factory),
872                    )
873                }),
874            );
875        }
876
877        // Night mode
878        if components.contains(&SettingType::NightMode) {
879            device_storage_factory
880                .initialize::<NightModeController>()
881                .await
882                .expect("storage should still be initializing");
883            factory_handle.register(
884                SettingType::NightMode,
885                Box::new(DataHandler::<NightModeController>::spawn),
886            );
887        }
888
889        // Privacy
890        if components.contains(&SettingType::Privacy) {
891            device_storage_factory
892                .initialize::<PrivacyController>()
893                .await
894                .expect("storage should still be initializing");
895            factory_handle
896                .register(SettingType::Privacy, Box::new(DataHandler::<PrivacyController>::spawn));
897        }
898
899        // Setup
900        if components.contains(&SettingType::Setup) {
901            device_storage_factory
902                .initialize::<SetupController>()
903                .await
904                .expect("storage should still be initializing");
905            factory_handle
906                .register(SettingType::Setup, Box::new(DataHandler::<SetupController>::spawn));
907        }
908    }
909}
910
911fn create_agent_blueprints(
912    agent_types: HashSet<AgentType>,
913    agent_blueprints: Vec<AgentCreator>,
914    media_buttons_event_txs: Vec<UnboundedSender<settings_media_buttons::Event>>,
915    setting_value_rx: UnboundedReceiver<(&'static str, String)>,
916    external_event_rx: UnboundedReceiver<ExternalServiceEvent>,
917    mut usage_router_rx: UnboundedReceiver<UsageEvent>,
918) -> Vec<AgentCreator> {
919    let (proxy_event_tx, proxy_event_rx) = mpsc::unbounded();
920    let (usage_event_tx, usage_event_rx) = mpsc::unbounded();
921
922    // Route general inspect requests to specific inspect agents.
923    fasync::Task::local(async move {
924        while let Some(usage_event) = usage_router_rx.next().await {
925            let _ = proxy_event_tx.unbounded_send(usage_event.clone());
926            let _ = usage_event_tx.unbounded_send(usage_event);
927        }
928    })
929    .detach();
930    let media_buttons_registrar = agent_types
931        .contains(&AgentType::MediaButtons)
932        .then(|| agent::media_buttons::create_registrar(media_buttons_event_txs));
933    let inspect_settings_values_registrar = agent_types
934        .contains(&AgentType::InspectSettingValues)
935        .then(|| agent::inspect::setting_values::create_registrar(setting_value_rx));
936    let inspect_external_apis_registrar = agent_types
937        .contains(&AgentType::InspectExternalApis)
938        .then(|| agent::inspect::external_apis::create_registrar(external_event_rx));
939    let inspect_setting_proxy_registrar = agent_types
940        .contains(&AgentType::InspectSettingProxy)
941        .then(|| agent::inspect::setting_proxy::create_registrar(proxy_event_rx));
942    let inspect_usages_registrar = agent_types
943        .contains(&AgentType::InspectSettingTypeUsage)
944        .then(|| agent::inspect::usage_counts::create_registrar(usage_event_rx));
945
946    let agent_registrars = [
947        media_buttons_registrar,
948        inspect_settings_values_registrar,
949        inspect_external_apis_registrar,
950        inspect_setting_proxy_registrar,
951        inspect_usages_registrar,
952    ];
953
954    let mut agent_blueprints = if agent_types.iter().all(|t| {
955        matches!(
956            t,
957            AgentType::MediaButtons
958                | AgentType::InspectSettingValues
959                | AgentType::InspectExternalApis
960                | AgentType::InspectSettingProxy
961                | AgentType::InspectSettingTypeUsage
962        )
963    }) {
964        agent_blueprints
965    } else {
966        agent_types.into_iter().filter_map(AgentCreator::from_type).collect()
967    };
968
969    agent_blueprints.extend(agent_registrars.into_iter().filter_map(|r| r));
970    agent_blueprints
971}
972
973/// Brings up the settings service environment.
974///
975/// This method generates the necessary infrastructure to support the settings
976/// service (handlers, agents, etc.) and brings up the components necessary to
977/// support the components specified in the components HashSet.
978#[allow(clippy::too_many_arguments)]
979async fn create_environment<'a, T>(
980    mut service_dir: ServiceFsDir<'_, ServiceObjLocal<'a, ()>>,
981    delegate: service::message::Delegate,
982    job_seeder: Seeder,
983    components: HashSet<SettingType>,
984    registrants: Vec<Registrant>,
985    agent_blueprints: Vec<AgentCreator>,
986    event_subscriber_blueprints: Vec<event::subscriber::BlueprintHandle>,
987    service_context: Rc<ServiceContext>,
988    handler_factory: Rc<Mutex<SettingHandlerFactoryImpl>>,
989    device_storage_factory: Rc<T>,
990    setting_proxies_node: &fuchsia_inspect::Node,
991    listener_logger: Rc<ListenerInspectLogger>,
992) -> Result<HashSet<Entity>, Error>
993where
994    T: StorageFactory<Storage = DeviceStorage> + 'static,
995{
996    for blueprint in event_subscriber_blueprints {
997        blueprint.create(delegate.clone()).await;
998    }
999
1000    let mut entities = HashSet::new();
1001
1002    for setting_type in &components {
1003        let _ = SettingProxy::create(
1004            *setting_type,
1005            handler_factory.clone(),
1006            delegate.clone(),
1007            DEFAULT_SETTING_PROXY_MAX_ATTEMPTS,
1008            DEFAULT_TEARDOWN_TIMEOUT,
1009            Some(MonotonicDuration::from_millis(DEFAULT_SETTING_PROXY_RESPONSE_TIMEOUT_MS)),
1010            true,
1011            setting_proxies_node.create_child(format!("{setting_type:?}")),
1012            Rc::clone(&listener_logger),
1013        )
1014        .await?;
1015
1016        let _ = entities.insert(Entity::Handler(*setting_type));
1017    }
1018
1019    let mut agent_authority = Authority::create(delegate.clone(), components.clone()).await?;
1020
1021    for registrant in registrants {
1022        if registrant.get_dependencies().iter().all(|dependency| {
1023            let dep_met = dependency.is_fulfilled(&entities);
1024            if !dep_met {
1025                log::error!(
1026                    "Skipping {} registration due to missing dependency {:?}",
1027                    registrant.get_interface(),
1028                    dependency
1029                );
1030            }
1031            dep_met
1032        }) {
1033            registrant.register(&job_seeder, &mut service_dir);
1034        }
1035    }
1036
1037    // The service does not work without storage, so ensure it is always included first.
1038    agent_authority
1039        .register(crate::agent::storage_agent::create_registrar(device_storage_factory))
1040        .await;
1041
1042    for blueprint in agent_blueprints {
1043        agent_authority.register(blueprint).await;
1044    }
1045
1046    // Execute initialization agents sequentially
1047    agent_authority
1048        .execute_lifespan(Lifespan::Initialization, Rc::clone(&service_context), true)
1049        .await
1050        .context("Agent initialization failed")?;
1051
1052    // Execute service agents concurrently
1053    agent_authority
1054        .execute_lifespan(Lifespan::Service, Rc::clone(&service_context), false)
1055        .await
1056        .context("Agent service start failed")?;
1057
1058    Ok(entities)
1059}
1060
1061#[cfg(test)]
1062mod tests;