| // 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. |
| |
| // This declaration is required to support the `select!`. |
| #![recursion_limit = "256"] |
| |
| use { |
| crate::accessibility::accessibility_controller::AccessibilityController, |
| crate::account::account_controller::AccountController, |
| crate::agent::authority_impl::AuthorityImpl, |
| crate::agent::base::{ |
| Authority, BlueprintHandle as AgentBlueprintHandle, InitializationContext, Lifespan, |
| }, |
| crate::audio::audio_controller::AudioController, |
| crate::config::base::ControllerFlag, |
| crate::device::device_controller::DeviceController, |
| crate::display::display_controller::DisplayController, |
| crate::display::light_sensor_controller::LightSensorController, |
| crate::do_not_disturb::do_not_disturb_controller::DoNotDisturbController, |
| crate::input::input_controller::InputController, |
| crate::inspect::inspect_broker::InspectBroker, |
| crate::intl::intl_controller::IntlController, |
| crate::light::light_controller::LightController, |
| crate::night_mode::night_mode_controller::NightModeController, |
| crate::power::power_controller::PowerController, |
| crate::privacy::privacy_controller::PrivacyController, |
| crate::registry::base::GenerateHandler, |
| crate::registry::device_storage::DeviceStorageFactory, |
| crate::registry::registry_impl::RegistryImpl, |
| crate::registry::setting_handler::persist::Handler as DataHandler, |
| crate::registry::setting_handler::Handler, |
| crate::registry::setting_handler_factory_impl::SettingHandlerFactoryImpl, |
| crate::service_context::GenerateService, |
| crate::service_context::ServiceContext, |
| crate::service_context::ServiceContextHandle, |
| crate::setup::setup_controller::SetupController, |
| crate::switchboard::accessibility_types::AccessibilityInfo, |
| crate::switchboard::base::{ |
| AudioInfo, DisplayInfo, DoNotDisturbInfo, InputInfo, NightModeInfo, PrivacyInfo, |
| SettingType, SetupInfo, |
| }, |
| crate::switchboard::intl_types::IntlInfo, |
| crate::switchboard::light_types::LightInfo, |
| crate::switchboard::switchboard_impl::SwitchboardBuilder, |
| anyhow::{format_err, Error}, |
| fidl_fuchsia_settings::*, |
| fuchsia_async as fasync, |
| fuchsia_component::server::{NestedEnvironment, ServiceFs, ServiceFsDir, ServiceObj}, |
| fuchsia_inspect::component, |
| fuchsia_syslog::fx_log_err, |
| futures::lock::Mutex, |
| futures::StreamExt, |
| serde::{Deserialize, Serialize}, |
| std::collections::{HashMap, HashSet}, |
| std::sync::Arc, |
| }; |
| |
| mod accessibility; |
| mod account; |
| mod audio; |
| mod clock; |
| mod device; |
| mod display; |
| mod do_not_disturb; |
| mod fidl_clone; |
| mod fidl_processor; |
| mod input; |
| mod inspect; |
| mod internal; |
| mod intl; |
| mod light; |
| mod night_mode; |
| mod power; |
| mod privacy; |
| mod setup; |
| |
| pub mod agent; |
| pub mod config; |
| pub mod fidl_common; |
| pub mod message; |
| pub mod registry; |
| pub mod service_context; |
| pub mod switchboard; |
| |
| /// 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. |
| enum Runtime { |
| Service, |
| Nested(&'static str), |
| } |
| |
| #[derive(PartialEq, Debug, Clone, Serialize, Deserialize)] |
| pub struct ServiceConfiguration { |
| pub services: HashSet<SettingType>, |
| #[serde(default)] |
| pub controller_flags: HashSet<ControllerFlag>, |
| } |
| |
| impl ServiceConfiguration { |
| pub fn with_services(services: HashSet<SettingType>) -> Self { |
| Self { services, controller_flags: HashSet::new() } |
| } |
| } |
| |
| /// 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. |
| pub struct Environment { |
| pub nested_environment: Option<NestedEnvironment>, |
| } |
| |
| impl Environment { |
| pub fn new(nested_environment: Option<NestedEnvironment>) -> Environment { |
| Environment { nested_environment: nested_environment } |
| } |
| } |
| |
| /// The EnvironmentBuilder aggregates the parameters surrounding an environment |
| /// and ultimately spawns an environment based on them. |
| pub struct EnvironmentBuilder<T: DeviceStorageFactory + Send + Sync + 'static> { |
| configuration: Option<ServiceConfiguration>, |
| agent_blueprints: Vec<AgentBlueprintHandle>, |
| event_subscriber_blueprints: Vec<internal::event::subscriber::BlueprintHandle>, |
| storage_factory: Arc<Mutex<T>>, |
| generate_service: Option<GenerateService>, |
| handlers: HashMap<SettingType, GenerateHandler<T>>, |
| } |
| |
| macro_rules! register_handler { |
| ($handler_factory:ident, $setting_type:expr, $spawn_method:expr) => { |
| $handler_factory.register($setting_type, Box::new($spawn_method)); |
| }; |
| } |
| |
| /// This macro conditionally adds a FIDL service handler based on the presence |
| /// of `SettingType`s in the available components. The caller specifies the |
| /// mod containing a generated fidl_io mod to handle the incoming request |
| /// streams, the target FIDL interface, and a list of `SettingType`s whose |
| /// presence will cause this handler to be included. |
| macro_rules! register_fidl_handler { |
| ($components:ident, $service_dir:ident, $messenger_factory:ident, |
| $interface:ident, $handler_mod:ident$(, $target:ident)+) => { |
| if false $(|| $components.contains(&SettingType::$target))+ |
| { |
| let factory = $messenger_factory.clone(); |
| $service_dir.add_fidl_service(move |stream: paste::item!{[<$interface RequestStream>]}| { |
| crate::$handler_mod::fidl_io::spawn(factory.clone(), stream); |
| }); |
| } |
| } |
| } |
| |
| impl<T: DeviceStorageFactory + Send + Sync + 'static> EnvironmentBuilder<T> { |
| pub fn new(storage_factory: Arc<Mutex<T>>) -> EnvironmentBuilder<T> { |
| EnvironmentBuilder { |
| configuration: None, |
| agent_blueprints: vec![], |
| event_subscriber_blueprints: vec![], |
| storage_factory: storage_factory, |
| generate_service: None, |
| handlers: HashMap::new(), |
| } |
| } |
| |
| pub fn handler( |
| mut self, |
| setting_type: SettingType, |
| generate_handler: GenerateHandler<T>, |
| ) -> EnvironmentBuilder<T> { |
| 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) -> EnvironmentBuilder<T> { |
| self.generate_service = Some(generate_service); |
| self |
| } |
| |
| /// A preset configuration to load preset parameters as a base. |
| pub fn configuration(mut self, configuration: ServiceConfiguration) -> EnvironmentBuilder<T> { |
| self.configuration = Some(configuration); |
| self |
| } |
| |
| /// Setting types to participate. |
| pub fn settings(mut self, settings: &[SettingType]) -> EnvironmentBuilder<T> { |
| let controller_flags = |
| self.configuration.take().map(|c| c.controller_flags).unwrap_or_else(|| HashSet::new()); |
| self.configuration(ServiceConfiguration { |
| services: settings.to_vec().into_iter().collect(), |
| controller_flags, |
| }) |
| } |
| |
| /// Setting types to participate with customized controllers. |
| pub fn flags(mut self, controller_flags: &[ControllerFlag]) -> EnvironmentBuilder<T> { |
| let services = |
| self.configuration.take().map(|c| c.services).unwrap_or_else(|| HashSet::new()); |
| self.configuration(ServiceConfiguration { |
| services, |
| controller_flags: controller_flags.iter().map(|f| *f).collect(), |
| }) |
| } |
| |
| pub fn agents(mut self, blueprints: &[AgentBlueprintHandle]) -> EnvironmentBuilder<T> { |
| self.agent_blueprints.append(&mut blueprints.to_vec()); |
| self |
| } |
| |
| /// Event subscribers to participate |
| pub fn event_subscribers( |
| mut self, |
| subscribers: &[internal::event::subscriber::BlueprintHandle], |
| ) -> EnvironmentBuilder<T> { |
| self.event_subscriber_blueprints.append(&mut subscribers.to_vec()); |
| self |
| } |
| |
| async fn prepare_env( |
| self, |
| runtime: Runtime, |
| ) -> Result<ServiceFs<ServiceObj<'static, ()>>, Error> { |
| let mut fs = ServiceFs::new(); |
| // Initialize inspect. |
| component::inspector().serve(&mut fs).ok(); |
| let service_dir = |
| if let Runtime::Service = runtime { fs.dir("svc") } else { fs.root_dir() }; |
| |
| let (settings, flags) = match self.configuration { |
| Some(configuration) => (configuration.services, configuration.controller_flags), |
| _ => (HashSet::new(), HashSet::new()), |
| }; |
| |
| let service_context = ServiceContext::create(self.generate_service); |
| |
| let mut handler_factory = SettingHandlerFactoryImpl::new( |
| settings.clone(), |
| service_context.clone(), |
| self.storage_factory.clone(), |
| ); |
| |
| EnvironmentBuilder::get_configuration_handlers(&flags, &mut handler_factory); |
| |
| // 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); |
| } |
| |
| if create_environment( |
| service_dir, |
| settings, |
| self.agent_blueprints, |
| self.event_subscriber_blueprints, |
| service_context, |
| Arc::new(Mutex::new(handler_factory)), |
| ) |
| .await |
| .is_err() |
| { |
| return Err(format_err!("could not create environment")); |
| } |
| |
| Ok(fs) |
| } |
| |
| pub fn spawn(self, mut executor: fasync::Executor) -> Result<(), Error> { |
| match executor.run_singlethreaded(self.prepare_env(Runtime::Service)) { |
| Ok(mut fs) => { |
| fs.take_and_serve_directory_handle().expect("could not service directory handle"); |
| let () = executor.run_singlethreaded(fs.collect()); |
| |
| Ok(()) |
| } |
| Err(error) => Err(error), |
| } |
| } |
| |
| pub async fn spawn_nested(self, env_name: &'static str) -> Result<Environment, Error> { |
| match self.prepare_env(Runtime::Nested(env_name)).await { |
| Ok(mut fs) => { |
| let nested_environment = Some(fs.create_salted_nested_environment(&env_name)?); |
| fasync::spawn(fs.collect()); |
| |
| Ok(Environment::new(nested_environment)) |
| } |
| Err(error) => Err(error), |
| } |
| } |
| |
| /// Spawns a nested environment and returns the associated |
| /// NestedEnvironment. Note that this is a helper function that provides a |
| /// shortcut for calling EnvironmentBuilder::name() and |
| /// EnvironmentBuilder::spawn(). |
| pub async fn spawn_and_get_nested_environment( |
| self, |
| env_name: &'static str, |
| ) -> Result<NestedEnvironment, Error> { |
| let environment = self.spawn_nested(env_name).await?; |
| |
| if let Some(env) = environment.nested_environment { |
| return Ok(env); |
| } |
| |
| return Err(format_err!("nested environment not created")); |
| } |
| |
| fn get_configuration_handlers( |
| _controller_flags: &HashSet<ControllerFlag>, |
| factory_handle: &mut SettingHandlerFactoryImpl<T>, |
| ) { |
| // Power |
| register_handler!(factory_handle, SettingType::Power, Handler::<PowerController>::spawn); |
| // Accessibility |
| register_handler!( |
| factory_handle, |
| SettingType::Accessibility, |
| DataHandler::<AccessibilityInfo, AccessibilityController>::spawn |
| ); |
| // Account |
| register_handler!( |
| factory_handle, |
| SettingType::Account, |
| Handler::<AccountController>::spawn |
| ); |
| // Audio |
| register_handler!( |
| factory_handle, |
| SettingType::Audio, |
| DataHandler::<AudioInfo, AudioController>::spawn |
| ); |
| // Device |
| register_handler!(factory_handle, SettingType::Device, Handler::<DeviceController>::spawn); |
| // Display |
| register_handler!( |
| factory_handle, |
| SettingType::Display, |
| DataHandler::<DisplayInfo, DisplayController>::spawn |
| ); |
| // Light |
| register_handler!( |
| factory_handle, |
| SettingType::Light, |
| DataHandler::<LightInfo, LightController>::spawn |
| ); |
| // Light sensor |
| register_handler!( |
| factory_handle, |
| SettingType::LightSensor, |
| Handler::<LightSensorController>::spawn |
| ); |
| // Input |
| register_handler!( |
| factory_handle, |
| SettingType::Input, |
| DataHandler::<InputInfo, InputController>::spawn |
| ); |
| // Intl |
| register_handler!( |
| factory_handle, |
| SettingType::Intl, |
| DataHandler::<IntlInfo, IntlController>::spawn |
| ); |
| // Do not disturb |
| register_handler!( |
| factory_handle, |
| SettingType::DoNotDisturb, |
| DataHandler::<DoNotDisturbInfo, DoNotDisturbController>::spawn |
| ); |
| // Night mode |
| register_handler!( |
| factory_handle, |
| SettingType::NightMode, |
| DataHandler::<NightModeInfo, NightModeController>::spawn |
| ); |
| // Privacy |
| register_handler!( |
| factory_handle, |
| SettingType::Privacy, |
| DataHandler::<PrivacyInfo, PrivacyController>::spawn |
| ); |
| // Setup |
| register_handler!( |
| factory_handle, |
| SettingType::Setup, |
| DataHandler::<SetupInfo, SetupController>::spawn |
| ); |
| } |
| } |
| |
| /// Brings up the settings service environment. |
| /// |
| /// This method generates the necessary infrastructure to support the settings |
| /// service (switchboard, registry, etc.) and brings up the components necessary |
| /// to support the components specified in the components HashSet. |
| async fn create_environment<'a, T: DeviceStorageFactory + Send + Sync + 'static>( |
| mut service_dir: ServiceFsDir<'_, ServiceObj<'a, ()>>, |
| components: HashSet<SettingType>, |
| agent_blueprints: Vec<AgentBlueprintHandle>, |
| event_subscriber_blueprints: Vec<internal::event::subscriber::BlueprintHandle>, |
| service_context_handle: ServiceContextHandle, |
| handler_factory: Arc<Mutex<SettingHandlerFactoryImpl<T>>>, |
| ) -> Result<(), Error> { |
| let registry_messenger_factory = internal::core::message::create_hub(); |
| let switchboard_messenger_factory = internal::switchboard::message::create_hub(); |
| let setting_handler_messenger_factory = internal::handler::message::create_hub(); |
| let event_messenger_factory = internal::event::message::create_hub(); |
| |
| for blueprint in event_subscriber_blueprints { |
| blueprint.create(event_messenger_factory.clone()).await; |
| } |
| |
| // Attach inspect broker, which watches messages between registry and setting handlers to |
| // record settings values to inspect. |
| let inspect_broker_node = component::inspector().root().create_child("setting_values"); |
| InspectBroker::create(setting_handler_messenger_factory.clone(), inspect_broker_node) |
| .await |
| .expect("could not create inspect"); |
| |
| // Creates switchboard, handed to interface implementations to send messages |
| // to handlers. |
| SwitchboardBuilder::create() |
| .registry_messenger_factory(registry_messenger_factory.clone()) |
| .switchboard_messenger_factory(switchboard_messenger_factory.clone()) |
| .build() |
| .await |
| .expect("could not create switchboard"); |
| |
| let mut agent_authority = AuthorityImpl::create( |
| internal::agent::message::create_hub(), |
| switchboard_messenger_factory.clone(), |
| event_messenger_factory.clone(), |
| ) |
| .await?; |
| |
| // Creates registry, used to register handlers for setting types. |
| let _ = RegistryImpl::create( |
| handler_factory.clone(), |
| registry_messenger_factory.clone(), |
| setting_handler_messenger_factory, |
| ) |
| .await |
| .expect("could not create registry"); |
| |
| register_fidl_handler!( |
| components, |
| service_dir, |
| switchboard_messenger_factory, |
| Light, |
| light, |
| Light |
| ); |
| |
| register_fidl_handler!( |
| components, |
| service_dir, |
| switchboard_messenger_factory, |
| Accessibility, |
| accessibility, |
| Accessibility |
| ); |
| |
| register_fidl_handler!( |
| components, |
| service_dir, |
| switchboard_messenger_factory, |
| Audio, |
| audio, |
| Audio |
| ); |
| |
| register_fidl_handler!( |
| components, |
| service_dir, |
| switchboard_messenger_factory, |
| Device, |
| device, |
| Device |
| ); |
| |
| register_fidl_handler!( |
| components, |
| service_dir, |
| switchboard_messenger_factory, |
| Display, |
| display, |
| Display, |
| LightSensor |
| ); |
| |
| register_fidl_handler!( |
| components, |
| service_dir, |
| switchboard_messenger_factory, |
| DoNotDisturb, |
| do_not_disturb, |
| DoNotDisturb |
| ); |
| |
| register_fidl_handler!( |
| components, |
| service_dir, |
| switchboard_messenger_factory, |
| Intl, |
| intl, |
| Intl |
| ); |
| |
| register_fidl_handler!( |
| components, |
| service_dir, |
| switchboard_messenger_factory, |
| NightMode, |
| night_mode, |
| NightMode |
| ); |
| |
| register_fidl_handler!( |
| components, |
| service_dir, |
| switchboard_messenger_factory, |
| Privacy, |
| privacy, |
| Privacy |
| ); |
| |
| register_fidl_handler!( |
| components, |
| service_dir, |
| switchboard_messenger_factory, |
| Input, |
| input, |
| Input |
| ); |
| |
| register_fidl_handler!( |
| components, |
| service_dir, |
| switchboard_messenger_factory, |
| Setup, |
| setup, |
| Setup |
| ); |
| |
| for blueprint in agent_blueprints { |
| if agent_authority.register(blueprint).await.is_err() { |
| fx_log_err!("failed to register agent via blueprint"); |
| } |
| } |
| |
| // Execute initialization agents sequentially |
| if agent_authority |
| .execute_lifespan( |
| Lifespan::Initialization(InitializationContext { available_components: components }), |
| service_context_handle.clone(), |
| true, |
| ) |
| .await |
| .is_err() |
| { |
| return Err(format_err!("Agent initialization failed")); |
| } |
| |
| // Execute service agents concurrently |
| agent_authority |
| .execute_lifespan(Lifespan::Service, service_context_handle.clone(), false) |
| .await |
| .ok(); |
| |
| return Ok(()); |
| } |
| |
| #[cfg(test)] |
| mod tests; |