| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #[cfg(test)] |
| use tracing as _; // Make it easier to debug tests by always building with tracing |
| |
| use std::collections::{HashMap, HashSet}; |
| use std::sync::atomic::AtomicU64; |
| use std::sync::Arc; |
| |
| #[cfg(test)] |
| use ::fidl::endpoints::{create_proxy, ServerEnd}; |
| #[cfg(test)] |
| use anyhow::format_err; |
| use anyhow::{Context, Error}; |
| #[cfg(test)] |
| use fidl_fuchsia_io::DirectoryMarker; |
| use fidl_fuchsia_io::DirectoryProxy; |
| use fidl_fuchsia_stash::StoreProxy; |
| use fuchsia_async as fasync; |
| use fuchsia_component::client::connect_to_protocol; |
| #[cfg(test)] |
| use fuchsia_component::server::ProtocolConnector; |
| use fuchsia_component::server::{ServiceFs, ServiceFsDir, ServiceObj}; |
| #[cfg(test)] |
| use fuchsia_fs::OpenFlags; |
| use fuchsia_inspect::component; |
| use fuchsia_zircon::{Duration, DurationNum}; |
| use futures::{lock::Mutex, StreamExt}; |
| use settings_storage::device_storage::DeviceStorage; |
| use settings_storage::fidl_storage::FidlStorage; |
| |
| pub use display::display_configuration::DisplayConfiguration; |
| pub use display::LightSensorConfig; |
| pub use handler::setting_proxy_inspect_info::SettingProxyInspectInfo; |
| pub use input::input_device_configuration::InputConfiguration; |
| pub use light::light_hardware_configuration::LightHardwareConfiguration; |
| use serde::Deserialize; |
| pub use service::{Address, Payload}; |
| #[cfg(test)] |
| use vfs::directory::entry_container::Directory; |
| #[cfg(test)] |
| use vfs::directory::mutable::simple::tree_constructor; |
| #[cfg(test)] |
| use vfs::execution_scope::ExecutionScope; |
| #[cfg(test)] |
| use vfs::file::vmo::read_write; |
| #[cfg(test)] |
| use vfs::mut_pseudo_directory; |
| |
| use crate::accessibility::accessibility_controller::AccessibilityController; |
| use crate::agent::authority::Authority; |
| use crate::agent::{AgentCreator, Lifespan}; |
| use crate::audio::audio_controller::AudioController; |
| use crate::base::{Dependency, Entity, SettingType}; |
| use crate::config::base::{AgentType, ControllerFlag}; |
| use crate::display::display_controller::{DisplayController, ExternalBrightnessControl}; |
| use crate::do_not_disturb::do_not_disturb_controller::DoNotDisturbController; |
| use crate::factory_reset::factory_reset_controller::FactoryResetController; |
| use crate::handler::base::GenerateHandler; |
| use crate::handler::setting_handler::persist::Handler as DataHandler; |
| use crate::handler::setting_handler_factory_impl::SettingHandlerFactoryImpl; |
| use crate::handler::setting_proxy::SettingProxy; |
| use crate::ingress::fidl; |
| use crate::ingress::registration::Registrant; |
| use crate::input::input_controller::InputController; |
| use crate::inspect::listener_logger::ListenerInspectLogger; |
| use crate::intl::intl_controller::IntlController; |
| use crate::job::manager::Manager; |
| use crate::job::source::Seeder; |
| use crate::keyboard::keyboard_controller::KeyboardController; |
| use crate::light::light_controller::LightController; |
| use crate::night_mode::night_mode_controller::NightModeController; |
| use crate::privacy::privacy_controller::PrivacyController; |
| use crate::service::message::Delegate; |
| use crate::service_context::GenerateService; |
| use crate::service_context::ServiceContext; |
| use crate::setup::setup_controller::SetupController; |
| use settings_storage::storage_factory::{FidlStorageFactory, StorageFactory}; |
| |
| mod accessibility; |
| mod audio; |
| mod clock; |
| mod display; |
| mod do_not_disturb; |
| mod event; |
| mod factory_reset; |
| mod input; |
| mod intl; |
| mod job; |
| mod keyboard; |
| mod light; |
| mod night_mode; |
| mod privacy; |
| mod service; |
| mod setup; |
| mod storage_migrations; |
| |
| pub mod agent; |
| pub mod base; |
| pub mod config; |
| pub mod handler; |
| pub mod ingress; |
| pub mod inspect; |
| pub mod message; |
| pub(crate) mod migration; |
| pub mod service_context; |
| pub mod storage; |
| pub mod trace; |
| |
| /// This value represents the duration the proxy will wait after the last request |
| /// before initiating the teardown of the controller. If a request is received |
| /// before the timeout triggers, then the timeout will be canceled. |
| // The value of 5 seconds was chosen arbitrarily to allow some time between manual |
| // button presses that occurs for some settings. |
| pub(crate) const DEFAULT_TEARDOWN_TIMEOUT: Duration = Duration::from_seconds(5); |
| const DEFAULT_SETTING_PROXY_MAX_ATTEMPTS: u64 = 3; |
| const DEFAULT_SETTING_PROXY_RESPONSE_TIMEOUT_MS: i64 = 10_000; |
| |
| /// A common trigger for exiting. |
| pub type ExitSender = futures::channel::mpsc::UnboundedSender<()>; |
| |
| /// Runtime defines where the environment will exist. Service is meant for |
| /// production environments and will hydrate components to be discoverable as |
| /// an environment service. Nested creates a service only usable in the scope |
| /// of a test. |
| #[derive(PartialEq)] |
| enum Runtime { |
| Service, |
| #[cfg(test)] |
| Nested(&'static str), |
| } |
| |
| #[derive(Debug, Default, Clone, Deserialize)] |
| pub struct AgentConfiguration { |
| pub agent_types: HashSet<AgentType>, |
| } |
| |
| #[derive(PartialEq, Debug, Clone, Deserialize)] |
| pub struct EnabledInterfacesConfiguration { |
| pub interfaces: HashSet<fidl::InterfaceSpec>, |
| } |
| |
| impl EnabledInterfacesConfiguration { |
| pub fn with_interfaces(interfaces: HashSet<fidl::InterfaceSpec>) -> Self { |
| Self { interfaces } |
| } |
| } |
| |
| #[derive(Default, Debug, Clone, Deserialize)] |
| pub struct ServiceFlags { |
| pub controller_flags: HashSet<ControllerFlag>, |
| } |
| |
| #[derive(PartialEq, Debug, Default, Clone)] |
| pub struct ServiceConfiguration { |
| agent_types: HashSet<AgentType>, |
| fidl_interfaces: HashSet<fidl::Interface>, |
| controller_flags: HashSet<ControllerFlag>, |
| } |
| |
| impl ServiceConfiguration { |
| pub fn from( |
| agent_types: AgentConfiguration, |
| interfaces: EnabledInterfacesConfiguration, |
| flags: ServiceFlags, |
| ) -> Self { |
| let fidl_interfaces: HashSet<fidl::Interface> = |
| interfaces.interfaces.into_iter().map(|x| x.into()).collect(); |
| |
| Self { |
| agent_types: agent_types.agent_types, |
| fidl_interfaces, |
| controller_flags: flags.controller_flags, |
| } |
| } |
| |
| fn set_fidl_interfaces(&mut self, interfaces: HashSet<fidl::Interface>) { |
| self.fidl_interfaces = interfaces; |
| |
| let display_subinterfaces = [ |
| fidl::Interface::Display(fidl::display::InterfaceFlags::LIGHT_SENSOR), |
| fidl::Interface::Display(fidl::display::InterfaceFlags::BASE), |
| ] |
| .into(); |
| |
| // Consolidate display type. |
| // TODO(https://fxbug.dev/42074066): Remove this special handling once light sensor is its own FIDL |
| // interface. |
| if self.fidl_interfaces.is_superset(&display_subinterfaces) { |
| self.fidl_interfaces = &self.fidl_interfaces - &display_subinterfaces; |
| let inserted = self.fidl_interfaces.insert(fidl::Interface::Display( |
| fidl::display::InterfaceFlags::BASE | fidl::display::InterfaceFlags::LIGHT_SENSOR, |
| )); |
| assert!( |
| inserted, |
| "Cannot insert the display interface twice. Please check the interface \ |
| configuration" |
| ); |
| } |
| } |
| |
| fn set_controller_flags(&mut self, controller_flags: HashSet<ControllerFlag>) { |
| self.controller_flags = controller_flags; |
| } |
| } |
| |
| /// Environment is handed back when an environment is spawned from the |
| /// EnvironmentBuilder. A nested environment (if available) is returned, |
| /// along with a receiver to be notified when initialization/setup is |
| /// complete. |
| #[cfg(test)] |
| pub struct Environment { |
| pub connector: Option<ProtocolConnector>, |
| pub delegate: Delegate, |
| pub entities: HashSet<Entity>, |
| pub job_seeder: Seeder, |
| } |
| |
| #[cfg(test)] |
| impl Environment { |
| pub fn new( |
| connector: Option<ProtocolConnector>, |
| delegate: Delegate, |
| job_seeder: Seeder, |
| entities: HashSet<Entity>, |
| ) -> Environment { |
| Environment { connector, delegate, job_seeder, entities } |
| } |
| } |
| |
| #[cfg(test)] |
| fn init_storage_dir() -> DirectoryProxy { |
| let fs_scope = ExecutionScope::build() |
| .entry_constructor(tree_constructor(move |_, _| Ok(read_write(b"")))) |
| .new(); |
| let (directory, server) = create_proxy::<DirectoryMarker>().unwrap(); |
| mut_pseudo_directory! {}.open( |
| fs_scope, |
| OpenFlags::RIGHT_READABLE | OpenFlags::RIGHT_WRITABLE, |
| vfs::path::Path::dot(), |
| ServerEnd::new(server.into_channel()), |
| ); |
| directory |
| } |
| |
| #[cfg(not(test))] |
| fn init_storage_dir() -> DirectoryProxy { |
| panic!("migration dir must be specified"); |
| } |
| |
| /// The [EnvironmentBuilder] aggregates the parameters surrounding an [environment](Environment) and |
| /// ultimately spawns an environment based on them. |
| pub struct EnvironmentBuilder< |
| 'a, |
| T: StorageFactory<Storage = DeviceStorage> + Send + Sync + 'static, |
| > { |
| configuration: Option<ServiceConfiguration>, |
| agent_blueprints: Vec<AgentCreator>, |
| event_subscriber_blueprints: Vec<event::subscriber::BlueprintHandle>, |
| storage_factory: Arc<T>, |
| generate_service: Option<GenerateService>, |
| registrants: Vec<Registrant>, |
| settings: Vec<SettingType>, |
| handlers: HashMap<SettingType, GenerateHandler>, |
| setting_proxy_inspect_info: Option<&'a fuchsia_inspect::Node>, |
| active_listener_inspect_logger: Option<Arc<Mutex<ListenerInspectLogger>>>, |
| storage_dir: Option<DirectoryProxy>, |
| store_proxy: Option<StoreProxy>, |
| fidl_storage_factory: Option<Arc<FidlStorageFactory>>, |
| } |
| |
| impl<'a, T: StorageFactory<Storage = DeviceStorage> + Send + Sync + 'static> |
| EnvironmentBuilder<'a, T> |
| { |
| /// Construct a new [EnvironmentBuilder] using `storage_factory` to construct the storage for |
| /// the future [Environment]. |
| pub fn new(storage_factory: Arc<T>) -> Self { |
| EnvironmentBuilder { |
| configuration: None, |
| agent_blueprints: vec![], |
| event_subscriber_blueprints: vec![], |
| storage_factory, |
| generate_service: None, |
| handlers: HashMap::new(), |
| registrants: vec![], |
| settings: vec![], |
| setting_proxy_inspect_info: None, |
| active_listener_inspect_logger: None, |
| storage_dir: None, |
| store_proxy: None, |
| fidl_storage_factory: None, |
| } |
| } |
| |
| /// Overrides the default [GenerateHandler] for a specific [SettingType]. |
| pub fn handler(mut self, setting_type: SettingType, generate_handler: GenerateHandler) -> Self { |
| // Ignore the old handler result. |
| let _ = self.handlers.insert(setting_type, generate_handler); |
| self |
| } |
| |
| /// A service generator to be used as an overlay on the ServiceContext. |
| pub fn service(mut self, generate_service: GenerateService) -> Self { |
| self.generate_service = Some(generate_service); |
| self |
| } |
| |
| /// A preset configuration to load preset parameters as a base. Note that this will override |
| /// any configuration modifications made by [EnvironmentBuilder::fidl_interface], |
| /// [EnvironmentBuilder::policies], and [EnvironmentBuilder::flags]. |
| pub fn configuration(mut self, configuration: ServiceConfiguration) -> Self { |
| self.configuration = Some(configuration); |
| self |
| } |
| |
| /// Will override all fidl interfaces in the [ServiceConfiguration]. |
| pub fn fidl_interfaces(mut self, interfaces: &[fidl::Interface]) -> Self { |
| if self.configuration.is_none() { |
| self.configuration = Some(ServiceConfiguration::default()); |
| } |
| |
| if let Some(c) = self.configuration.as_mut() { |
| c.set_fidl_interfaces(interfaces.iter().copied().collect()); |
| } |
| |
| self |
| } |
| |
| /// Appends the [Registrant]s to the list of registrants already configured. |
| pub fn registrants(mut self, mut registrants: Vec<Registrant>) -> Self { |
| self.registrants.append(&mut registrants); |
| |
| self |
| } |
| |
| /// Setting types to participate. |
| pub fn settings(mut self, settings: &[SettingType]) -> Self { |
| self.settings.extend(settings); |
| |
| self |
| } |
| |
| /// Setting types to participate with customized controllers. |
| pub fn flags(mut self, controller_flags: &[ControllerFlag]) -> Self { |
| if self.configuration.is_none() { |
| self.configuration = Some(ServiceConfiguration::default()); |
| } |
| |
| if let Some(c) = self.configuration.as_mut() { |
| c.set_controller_flags(controller_flags.iter().copied().collect()); |
| } |
| |
| self |
| } |
| |
| /// Appends the supplied [AgentRegistrar]s to the list of agent registrars. |
| pub fn agents(mut self, mut registrars: Vec<AgentCreator>) -> Self { |
| self.agent_blueprints.append(&mut registrars); |
| self |
| } |
| |
| /// Event subscribers to participate |
| pub fn event_subscribers(mut self, subscribers: &[event::subscriber::BlueprintHandle]) -> Self { |
| self.event_subscriber_blueprints.append(&mut subscribers.to_vec()); |
| self |
| } |
| |
| /// Sets the inspect node for setting proxy inspect information and any required |
| /// inspect loggers. |
| pub fn setting_proxy_inspect_info( |
| mut self, |
| setting_proxy_inspect_info: &'a fuchsia_inspect::Node, |
| active_listener_inspect_logger: Arc<Mutex<ListenerInspectLogger>>, |
| ) -> Self { |
| self.setting_proxy_inspect_info = Some(setting_proxy_inspect_info); |
| self.active_listener_inspect_logger = Some(active_listener_inspect_logger); |
| self |
| } |
| |
| pub fn storage_dir(mut self, storage_dir: DirectoryProxy) -> Self { |
| self.storage_dir = Some(storage_dir); |
| self |
| } |
| |
| pub fn store_proxy(mut self, store_proxy: StoreProxy) -> Self { |
| self.store_proxy = Some(store_proxy); |
| self |
| } |
| |
| pub fn fidl_storage_factory(mut self, fidl_storage_factory: Arc<FidlStorageFactory>) -> Self { |
| self.fidl_storage_factory = Some(fidl_storage_factory); |
| self |
| } |
| |
| /// Prepares an environment so that it may be spawned. This ensures that all necessary |
| /// components are spawned and ready to handle events and FIDL requests. |
| async fn prepare_env( |
| mut self, |
| mut fs: ServiceFs<ServiceObj<'_, ()>>, |
| runtime: Runtime, |
| ) -> Result<(ServiceFs<ServiceObj<'_, ()>>, Delegate, Seeder, HashSet<Entity>), Error> { |
| let service_dir = match runtime { |
| Runtime::Service => fs.dir("svc"), |
| #[cfg(test)] |
| Runtime::Nested(_) => fs.root_dir(), |
| }; |
| |
| // Define top level MessageHub for service communication. |
| let delegate = service::MessageHub::create_hub(); |
| |
| let (agent_types, fidl_interfaces, flags) = match self.configuration { |
| Some(configuration) => ( |
| configuration.agent_types, |
| configuration.fidl_interfaces, |
| configuration.controller_flags, |
| ), |
| _ => (HashSet::new(), HashSet::new(), HashSet::new()), |
| }; |
| |
| self.registrants.extend(fidl_interfaces.into_iter().map(|x| x.registrant())); |
| |
| let mut settings = HashSet::new(); |
| settings.extend(self.settings); |
| |
| for registrant in &self.registrants { |
| for dependency in registrant.get_dependencies() { |
| match dependency { |
| Dependency::Entity(Entity::Handler(setting_type)) => { |
| let _ = settings.insert(*setting_type); |
| } |
| } |
| } |
| } |
| |
| let fidl_storage_factory = if let Some(factory) = self.fidl_storage_factory { |
| factory |
| } else { |
| let (migration_id, storage_dir) = if let Some(storage_dir) = self.storage_dir { |
| let store_proxy = self.store_proxy.unwrap_or_else(|| { |
| let store_proxy = connect_to_protocol::<fidl_fuchsia_stash::StoreMarker>() |
| .expect("failed to connect to stash"); |
| store_proxy |
| .identify("setting_service") |
| .expect("should be able to identify to stash"); |
| store_proxy |
| }); |
| |
| let migration_manager = storage_migrations::register_migrations( |
| &settings, |
| Clone::clone(&storage_dir), |
| store_proxy, |
| ) |
| .context("failed to register migrations")?; |
| let migration_id = match migration_manager.run_migrations().await { |
| Ok(id) => { |
| tracing::info!("migrated storage to {id:?}"); |
| id |
| } |
| Err((id, e)) => { |
| tracing::error!("Settings migration failed: {e:?}"); |
| id |
| } |
| }; |
| let migration_id = migration_id.map(|migration| migration.migration_id); |
| (migration_id, storage_dir) |
| } else { |
| (None, init_storage_dir()) |
| }; |
| |
| Arc::new(FidlStorageFactory::new(migration_id.unwrap_or(0), storage_dir)) |
| }; |
| |
| let service_context = |
| Arc::new(ServiceContext::new(self.generate_service, Some(delegate.clone()))); |
| |
| let context_id_counter = Arc::new(AtomicU64::new(1)); |
| |
| let mut handler_factory = SettingHandlerFactoryImpl::new( |
| settings.clone(), |
| Arc::clone(&service_context), |
| context_id_counter.clone(), |
| ); |
| |
| EnvironmentBuilder::register_setting_handlers( |
| &settings, |
| Arc::clone(&self.storage_factory), |
| Arc::clone(&fidl_storage_factory), |
| &flags, |
| &mut handler_factory, |
| ) |
| .await; |
| |
| // Override the configuration handlers with any custom handlers specified |
| // in the environment. |
| for (setting_type, handler) in self.handlers { |
| handler_factory.register(setting_type, handler); |
| } |
| |
| let agent_blueprints = if agent_types.is_empty() { |
| self.agent_blueprints |
| } else { |
| agent_types.into_iter().map(AgentCreator::from).collect() |
| }; |
| |
| let job_manager_signature = Manager::spawn(&delegate).await; |
| let job_seeder = Seeder::new(&delegate, job_manager_signature).await; |
| |
| let entities = create_environment( |
| service_dir, |
| delegate.clone(), |
| job_seeder.clone(), |
| settings, |
| self.registrants, |
| agent_blueprints, |
| self.event_subscriber_blueprints, |
| service_context, |
| Arc::new(Mutex::new(handler_factory)), |
| self.storage_factory, |
| fidl_storage_factory, |
| self.setting_proxy_inspect_info.unwrap_or_else(|| component::inspector().root()), |
| self.active_listener_inspect_logger |
| .unwrap_or_else(|| Arc::new(Mutex::new(ListenerInspectLogger::new()))), |
| ) |
| .await |
| .context("Could not create environment")?; |
| |
| Ok((fs, delegate, job_seeder, entities)) |
| } |
| |
| /// Spawn an [Environment] on the supplied [fasync::LocalExecutor] so that it may process |
| /// incoming FIDL requests. |
| pub fn spawn( |
| self, |
| mut executor: fasync::LocalExecutor, |
| fs: ServiceFs<ServiceObj<'_, ()>>, |
| ) -> Result<(), Error> { |
| let (mut fs, ..) = executor |
| .run_singlethreaded(self.prepare_env(fs, Runtime::Service)) |
| .context("Failed to prepare env")?; |
| let _ = fs.take_and_serve_directory_handle().expect("could not service directory handle"); |
| executor.run_singlethreaded(fs.collect::<()>()); |
| Ok(()) |
| } |
| |
| /// Spawn a nested [Environment] so that it can be used for tests. |
| #[cfg(test)] |
| pub async fn spawn_nested(self, env_name: &'static str) -> Result<Environment, Error> { |
| let (mut fs, delegate, job_seeder, entities) = self |
| .prepare_env(ServiceFs::new(), Runtime::Nested(env_name)) |
| .await |
| .context("Failed to prepare env")?; |
| let connector = Some(fs.create_protocol_connector()?); |
| fasync::Task::spawn(fs.collect()).detach(); |
| |
| Ok(Environment::new(connector, delegate, job_seeder, entities)) |
| } |
| |
| /// Spawns a nested environment and returns the associated |
| /// ProtocolConnector. Note that this is a helper function that provides a |
| /// shortcut for calling EnvironmentBuilder::name() and |
| /// EnvironmentBuilder::spawn(). |
| #[cfg(test)] |
| pub async fn spawn_and_get_protocol_connector( |
| self, |
| env_name: &'static str, |
| ) -> Result<ProtocolConnector, Error> { |
| let environment = self.spawn_nested(env_name).await?; |
| |
| environment.connector.ok_or_else(|| format_err!("connector not created")) |
| } |
| |
| /// Initializes storage and registers handler generation functions for the configured setting |
| /// types. |
| async fn register_setting_handlers<F>( |
| components: &HashSet<SettingType>, |
| device_storage_factory: Arc<T>, |
| fidl_storage_factory: Arc<F>, |
| controller_flags: &HashSet<ControllerFlag>, |
| factory_handle: &mut SettingHandlerFactoryImpl, |
| ) where |
| F: StorageFactory<Storage = FidlStorage>, |
| { |
| // Accessibility |
| if components.contains(&SettingType::Accessibility) { |
| device_storage_factory |
| .initialize::<AccessibilityController>() |
| .await |
| .expect("storage should still be initializing"); |
| factory_handle.register( |
| SettingType::Accessibility, |
| Box::new(DataHandler::<AccessibilityController>::spawn), |
| ); |
| } |
| |
| // Audio |
| if components.contains(&SettingType::Audio) { |
| device_storage_factory |
| .initialize::<AudioController>() |
| .await |
| .expect("storage should still be initializing"); |
| factory_handle |
| .register(SettingType::Audio, Box::new(DataHandler::<AudioController>::spawn)); |
| } |
| |
| // Display |
| if components.contains(&SettingType::Display) { |
| device_storage_factory |
| .initialize::<DisplayController>() |
| .await |
| .expect("storage should still be initializing"); |
| factory_handle.register( |
| SettingType::Display, |
| Box::new( |
| if controller_flags.contains(&ControllerFlag::ExternalBrightnessControl) { |
| DataHandler::<DisplayController<ExternalBrightnessControl>>::spawn |
| } else { |
| DataHandler::<DisplayController>::spawn |
| }, |
| ), |
| ); |
| } |
| |
| // Light |
| if components.contains(&SettingType::Light) { |
| fidl_storage_factory |
| .initialize::<LightController>() |
| .await |
| .expect("storage should still be initializing"); |
| factory_handle |
| .register(SettingType::Light, Box::new(DataHandler::<LightController>::spawn)); |
| } |
| |
| // Input |
| if components.contains(&SettingType::Input) { |
| device_storage_factory |
| .initialize::<InputController>() |
| .await |
| .expect("storage should still be initializing"); |
| factory_handle |
| .register(SettingType::Input, Box::new(DataHandler::<InputController>::spawn)); |
| } |
| |
| // Intl |
| if components.contains(&SettingType::Intl) { |
| device_storage_factory |
| .initialize::<IntlController>() |
| .await |
| .expect("storage should still be initializing"); |
| factory_handle |
| .register(SettingType::Intl, Box::new(DataHandler::<IntlController>::spawn)); |
| } |
| |
| // Keyboard |
| if components.contains(&SettingType::Keyboard) { |
| device_storage_factory |
| .initialize::<KeyboardController>() |
| .await |
| .expect("storage should still be initializing"); |
| factory_handle.register( |
| SettingType::Keyboard, |
| Box::new(DataHandler::<KeyboardController>::spawn), |
| ); |
| } |
| |
| // Do not disturb |
| if components.contains(&SettingType::DoNotDisturb) { |
| device_storage_factory |
| .initialize::<DoNotDisturbController>() |
| .await |
| .expect("storage should still be initializing"); |
| factory_handle.register( |
| SettingType::DoNotDisturb, |
| Box::new(DataHandler::<DoNotDisturbController>::spawn), |
| ); |
| } |
| |
| // Factory Reset |
| if components.contains(&SettingType::FactoryReset) { |
| device_storage_factory |
| .initialize::<FactoryResetController>() |
| .await |
| .expect("storage should still be initializing"); |
| factory_handle.register( |
| SettingType::FactoryReset, |
| Box::new(DataHandler::<FactoryResetController>::spawn), |
| ); |
| } |
| |
| // Night mode |
| if components.contains(&SettingType::NightMode) { |
| device_storage_factory |
| .initialize::<NightModeController>() |
| .await |
| .expect("storage should still be initializing"); |
| factory_handle.register( |
| SettingType::NightMode, |
| Box::new(DataHandler::<NightModeController>::spawn), |
| ); |
| } |
| |
| // Privacy |
| if components.contains(&SettingType::Privacy) { |
| device_storage_factory |
| .initialize::<PrivacyController>() |
| .await |
| .expect("storage should still be initializing"); |
| factory_handle |
| .register(SettingType::Privacy, Box::new(DataHandler::<PrivacyController>::spawn)); |
| } |
| |
| // Setup |
| if components.contains(&SettingType::Setup) { |
| device_storage_factory |
| .initialize::<SetupController>() |
| .await |
| .expect("storage should still be initializing"); |
| factory_handle |
| .register(SettingType::Setup, Box::new(DataHandler::<SetupController>::spawn)); |
| } |
| } |
| } |
| |
| /// Brings up the settings service environment. |
| /// |
| /// This method generates the necessary infrastructure to support the settings |
| /// service (handlers, agents, etc.) and brings up the components necessary to |
| /// support the components specified in the components HashSet. |
| #[allow(clippy::too_many_arguments)] |
| async fn create_environment<'a, T, F>( |
| mut service_dir: ServiceFsDir<'_, ServiceObj<'a, ()>>, |
| delegate: service::message::Delegate, |
| job_seeder: Seeder, |
| components: HashSet<SettingType>, |
| registrants: Vec<Registrant>, |
| agent_blueprints: Vec<AgentCreator>, |
| event_subscriber_blueprints: Vec<event::subscriber::BlueprintHandle>, |
| service_context: Arc<ServiceContext>, |
| handler_factory: Arc<Mutex<SettingHandlerFactoryImpl>>, |
| device_storage_factory: Arc<T>, |
| fidl_storage_factory: Arc<F>, |
| setting_proxies_node: &fuchsia_inspect::Node, |
| listener_logger: Arc<Mutex<ListenerInspectLogger>>, |
| ) -> Result<HashSet<Entity>, Error> |
| where |
| T: StorageFactory<Storage = DeviceStorage> + Send + Sync + 'static, |
| F: StorageFactory<Storage = FidlStorage> + Send + Sync + 'static, |
| { |
| for blueprint in event_subscriber_blueprints { |
| blueprint.create(delegate.clone()).await; |
| } |
| |
| let mut entities = HashSet::new(); |
| |
| for setting_type in &components { |
| let _ = SettingProxy::create( |
| *setting_type, |
| handler_factory.clone(), |
| delegate.clone(), |
| DEFAULT_SETTING_PROXY_MAX_ATTEMPTS, |
| DEFAULT_TEARDOWN_TIMEOUT, |
| Some(DEFAULT_SETTING_PROXY_RESPONSE_TIMEOUT_MS.millis()), |
| true, |
| setting_proxies_node.create_child(format!("{setting_type:?}")), |
| Arc::clone(&listener_logger), |
| ) |
| .await?; |
| |
| let _ = entities.insert(Entity::Handler(*setting_type)); |
| } |
| |
| let mut agent_authority = Authority::create(delegate.clone(), components.clone()).await?; |
| |
| for registrant in registrants { |
| if registrant.get_dependencies().iter().all(|dependency| { |
| let dep_met = dependency.is_fulfilled(&entities); |
| if !dep_met { |
| tracing::error!( |
| "Skipping {} registration due to missing dependency {:?}", |
| registrant.get_interface(), |
| dependency |
| ); |
| } |
| dep_met |
| }) { |
| registrant.register(&job_seeder, &mut service_dir); |
| } |
| } |
| |
| // The service does not work without storage, so ensure it is always included first. |
| agent_authority |
| .register(crate::agent::storage_agent::create_registrar( |
| device_storage_factory, |
| fidl_storage_factory, |
| )) |
| .await; |
| |
| for blueprint in agent_blueprints { |
| agent_authority.register(blueprint).await; |
| } |
| |
| // Execute initialization agents sequentially |
| agent_authority |
| .execute_lifespan(Lifespan::Initialization, Arc::clone(&service_context), true) |
| .await |
| .context("Agent initialization failed")?; |
| |
| // Execute service agents concurrently |
| agent_authority |
| .execute_lifespan(Lifespan::Service, Arc::clone(&service_context), false) |
| .await |
| .context("Agent service start failed")?; |
| |
| Ok(entities) |
| } |
| |
| #[cfg(test)] |
| mod tests; |