blob: de04d4ad772e6468331cfe6e0024c37f0f93097f [file] [log] [blame]
// 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::Authority,
crate::agent::{BlueprintHandle as AgentBlueprintHandle, Lifespan},
crate::audio::audio_controller::AudioController,
crate::audio::policy::audio_policy_handler::AudioPolicyHandler,
crate::base::SettingType,
crate::config::base::{AgentType, ControllerFlag},
crate::device::device_controller::DeviceController,
crate::display::display_controller::{DisplayController, ExternalBrightnessControl},
crate::display::light_sensor_controller::LightSensorController,
crate::do_not_disturb::do_not_disturb_controller::DoNotDisturbController,
crate::factory_reset::factory_reset_controller::FactoryResetController,
crate::handler::base::GenerateHandler,
crate::handler::device_storage::DeviceStorageFactory,
crate::handler::setting_handler::persist::Handler as DataHandler,
crate::handler::setting_handler_factory_impl::SettingHandlerFactoryImpl,
crate::handler::setting_proxy::SettingProxy,
crate::input::input_controller::InputController,
crate::intl::intl_controller::IntlController,
crate::light::light_controller::LightController,
crate::monitor::base as monitor_base,
crate::night_mode::night_mode_controller::NightModeController,
crate::policy::policy_handler,
crate::policy::policy_handler_factory_impl::PolicyHandlerFactoryImpl,
crate::policy::policy_proxy::PolicyProxy,
crate::policy::PolicyType,
crate::power::power_controller::PowerController,
crate::privacy::privacy_controller::PrivacyController,
crate::service::message::Factory as MessengerFactory,
crate::service_context::GenerateService,
crate::service_context::ServiceContext,
crate::setup::setup_controller::SetupController,
anyhow::{format_err, Error},
fidl_fuchsia_settings::{
AccessibilityRequestStream, AudioRequestStream, DeviceRequestStream, DisplayRequestStream,
DoNotDisturbRequestStream, FactoryResetRequestStream, InputRequestStream,
IntlRequestStream, LightRequestStream, NightModeRequestStream, PrivacyRequestStream,
SetupRequestStream,
},
fidl_fuchsia_settings_policy::VolumePolicyControllerRequestStream,
fuchsia_async as fasync,
fuchsia_component::server::{NestedEnvironment, ServiceFs, ServiceFsDir, ServiceObj},
fuchsia_inspect::component,
fuchsia_zircon::{Duration, DurationNum},
futures::lock::Mutex,
futures::StreamExt,
handler::setting_handler::Handler,
serde::Deserialize,
std::collections::{HashMap, HashSet},
std::sync::atomic::AtomicU64,
std::sync::Arc,
};
mod accessibility;
mod account;
mod audio;
mod clock;
mod device;
mod display;
mod do_not_disturb;
mod event;
mod factory_reset;
mod fidl_processor;
mod hanging_get_handler;
mod input;
mod intl;
mod light;
mod night_mode;
mod policy;
mod power;
mod privacy;
mod service;
mod setup;
pub mod task;
pub use display::display_configuration::DisplayConfiguration;
pub use display::LightSensorConfig;
pub use input::input_device_configuration::InputConfiguration;
pub use light::light_hardware_configuration::LightHardwareConfiguration;
pub use service::{Address, Payload, Role};
pub mod agent;
pub mod base;
pub mod config;
pub mod fidl_common;
pub mod handler;
pub mod message;
pub mod monitor;
pub mod service_context;
pub mod storage;
/// 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,
Nested(&'static str),
}
#[derive(Debug, Default, Clone, Deserialize)]
pub struct AgentConfiguration {
pub agent_types: HashSet<AgentType>,
}
#[derive(PartialEq, Debug, Clone, Deserialize)]
pub struct EnabledServicesConfiguration {
pub services: HashSet<SettingType>,
}
impl EnabledServicesConfiguration {
pub fn with_services(services: HashSet<SettingType>) -> Self {
Self { services }
}
}
#[derive(PartialEq, Debug, Clone, Deserialize)]
pub struct EnabledPoliciesConfiguration {
pub policies: HashSet<PolicyType>,
}
impl EnabledPoliciesConfiguration {
pub fn with_policies(policies: HashSet<PolicyType>) -> Self {
Self { policies }
}
}
#[derive(Default, Debug, Clone, Deserialize)]
pub struct ServiceFlags {
pub controller_flags: HashSet<ControllerFlag>,
}
#[derive(PartialEq, Debug, Default, Clone)]
pub struct ServiceConfiguration {
pub agent_types: HashSet<AgentType>,
pub services: HashSet<SettingType>,
pub policies: HashSet<PolicyType>,
pub controller_flags: HashSet<ControllerFlag>,
}
impl ServiceConfiguration {
pub fn from(
agent_types: AgentConfiguration,
services: EnabledServicesConfiguration,
policies: EnabledPoliciesConfiguration,
flags: ServiceFlags,
) -> Self {
Self {
agent_types: agent_types.agent_types,
services: services.services,
policies: policies.policies,
controller_flags: flags.controller_flags,
}
}
fn set_services(&mut self, services: HashSet<SettingType>) {
self.services = services;
}
fn set_policies(&mut self, policies: HashSet<PolicyType>) {
self.policies = policies;
}
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.
pub struct Environment {
pub nested_environment: Option<NestedEnvironment>,
pub messenger_factory: MessengerFactory,
}
impl Environment {
pub fn new(
nested_environment: Option<NestedEnvironment>,
messenger_factory: MessengerFactory,
) -> Environment {
Environment { nested_environment, messenger_factory }
}
}
/// 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>,
agent_mapping_func: Option<Box<dyn Fn(AgentType) -> AgentBlueprintHandle>>,
event_subscriber_blueprints: Vec<event::subscriber::BlueprintHandle>,
storage_factory: Arc<T>,
generate_service: Option<GenerateService>,
handlers: HashMap<SettingType, GenerateHandler>,
resource_monitors: Vec<monitor_base::monitor::Generate>,
}
macro_rules! register_handler {
(
$components:ident,
$storage_factory:ident,
$handler_factory:ident,
$setting_type:expr,
$controller:ty,
$spawn_method:expr
) => {
if $components.contains(&$setting_type) {
$storage_factory
.initialize::<$controller>()
.await
.expect("should be initializing still");
}
$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 messenger_factory = $messenger_factory.clone();
$service_dir.add_fidl_service(
move |stream: $interface| {
crate::$handler_mod::fidl_io::spawn(messenger_factory.clone(),
stream);
});
}
}
}
impl<T: DeviceStorageFactory + Send + Sync + 'static> EnvironmentBuilder<T> {
pub fn new(storage_factory: Arc<T>) -> EnvironmentBuilder<T> {
EnvironmentBuilder {
configuration: None,
agent_blueprints: vec![],
agent_mapping_func: None,
event_subscriber_blueprints: vec![],
storage_factory,
generate_service: None,
handlers: HashMap::new(),
resource_monitors: vec![],
}
}
pub fn handler(
mut self,
setting_type: SettingType,
generate_handler: GenerateHandler,
) -> 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> {
if self.configuration.is_none() {
self.configuration = Some(ServiceConfiguration::default());
}
self.configuration.as_mut().map(|c| c.set_services(settings.iter().copied().collect()));
self
}
/// Sets policies types that are enabled.
pub fn policies(mut self, policies: &[PolicyType]) -> EnvironmentBuilder<T> {
if self.configuration.is_none() {
self.configuration = Some(ServiceConfiguration::default());
}
self.configuration.as_mut().map(|c| c.set_policies(policies.iter().copied().collect()));
self
}
/// Setting types to participate with customized controllers.
pub fn flags(mut self, controller_flags: &[ControllerFlag]) -> EnvironmentBuilder<T> {
if self.configuration.is_none() {
self.configuration = Some(ServiceConfiguration::default());
}
self.configuration
.as_mut()
.map(|c| c.set_controller_flags(controller_flags.iter().map(|f| *f).collect()));
self
}
pub fn agent_mapping<F>(mut self, agent_mapping_func: F) -> EnvironmentBuilder<T>
where
F: Fn(AgentType) -> AgentBlueprintHandle + 'static,
{
self.agent_mapping_func = Some(Box::new(agent_mapping_func));
self
}
pub fn agents(mut self, blueprints: &[AgentBlueprintHandle]) -> EnvironmentBuilder<T> {
self.agent_blueprints.append(&mut blueprints.to_vec());
self
}
pub fn resource_monitors(
mut self,
monitors: &[monitor_base::monitor::Generate],
) -> EnvironmentBuilder<T> {
self.resource_monitors.append(&mut monitors.to_vec());
self
}
/// Event subscribers to participate
pub fn event_subscribers(
mut self,
subscribers: &[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, ()>>, MessengerFactory), Error> {
let mut fs = ServiceFs::new();
let service_dir;
if runtime == Runtime::Service {
// Initialize inspect.
component::inspector().serve(&mut fs).ok();
service_dir = fs.dir("svc");
} else {
service_dir = fs.root_dir();
}
// Define top level MessageHub for service communication.
let messenger_factory = service::message::create_hub();
let (agent_types, settings, policies, flags) = match self.configuration {
Some(configuration) => (
configuration.agent_types,
configuration.services,
configuration.policies,
configuration.controller_flags,
),
_ => (HashSet::new(), HashSet::new(), HashSet::new(), HashSet::new()),
};
let service_context =
Arc::new(ServiceContext::new(self.generate_service, Some(messenger_factory.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(),
);
// Create the policy handler factory and register policy handlers.
let mut policy_handler_factory = PolicyHandlerFactoryImpl::new(
policies.clone(),
settings.clone(),
self.storage_factory.clone(),
context_id_counter,
);
// If policy registration becomes configurable, then this initialization needs to be made
// configurable with the registration.
PolicyType::Audio
.initialize_storage(&self.storage_factory)
.await
.expect("was not able to initialize storage for audio policy");
policy_handler_factory.register(
PolicyType::Audio,
Box::new(policy_handler::create_handler::<AudioPolicyHandler, _>),
);
EnvironmentBuilder::get_configuration_handlers(
&settings,
Arc::clone(&self.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);
}
for agent_type in &agent_types {
agent_type
.initialize_storage(&self.storage_factory)
.await
.expect("unable to initialize storage for agent");
}
let agent_blueprints = self
.agent_mapping_func
.map(|agent_mapping_func| {
agent_types.into_iter().map(|agent_type| (agent_mapping_func)(agent_type)).collect()
})
.unwrap_or(self.agent_blueprints);
create_environment(
service_dir,
messenger_factory.clone(),
settings,
policies,
agent_blueprints,
self.resource_monitors,
self.event_subscriber_blueprints,
service_context,
Arc::new(Mutex::new(handler_factory)),
Arc::new(Mutex::new(policy_handler_factory)),
self.storage_factory,
)
.await
.map_err(|err| format_err!("could not create environment: {:?}", err))?;
Ok((fs, messenger_factory))
}
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, messenger_factory)) => {
let nested_environment = Some(fs.create_salted_nested_environment(&env_name)?);
fasync::Task::spawn(fs.collect()).detach();
Ok(Environment::new(nested_environment, messenger_factory))
}
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"));
}
async fn get_configuration_handlers(
components: &HashSet<SettingType>,
storage_factory: Arc<T>,
controller_flags: &HashSet<ControllerFlag>,
factory_handle: &mut SettingHandlerFactoryImpl,
) {
// Power
register_handler!(
components,
storage_factory,
factory_handle,
SettingType::Power,
PowerController,
Handler::<PowerController>::spawn
);
// Accessibility
register_handler!(
components,
storage_factory,
factory_handle,
SettingType::Accessibility,
AccessibilityController,
DataHandler::<AccessibilityController>::spawn
);
// Account
register_handler!(
components,
storage_factory,
factory_handle,
SettingType::Account,
AccountController,
Handler::<AccountController>::spawn
);
// Audio
register_handler!(
components,
storage_factory,
factory_handle,
SettingType::Audio,
AudioController,
DataHandler::<AudioController>::spawn
);
// Device
register_handler!(
components,
storage_factory,
factory_handle,
SettingType::Device,
DeviceController,
Handler::<DeviceController>::spawn
);
// Display
register_handler!(
components,
storage_factory,
factory_handle,
SettingType::Display,
DisplayController,
if controller_flags.contains(&ControllerFlag::ExternalBrightnessControl) {
DataHandler::<DisplayController<ExternalBrightnessControl>>::spawn
} else {
DataHandler::<DisplayController>::spawn
}
);
// Light
register_handler!(
components,
storage_factory,
factory_handle,
SettingType::Light,
LightController,
DataHandler::<LightController>::spawn
);
// Light sensor
register_handler!(
components,
storage_factory,
factory_handle,
SettingType::LightSensor,
LightSensorController,
Handler::<LightSensorController>::spawn
);
// Input
register_handler!(
components,
storage_factory,
factory_handle,
SettingType::Input,
InputController,
DataHandler::<InputController>::spawn
);
// Intl
register_handler!(
components,
storage_factory,
factory_handle,
SettingType::Intl,
IntlController,
DataHandler::<IntlController>::spawn
);
// Do not disturb
register_handler!(
components,
storage_factory,
factory_handle,
SettingType::DoNotDisturb,
DoNotDisturbController,
DataHandler::<DoNotDisturbController>::spawn
);
// Factory Reset
register_handler!(
components,
storage_factory,
factory_handle,
SettingType::FactoryReset,
FactoryResetController,
DataHandler::<FactoryResetController>::spawn
);
// Night mode
register_handler!(
components,
storage_factory,
factory_handle,
SettingType::NightMode,
NightModeController,
DataHandler::<NightModeController>::spawn
);
// Privacy
register_handler!(
components,
storage_factory,
factory_handle,
SettingType::Privacy,
PrivacyController,
DataHandler::<PrivacyController>::spawn
);
// Setup
register_handler!(
components,
storage_factory,
factory_handle,
SettingType::Setup,
SetupController,
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.
async fn create_environment<'a, T: DeviceStorageFactory + Send + Sync + 'static>(
mut service_dir: ServiceFsDir<'_, ServiceObj<'a, ()>>,
messenger_factory: service::message::Factory,
components: HashSet<SettingType>,
policies: HashSet<PolicyType>,
agent_blueprints: Vec<AgentBlueprintHandle>,
resource_monitor_generators: Vec<monitor_base::monitor::Generate>,
event_subscriber_blueprints: Vec<event::subscriber::BlueprintHandle>,
service_context: Arc<ServiceContext>,
handler_factory: Arc<Mutex<SettingHandlerFactoryImpl>>,
policy_handler_factory: Arc<Mutex<PolicyHandlerFactoryImpl<T>>>,
storage_factory: Arc<T>,
) -> Result<(), Error> {
for blueprint in event_subscriber_blueprints {
blueprint.create(messenger_factory.clone()).await;
}
let monitor_actor = if resource_monitor_generators.is_empty() {
None
} else {
Some(monitor::environment::Builder::new().add_monitors(resource_monitor_generators).build())
};
// TODO(fxbug.dev/58893): make max attempts a configurable option.
// TODO(fxbug.dev/59174): make setting proxy response timeout and retry configurable.
for setting_type in &components {
SettingProxy::create(
*setting_type,
handler_factory.clone(),
messenger_factory.clone(),
DEFAULT_SETTING_PROXY_MAX_ATTEMPTS,
DEFAULT_TEARDOWN_TIMEOUT,
Some(DEFAULT_SETTING_PROXY_RESPONSE_TIMEOUT_MS.millis()),
true,
)
.await?;
}
for policy_type in policies {
let setting_type = policy_type.setting_type();
if components.contains(&setting_type) {
PolicyProxy::create(
policy_type,
policy_handler_factory.clone(),
messenger_factory.clone(),
)
.await?;
}
}
let mut agent_authority =
Authority::create(messenger_factory.clone(), components.clone(), monitor_actor).await?;
register_fidl_handler!(
components,
service_dir,
messenger_factory,
LightRequestStream,
light,
Light
);
register_fidl_handler!(
components,
service_dir,
messenger_factory,
AccessibilityRequestStream,
accessibility,
Accessibility
);
register_fidl_handler!(
components,
service_dir,
messenger_factory,
AudioRequestStream,
audio,
Audio
);
register_fidl_handler!(
components,
service_dir,
messenger_factory,
DeviceRequestStream,
device,
Device
);
register_fidl_handler!(
components,
service_dir,
messenger_factory,
DisplayRequestStream,
display,
Display,
LightSensor
);
register_fidl_handler!(
components,
service_dir,
messenger_factory,
DoNotDisturbRequestStream,
do_not_disturb,
DoNotDisturb
);
register_fidl_handler!(
components,
service_dir,
messenger_factory,
FactoryResetRequestStream,
factory_reset,
FactoryReset
);
register_fidl_handler!(
components,
service_dir,
messenger_factory,
IntlRequestStream,
intl,
Intl
);
register_fidl_handler!(
components,
service_dir,
messenger_factory,
NightModeRequestStream,
night_mode,
NightMode
);
register_fidl_handler!(
components,
service_dir,
messenger_factory,
PrivacyRequestStream,
privacy,
Privacy
);
register_fidl_handler!(
components,
service_dir,
messenger_factory,
InputRequestStream,
input,
Input
);
register_fidl_handler!(
components,
service_dir,
messenger_factory,
SetupRequestStream,
setup,
Setup
);
// TODO(fxbug.dev/60925): allow configuration of policy API
service_dir.add_fidl_service(move |stream: VolumePolicyControllerRequestStream| {
crate::audio::policy::volume_policy_fidl_handler::fidl_io::spawn(
messenger_factory.clone(),
stream,
);
});
// The service does not work without storage, so ensure it is always included first.
agent_authority
.register(Arc::new(crate::agent::storage_agent::Blueprint::new(storage_factory)))
.await;
for blueprint in agent_blueprints {
agent_authority.register(blueprint).await;
}
// Execute initialization agents sequentially
if agent_authority
.execute_lifespan(Lifespan::Initialization, Arc::clone(&service_context), true)
.await
.is_err()
{
return Err(format_err!("Agent initialization failed"));
}
// Execute service agents concurrently
agent_authority
.execute_lifespan(Lifespan::Service, Arc::clone(&service_context), false)
.await
.ok();
return Ok(());
}
#[cfg(test)]
mod tests;