use super::AudioInfoLoader;
use crate::audio::types::{AudioInfo, AudioStream, AudioStreamType, SetAudioStream};
use crate::audio::{create_default_modified_counters, ModifiedCounters, StreamVolumeControl};
use crate::base::SettingType;
use crate::handler::base::Request;
use crate::handler::setting_handler::persist::{
controller as data_controller, ClientProxy, WriteResult,
};
use crate::handler::setting_handler::{
controller, ControllerError, ControllerStateResult, Event, SettingHandlerResult, State,
};
use crate::{trace, trace_guard};
use async_trait::async_trait;
use futures::lock::Mutex;
use settings_storage::device_storage::{DeviceStorage, DeviceStorageCompatible};
use settings_storage::storage_factory::{DefaultLoader, StorageAccess};
use std::collections::HashMap;
use std::rc::Rc;
use {fuchsia_async as fasync, fuchsia_trace as ftrace};
type VolumeControllerHandle = Rc<Mutex<VolumeController>>;
pub(crate) struct VolumeController {
client: ClientProxy,
audio_service_connected: bool,
stream_volume_controls: HashMap<AudioStreamType, StreamVolumeControl>,
modified_counters: ModifiedCounters,
audio_info_loader: AudioInfoLoader,
}
enum UpdateFrom {
AudioInfo(AudioInfo),
NewStreams(Vec<SetAudioStream>),
}
impl VolumeController {
fn create_with(
client: ClientProxy,
audio_info_loader: AudioInfoLoader,
) -> VolumeControllerHandle {
Rc::new(Mutex::new(Self {
client,
stream_volume_controls: HashMap::new(),
audio_service_connected: false,
modified_counters: create_default_modified_counters(),
audio_info_loader,
}))
}
async fn restore(&mut self, id: ftrace::Id) -> ControllerStateResult {
self.restore_volume_state(true, id).await
}
async fn restore_volume_state(
&mut self,
push_to_audio_core: bool,
id: ftrace::Id,
) -> ControllerStateResult {
let audio_info = self.client.read_setting::<AudioInfo>(id).await;
let _ = self
.update_volume_streams(UpdateFrom::AudioInfo(audio_info), push_to_audio_core, id)
.await?;
Ok(())
}
async fn get_info(&self, id: ftrace::Id) -> Result<AudioInfo, ControllerError> {
let mut audio_info = self.client.read_setting::<AudioInfo>(id).await;
audio_info.modified_counters = Some(self.modified_counters.clone());
Ok(audio_info)
}
async fn set_volume(
&mut self,
volume: Vec<SetAudioStream>,
id: ftrace::Id,
) -> SettingHandlerResult {
let guard = trace_guard!(id, c"set volume updating counters");
for stream in &volume {
let _ = self.modified_counters.insert(
stream.stream_type,
self.modified_counters
.get(&stream.stream_type)
.map_or(0, |flag| flag.wrapping_add(1)),
);
}
drop(guard);
if !(self.update_volume_streams(UpdateFrom::NewStreams(volume), true, id).await?) {
trace!(id, c"set volume notifying");
let info = self.get_info(id).await?.into();
self.client.notify(Event::Changed(info)).await;
}
Ok(None)
}
async fn get_streams_array_from_map(
&self,
stream_map: &HashMap<AudioStreamType, StreamVolumeControl>,
) -> [AudioStream; 5] {
let mut streams: [AudioStream; 5] = self.audio_info_loader.default_value().streams;
for stream in &mut streams {
if let Some(volume_control) = stream_map.get(&stream.stream_type) {
*stream = volume_control.stored_stream;
}
}
streams
}
async fn update_volume_streams(
&mut self,
update_from: UpdateFrom,
push_to_audio_core: bool,
id: ftrace::Id,
) -> Result<bool, ControllerError> {
let mut new_vec = vec![];
trace!(id, c"update volume streams");
let calculating_guard = trace_guard!(id, c"check and bind");
let (stored_value, new_streams) = match &update_from {
UpdateFrom::AudioInfo(audio_info) => (None, audio_info.streams.iter()),
UpdateFrom::NewStreams(streams) => {
trace!(id, c"reading setting");
let stored_value = self.client.read_setting::<AudioInfo>(id).await;
for set_stream in streams.iter() {
let stored_stream = stored_value
.streams
.iter()
.find(|stream| stream.stream_type == set_stream.stream_type)
.ok_or_else(|| {
ControllerError::InvalidArgument(
SettingType::Audio,
"stream".into(),
format!("{set_stream:?}").into(),
)
})?;
new_vec.push(AudioStream {
stream_type: stored_stream.stream_type,
source: set_stream.source,
user_volume_level: set_stream
.user_volume_level
.unwrap_or(stored_stream.user_volume_level),
user_volume_muted: set_stream
.user_volume_muted
.unwrap_or(stored_stream.user_volume_muted),
});
}
(Some(stored_value), new_vec.iter())
}
};
if push_to_audio_core {
let guard = trace_guard!(id, c"push to core");
self.check_and_bind_volume_controls(
id,
self.audio_info_loader.default_value().streams.iter(),
)
.await?;
drop(guard);
trace!(id, c"setting core");
for stream in new_streams {
if let Some(volume_control) =
self.stream_volume_controls.get_mut(&stream.stream_type)
{
volume_control.set_volume(id, *stream).await?;
}
}
} else {
trace!(id, c"without push to core");
self.check_and_bind_volume_controls(id, new_streams).await?;
}
drop(calculating_guard);
if let Some(mut stored_value) = stored_value {
let guard = trace_guard!(id, c"updating streams and counters");
stored_value.streams =
self.get_streams_array_from_map(&self.stream_volume_controls).await;
stored_value.modified_counters = Some(self.modified_counters.clone());
drop(guard);
let guard = trace_guard!(id, c"writing setting");
let write_result = self.client.write_setting(stored_value.into(), id).await;
drop(guard);
Ok(write_result.notified())
} else {
Ok(false)
}
}
async fn check_and_bind_volume_controls(
&mut self,
id: ftrace::Id,
streams: impl Iterator<Item = &AudioStream>,
) -> ControllerStateResult {
trace!(id, c"check and bind fn");
if self.audio_service_connected {
return Ok(());
}
let guard = trace_guard!(id, c"connecting to service");
let service_result = self
.client
.get_service_context()
.connect::<fidl_fuchsia_media::AudioCoreMarker>()
.await;
let audio_service = service_result.map_err(|e| {
ControllerError::ExternalFailure(
SettingType::Audio,
"fuchsia.media.audio".into(),
"connect for audio_core".into(),
format!("{e:?}").into(),
)
})?;
drop(guard);
let mut stream_tuples = Vec::new();
for stream in streams {
trace!(id, c"create stream volume control");
let client = self.client.clone();
stream_tuples.push((
stream.stream_type,
StreamVolumeControl::create(
id,
&audio_service,
*stream,
Some(Rc::new(move || {
let client = client.clone();
fasync::Task::local(async move {
trace!(id, c"stream exit");
client
.notify(Event::Exited(Err(ControllerError::UnexpectedError(
"stream_volume_control exit".into(),
))))
.await;
})
.detach();
})),
None,
)
.await?,
));
}
stream_tuples.into_iter().for_each(|(stream_type, stream_volume_control)| {
let _ = self.stream_volume_controls.insert(stream_type, stream_volume_control);
});
self.audio_service_connected = true;
Ok(())
}
}
pub(crate) struct AudioController {
volume: VolumeControllerHandle,
}
impl StorageAccess for AudioController {
type Storage = DeviceStorage;
type Data = AudioInfo;
const STORAGE_KEY: &'static str = AudioInfo::KEY;
}
impl data_controller::CreateWith for AudioController {
type Data = AudioInfoLoader;
fn create_with(client: ClientProxy, data: Self::Data) -> Result<Self, ControllerError> {
Ok(AudioController { volume: VolumeController::create_with(client, data) })
}
}
#[async_trait(?Send)]
impl controller::Handle for AudioController {
async fn handle(&self, request: Request) -> Option<SettingHandlerResult> {
match request {
Request::Restore => Some({
let id = ftrace::Id::new();
trace!(id, c"controller restore");
self.volume.lock().await.restore(id).await.map(|_| None)
}),
Request::SetVolume(volume, id) => {
trace!(id, c"controller set");
for audio_stream in &volume {
if !audio_stream.has_valid_volume_level() {
return Some(Err(ControllerError::InvalidArgument(
SettingType::Audio,
"stream".into(),
format!("{audio_stream:?}").into(),
)));
}
}
Some(self.volume.lock().await.set_volume(volume, id).await)
}
Request::Get => {
let id = ftrace::Id::new();
Some(self.volume.lock().await.get_info(id).await.map(|info| Some(info.into())))
}
_ => None,
}
}
async fn change_state(&mut self, state: State) -> Option<ControllerStateResult> {
match state {
State::Startup => {
Some({
let id = ftrace::Id::new();
trace!(id, c"controller startup");
self.volume.lock().await.restore_volume_state(false, id).await
})
}
_ => None,
}
}
}