blob: 139ae0ce806ee0f5d0a265b94009b0ea28e0f2a4 [file] [log] [blame]
// Copyright 2020 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.
use crate::agent::storage::storage_factory::DeviceStorageFactory;
use crate::audio::policy::{self as audio, State};
use crate::base::SettingType;
use crate::generate_inspect_with_info;
use crate::payload_convert;
use crate::policy::policy_handler::PolicyHandler;
use crate::policy::response::Response;
use crate::service;
use anyhow::Error;
use async_trait::async_trait;
use futures::future::BoxFuture;
use serde::{Deserialize, Serialize};
use std::borrow::Cow;
use std::convert::TryFrom;
use std::sync::Arc;
use thiserror::Error;
/// Defines the policy handler trait, which describes a component that persists and applies the
/// policies specified by policy clients
pub mod policy_handler;
/// Defines a proxy between the policy FIDL handler and policy handler that is responsible for
/// intercepting incoming setting requests and returning responses to the policy FIDL handler.
pub mod policy_proxy;
/// Defines a factory for creating policy handlers on demand.
pub mod policy_handler_factory_impl;
/// The policy types supported by the service.
#[derive(PartialEq, Debug, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
pub enum PolicyType {
/// This type is reserved for testing purposes.
#[cfg(test)]
Unknown,
Audio,
}
pub(crate) trait HasPolicyType {
const POLICY_TYPE: PolicyType;
}
impl PolicyType {
/// Returns the corresponding setting type that this policy controls.
pub(crate) fn setting_type(&self) -> SettingType {
match self {
#[cfg(test)]
PolicyType::Unknown => SettingType::Unknown,
PolicyType::Audio => SettingType::Audio,
}
}
}
generate_inspect_with_info! {
/// Enumeration over the possible policy state information for all policies.
#[derive(PartialEq, Debug, Clone)]
pub enum PolicyInfo {
/// This value is reserved for testing purposes.
#[cfg(test)]
Unknown(UnknownInfo),
Audio(State),
}
}
macro_rules! conversion_impls {
($($(#[cfg($test:meta)])? $variant:ident($info_ty:ty) => $ty_variant:ident ),+ $(,)?) => {
$(
$(#[cfg($test)])?
impl HasPolicyType for $info_ty {
const POLICY_TYPE: PolicyType = PolicyType::$ty_variant;
}
$(#[cfg($test)])?
impl TryFrom<PolicyInfo> for $info_ty {
type Error = ();
fn try_from(setting_info: PolicyInfo) -> Result<Self, ()> {
// Remove allow once additional non-test variant is added.
#[allow(unreachable_patterns)]
match setting_info {
PolicyInfo::$variant(info) => Ok(info),
_ => Err(()),
}
}
}
)+
}
}
conversion_impls! {
#[cfg(test)] Unknown(UnknownInfo) => Unknown,
Audio(State) => Audio,
}
#[cfg(test)]
mod testing {
use super::{PolicyInfo, UnknownInfo};
use crate::agent::storage::device_storage::DeviceStorageCompatible;
impl DeviceStorageCompatible for UnknownInfo {
const KEY: &'static str = "unknown_info";
fn default_value() -> Self {
Self(false)
}
}
impl From<UnknownInfo> for PolicyInfo {
fn from(unknown_info: UnknownInfo) -> Self {
PolicyInfo::Unknown(unknown_info)
}
}
}
impl From<State> for PolicyInfo {
fn from(state: State) -> Self {
PolicyInfo::Audio(state)
}
}
impl From<&PolicyInfo> for PolicyType {
fn from(policy_info: &PolicyInfo) -> Self {
match policy_info {
#[cfg(test)]
PolicyInfo::Unknown(_) => PolicyType::Unknown,
PolicyInfo::Audio(_) => PolicyType::Audio,
}
}
}
/// This struct is reserved for testing purposes.
#[derive(PartialEq, Debug, Copy, Clone, Serialize, Deserialize)]
#[cfg(test)]
pub struct UnknownInfo(pub bool);
#[derive(PartialEq, Clone, Debug)]
pub enum Payload {
Request(Request),
Response(Response),
}
payload_convert!(Policy, Payload);
/// `Role` defines grouping for responsibilities on the policy message hub.
#[derive(PartialEq, Copy, Clone, Debug, Eq, Hash)]
pub enum Role {
/// This role indicates that the messenger handles and enacts policy requests.
PolicyHandler,
}
/// `Request` defines the request space for all policies handled by
/// the Setting Service. Note that the actions that can be taken upon each
/// policy should be defined within each policy's Request enum.
#[derive(PartialEq, Debug, Clone)]
pub enum Request {
/// Fetches the current policy state.
Get,
/// Restore saved state from disk.
Restore,
/// Request targeted to the Audio policy.
Audio(audio::Request),
}
pub mod response {
use super::*;
pub type Response = Result<Payload, Error>;
/// `Payload` defines the possible successful responses for a request. There
/// should be a corresponding policy response payload type for each request type.
#[derive(PartialEq, Debug, Clone)]
pub enum Payload {
PolicyInfo(PolicyInfo),
Restore,
Audio(audio::Response),
}
/// The possible errors that can be returned from a request. Note that
/// unlike the request and response space, errors are not type specific.
#[derive(Error, Debug, Clone, PartialEq)]
pub enum Error {
#[error("Unexpected error")]
Unexpected,
#[error("Communication error")]
CommunicationError,
#[error("Invalid input argument for policy: {0:?} argument:{1:?} value:{2:?}")]
InvalidArgument(PolicyType, Cow<'static, str>, Cow<'static, str>),
#[error("Write failed for policy: {0:?}")]
WriteFailure(PolicyType),
}
}
pub type BoxedHandler = Box<dyn PolicyHandler + Send + Sync>;
pub type GenerateHandlerResult = Result<BoxedHandler, Error>;
pub type GenerateHandler<T> =
Box<dyn Fn(Context<T>) -> BoxFuture<'static, GenerateHandlerResult> + Send + Sync>;
/// Context captures all details necessary for a policy handler to execute in a given
/// settings service environment.
pub struct Context<T: DeviceStorageFactory> {
pub policy_type: PolicyType,
pub service_messenger: service::message::Messenger,
pub storage_factory: Arc<T>,
pub id: u64,
}
/// A factory capable of creating a policy handler for a given setting on-demand. If no
/// viable handler can be created, None will be returned.
#[async_trait]
pub trait PolicyHandlerFactory {
async fn generate(
&mut self,
policy_type: PolicyType,
service_messenger: service::message::Messenger,
) -> Result<BoxedHandler, PolicyHandlerFactoryError>;
}
#[derive(thiserror::Error, Debug, Clone, PartialEq)]
pub enum PolicyHandlerFactoryError {
#[error("Policy type {0:?} not registered in environment")]
PolicyNotFound(PolicyType),
#[error("Setting type {0:?} for policy {0:?} not registered in environment")]
SettingNotFound(SettingType, PolicyType),
#[error("Cannot find policy handler generator for {0:?}")]
GeneratorNotFound(PolicyType),
#[error("Policy handler {0:?} failed to startup")]
HandlerStartupError(PolicyType),
}