blob: 1f383d1cbca09177b80549740bd9e4dfcc23e85b [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.
use std::collections::HashSet;
use fuchsia_syslog::fx_log_warn;
use serde::{Deserialize, Serialize};
use thiserror::Error;
use crate::audio::ModifiedTimestamps;
use crate::switchboard::accessibility_types::AccessibilityInfo;
use crate::switchboard::intl_types::IntlInfo;
use crate::switchboard::light_types::{LightInfo, LightState};
use bitflags::bitflags;
/// Return type from a controller after handling a state change.
pub type ControllerStateResult = Result<(), SwitchboardError>;
pub type SettingResponseResult = Result<Option<SettingResponse>, SwitchboardError>;
/// A trait for structs where all fields are options. Recursively performs
/// [Option::or](std::option::Option::or) on each field in the struct and substructs.
pub trait Merge {
fn merge(&self, other: Self) -> Self;
}
#[derive(Error, Debug, Clone, PartialEq)]
pub enum SwitchboardError {
#[error("Unimplemented Request:{request:?} for setting type: {setting_type:?}")]
UnimplementedRequest { setting_type: SettingType, request: SettingRequest },
#[error("Storage failure for setting type: {setting_type:?}")]
StorageFailure { setting_type: SettingType },
#[error(
"Invalid argument for setting type: {setting_type:?} argument:{argument:?} value:{value:?}"
)]
InvalidArgument { setting_type: SettingType, argument: String, value: String },
#[error(
"External failure for setting type:{setting_type:?} dependency: {dependency:?} reqeust:{request:?}"
)]
ExternalFailure { setting_type: SettingType, dependency: String, request: String },
#[error("Unhandled type: {setting_type:?}")]
UnhandledType { setting_type: SettingType },
#[error("Unexpected error: {description:?}")]
UnexpectedError { description: String },
#[error("Undeliverable Request:{request:?} for setting type: {setting_type:?}")]
UndeliverableError { setting_type: SettingType, request: SettingRequest },
#[error("Communication error")]
CommunicationError,
}
/// The setting types supported by the messaging system. This is used as a key
/// for listening to change notifications and sending requests.
/// The types are arranged alphabetically.
#[derive(PartialEq, Debug, Eq, Hash, Clone, Copy, Serialize, Deserialize)]
pub enum SettingType {
Unknown,
Accessibility,
Account,
Audio,
Device,
Display,
DoNotDisturb,
Input,
Intl,
Light,
LightSensor,
NightMode,
Power,
Privacy,
Setup,
}
/// Returns all known setting types. New additions to SettingType should also
/// be inserted here.
pub fn get_all_setting_types() -> HashSet<SettingType> {
return vec![
SettingType::Accessibility,
SettingType::Audio,
SettingType::Device,
SettingType::Display,
SettingType::DoNotDisturb,
SettingType::Input,
SettingType::Intl,
SettingType::Light,
SettingType::LightSensor,
SettingType::NightMode,
SettingType::Power,
SettingType::Privacy,
SettingType::Setup,
]
.into_iter()
.collect();
}
/// Returns default setting types. These types should be product-agnostic,
/// capable of operating with platform level support.
pub fn get_default_setting_types() -> HashSet<SettingType> {
return vec![
SettingType::Accessibility,
SettingType::Device,
SettingType::Intl,
SettingType::Power,
SettingType::Privacy,
SettingType::Setup,
]
.into_iter()
.collect();
}
/// The possible requests that can be made on a setting. The sink will expect a
/// subset of the values defined below based on the associated type.
/// The types are arranged alphabetically.
#[derive(PartialEq, Debug, Clone)]
pub enum SettingRequest {
Get,
// Accessibility requests.
SetAccessibilityInfo(AccessibilityInfo),
// Account requests
ScheduleClearAccounts,
// Audio requests.
SetVolume(Vec<AudioStream>),
// Audio in requests.
SetMicMute(bool),
// Display requests.
SetBrightness(f32),
SetAutoBrightness(bool),
SetLowLightMode(LowLightMode),
// Do not disturb requests.
SetDnD(DoNotDisturbInfo),
// Intl requests.
SetIntlInfo(IntlInfo),
// Light requests.
SetLightGroupValue(String, Vec<LightState>),
// Night mode requests.
SetNightModeInfo(NightModeInfo),
// Power requests.
Reboot,
// Restores settings to outside dependencies.
Restore,
// Privacy requests.
SetUserDataSharingConsent(Option<bool>),
// Setup info requests.
SetConfigurationInterfaces(ConfigurationInterfaceFlags),
}
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
pub struct DeviceInfo {
pub build_tag: String,
}
impl DeviceInfo {
pub const fn new(build_tag: String) -> DeviceInfo {
DeviceInfo { build_tag }
}
}
#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
pub enum AudioSettingSource {
User,
System,
}
#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize, Hash, Eq)]
pub enum AudioStreamType {
Background,
Media,
Interruption,
SystemAgent,
Communication,
}
#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
pub struct AudioStream {
pub stream_type: AudioStreamType,
pub source: AudioSettingSource,
pub user_volume_level: f32,
pub user_volume_muted: bool,
}
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
pub struct InputInfo {
pub microphone: Microphone,
}
#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Microphone {
pub muted: bool,
}
#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
pub struct AudioInputInfo {
pub mic_mute: bool,
}
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
pub struct AudioInfo {
pub streams: [AudioStream; 5],
pub input: AudioInputInfo,
pub modified_timestamps: Option<ModifiedTimestamps>,
}
impl AudioInfo {
/// Selectively replaces an existing stream of the same type with the one
/// provided. The `AudioInfo` is left intact if that stream type does not
/// exist.
pub fn replace_stream(&mut self, stream: AudioStream) {
if let Some(s) = self.streams.iter_mut().find(|s| s.stream_type == stream.stream_type) {
*s = stream;
}
}
}
#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
pub struct DisplayInfo {
/// The last brightness value that was manually set.
pub manual_brightness_value: f32,
pub auto_brightness: bool,
pub low_light_mode: LowLightMode,
}
impl DisplayInfo {
pub const fn new(
auto_brightness: bool,
manual_brightness_value: f32,
low_light_mode: LowLightMode,
) -> DisplayInfo {
DisplayInfo { manual_brightness_value, auto_brightness, low_light_mode }
}
}
bitflags! {
#[derive(Serialize, Deserialize)]
pub struct ConfigurationInterfaceFlags: u32 {
const ETHERNET = 1 << 0;
const WIFI = 1 << 1;
const DEFAULT = Self::WIFI.bits;
}
}
#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
pub struct DoNotDisturbInfo {
pub user_dnd: Option<bool>,
pub night_mode_dnd: Option<bool>,
}
impl DoNotDisturbInfo {
pub const fn empty() -> DoNotDisturbInfo {
DoNotDisturbInfo { user_dnd: None, night_mode_dnd: None }
}
pub const fn new(user_dnd: bool, night_mode_dnd: bool) -> DoNotDisturbInfo {
DoNotDisturbInfo { user_dnd: Some(user_dnd), night_mode_dnd: Some(night_mode_dnd) }
}
}
#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize, Hash, Eq)]
pub enum LowLightMode {
/// Device should not be in low-light mode.
Disable,
/// Device should not be in low-light mode and should transition
/// out of it immediately.
DisableImmediately,
/// Device should be in low-light mode.
Enable,
}
#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
pub struct NightModeInfo {
pub night_mode_enabled: Option<bool>,
}
impl NightModeInfo {
pub const fn empty() -> NightModeInfo {
NightModeInfo { night_mode_enabled: None }
}
pub const fn new(user_night_mode_enabled: bool) -> NightModeInfo {
NightModeInfo { night_mode_enabled: Some(user_night_mode_enabled) }
}
}
#[derive(PartialEq, Debug, Clone, Copy, Serialize, Deserialize)]
pub struct PrivacyInfo {
pub user_data_sharing_consent: Option<bool>,
}
#[derive(PartialEq, Debug, Clone, Copy, Deserialize, Serialize)]
pub struct SetupInfo {
pub configuration_interfaces: ConfigurationInterfaceFlags,
}
#[derive(PartialEq, Debug, Clone, Copy)]
pub struct LightData {
/// Overall illuminance as measured in lux.
pub illuminance: f32,
/// Light sensor color reading in rgb.
pub color: fidl_fuchsia_ui_types::ColorRgb,
}
/// The possible responses to a SettingRequest.
#[derive(PartialEq, Debug, Clone)]
pub enum SettingResponse {
Unknown,
Accessibility(AccessibilityInfo),
Audio(AudioInfo),
/// Response to a request to get current brightness state.AccessibilityEncoder
Brightness(DisplayInfo),
Device(DeviceInfo),
Light(LightInfo),
LightSensor(LightData),
DoNotDisturb(DoNotDisturbInfo),
Input(InputInfo),
Intl(IntlInfo),
NightMode(NightModeInfo),
Privacy(PrivacyInfo),
Setup(SetupInfo),
}
impl SettingResponse {
/// Returns the name of the enum and its value, debug-formatted, for writing to inspect.
/// TODO(fxb/51123): simplify this with a macro or fuchsia-inspect-derive.
pub fn for_inspect(self) -> (&'static str, String) {
match self {
SettingResponse::Unknown => ("Unknown", "".to_string()),
SettingResponse::Accessibility(info) => ("Accessibility", format!("{:?}", info)),
SettingResponse::Audio(info) => ("Audio", format!("{:?}", info)),
SettingResponse::Brightness(info) => ("Brightness", format!("{:?}", info)),
SettingResponse::Device(info) => ("Device", format!("{:?}", info)),
SettingResponse::Light(info) => ("Light", format!("{:?}", info)),
SettingResponse::LightSensor(info) => ("LightSensor", format!("{:?}", info)),
SettingResponse::DoNotDisturb(info) => ("DoNotDisturb", format!("{:?}", info)),
SettingResponse::Input(info) => ("Input", format!("{:?}", info)),
SettingResponse::Intl(info) => ("Intl", format!("{:?}", info)),
SettingResponse::NightMode(info) => ("NightMode", format!("{:?}", info)),
SettingResponse::Privacy(info) => ("Privacy", format!("{:?}", info)),
SettingResponse::Setup(info) => ("Setup", format!("{:?}", info)),
}
}
}
/// Description of an action request on a setting. This wraps a
/// SettingActionData, providing destination details (setting type) along with
/// callback information (action id).
#[derive(PartialEq, Debug, Clone)]
pub struct SettingAction {
pub id: u64,
pub setting_type: SettingType,
pub data: SettingActionData,
}
/// The types of actions. Note that specific request types should be enumerated
/// in the SettingRequest enum.
#[derive(PartialEq, Debug, Clone)]
pub enum SettingActionData {
/// The listening state has changed for the particular setting. The provided
/// value indicates the number of active listeners. 0 indicates there are
/// no more listeners.
Listen(u64),
/// A request has been made on a particular setting. The specific setting
/// and request data are encoded in SettingRequest.
Request(SettingRequest),
}
/// The events generated in response to SettingAction.
#[derive(Clone, Debug)]
pub enum SettingEvent {
/// The backing data for the specified setting type has changed. Interested
/// parties can query through request to get the updated values.
Changed(SettingType),
/// A response to a previous SettingActionData::Request is ready. The source
/// SettingAction's id is provided alongside the result.
Response(u64, SettingResponseResult),
}
/// A trait handed back from Switchboard's listen interface. Allows client to
/// signal they want to end the session.
pub trait ListenSession: Drop {
/// Invoked to close the current listening session. No further updates will
/// be provided to the listener provided at the initial listen call.
fn close(&mut self);
}
/// Custom trait used to handle results from responding to FIDL calls.
pub trait FidlResponseErrorLogger {
fn log_fidl_response_error(&self, client_name: &str);
}
/// In order to not crash when a client dies, logs but doesn't crash for the specific case of
/// being unable to write to the client. Crashes if other types of errors occur.
impl FidlResponseErrorLogger for Result<(), fidl::Error> {
fn log_fidl_response_error(&self, client_name: &str) {
if let Some(error) = self.as_ref().err() {
match error {
fidl::Error::ServerResponseWrite(_) => {
fx_log_warn!("Failed to respond to client {:?} : {:?}", client_name, error);
}
_ => {
panic!(
"Unexpected client response error from client {:?} : {:?}",
client_name, error
);
}
}
}
}
}
#[cfg(test)]
mod tests {
use fuchsia_zircon as zx;
use super::*;
/// Should succeed either when responding was successful or there was an error on the client side.
#[test]
fn test_error_logger_succeeds() {
let result = Err(fidl::Error::ServerResponseWrite(zx::Status::PEER_CLOSED));
result.log_fidl_response_error("");
let result = Ok(());
result.log_fidl_response_error("");
}
/// Should fail at all other times.
#[should_panic]
#[test]
fn test_error_logger_fails() {
let result = Err(fidl::Error::ServerRequestRead(zx::Status::PEER_CLOSED));
result.log_fidl_response_error("");
}
}