blob: 40208273f75bcd5494bbe92aea4b808c31bafa69 [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 {
crate::audio::{default_audio_info, StreamVolumeControl},
crate::input::monitor_media_buttons,
crate::registry::base::{Command, Context, Notifier, SettingHandler, State},
crate::registry::device_storage::DeviceStorageFactory,
crate::service_context::ServiceContextHandle,
crate::switchboard::base::*,
anyhow::Error,
fidl_fuchsia_ui_input::MediaButtonsEvent,
fuchsia_async as fasync,
fuchsia_syslog::fx_log_err,
futures::StreamExt,
parking_lot::RwLock,
std::collections::HashMap,
std::sync::Arc,
};
fn get_streams_array_from_map(
stream_map: &HashMap<AudioStreamType, StreamVolumeControl>,
) -> [AudioStream; 5] {
let mut streams: [AudioStream; 5] = default_audio_info().streams;
for i in 0..streams.len() {
if let Some(volume_control) = stream_map.get(&streams[i].stream_type) {
streams[i] = volume_control.stored_stream.clone();
}
}
streams
}
/// Controller that handles commands for SettingType::Audio.
/// TODO(go/fxb/35988): Hook up the presentation service to listen for the mic mute state.
pub fn spawn_audio_controller<T: DeviceStorageFactory + Send + Sync + 'static>(
context: &Context<T>,
) -> SettingHandler {
let service_context_handle = context.environment.service_context_handle.clone();
let storage_factory_handle = context.environment.storage_factory_handle.clone();
let (audio_handler_tx, mut audio_handler_rx) = futures::channel::mpsc::unbounded::<Command>();
let default_audio_settings = default_audio_info();
let notifier_lock = Arc::<RwLock<Option<Notifier>>>::new(RwLock::new(None));
let mic_mute_state =
Arc::<RwLock<bool>>::new(RwLock::new(default_audio_settings.input.mic_mute));
let input_service_connected = Arc::<RwLock<bool>>::new(RwLock::new(false));
let audio_service_connected = Arc::<RwLock<bool>>::new(RwLock::new(false));
let mut stream_volume_controls = HashMap::new();
let (input_tx, mut input_rx) = futures::channel::mpsc::unbounded::<MediaButtonsEvent>();
let input_service_connected_clone = input_service_connected.clone();
let service_context_handle_clone = service_context_handle.clone();
let input_tx_clone = input_tx.clone();
fasync::spawn(async move {
*input_service_connected_clone.write() =
monitor_media_buttons(service_context_handle_clone, input_tx_clone).await.is_ok();
});
let mic_mute_state_clone = mic_mute_state.clone();
let notifier_lock_clone = notifier_lock.clone();
fasync::spawn(async move {
while let Some(event) = input_rx.next().await {
if let Some(mic_mute) = event.mic_mute {
if *mic_mute_state_clone.read() != mic_mute {
*mic_mute_state_clone.write() = mic_mute;
if let Some(notifier) = (*notifier_lock_clone.read()).clone() {
notifier.unbounded_send(SettingType::Audio).unwrap();
}
}
}
}
fx_log_err!("[audio_controller] exited input event loop");
});
let input_service_connected_clone = input_service_connected.clone();
let service_context_handle_clone = service_context_handle.clone();
fasync::spawn(async move {
check_and_bind_volume_controls(
audio_service_connected.clone(),
service_context_handle_clone,
&mut stream_volume_controls,
)
.await
.ok();
let storage = storage_factory_handle.lock().await.get_store::<AudioInfo>();
let mut changed_streams = None;
while let Some(command) = audio_handler_rx.next().await {
match command {
Command::ChangeState(state) => match state {
State::Listen(notifier) => {
*notifier_lock.write() = Some(notifier);
}
State::EndListen => {
*notifier_lock.write() = None;
}
},
Command::HandleRequest(request, responder) => {
let mut stored_value = storage.lock().await.get().await;
#[allow(unreachable_patterns)]
match request {
SettingRequest::Restore => {
let stored_streams = stored_value.streams.iter().cloned().collect();
update_volume_stream(&stored_streams, &mut stream_volume_controls)
.await;
}
SettingRequest::SetVolume(volume) => {
if check_and_bind_volume_controls(
audio_service_connected.clone(),
service_context_handle.clone(),
&mut stream_volume_controls,
)
.await
.is_err()
{
continue;
};
update_volume_stream(&volume, &mut stream_volume_controls).await;
changed_streams = Some(volume);
stored_value.streams =
get_streams_array_from_map(&stream_volume_controls);
let storage_handle_clone = storage.clone();
fasync::spawn(async move {
let mut storage_lock = storage_handle_clone.lock().await;
storage_lock.write(&stored_value, false).await.unwrap_or_else(
move |e| {
fx_log_err!("failed storing audio, {}", e);
},
);
});
let _ = responder.send(Ok(None)).ok();
if let Some(notifier) = (*notifier_lock.read()).clone() {
notifier.unbounded_send(SettingType::Audio).unwrap();
}
}
SettingRequest::Get => {
{
if !*input_service_connected_clone.read() {
let connected = monitor_media_buttons(
service_context_handle.clone(),
input_tx.clone(),
)
.await
.is_ok();
*input_service_connected_clone.write() = connected;
}
}
check_and_bind_volume_controls(
audio_service_connected.clone(),
service_context_handle.clone(),
&mut stream_volume_controls,
)
.await
.ok();
let _ = responder
.send(Ok(Some(SettingResponse::Audio(AudioInfo {
streams: stored_value.streams,
input: AudioInputInfo { mic_mute: *mic_mute_state.read() },
changed_streams: changed_streams.clone(),
}))))
.ok();
}
_ => {
responder
.send(Err(SwitchboardError::UnimplementedRequest {
setting_type: SettingType::Audio,
request: request,
}))
.ok();
}
}
}
}
}
fx_log_err!("[audio_controller] exited service event loop");
});
audio_handler_tx
}
// Updates |stored_audio_streams| and then update volume via the AudioCore service.
async fn update_volume_stream(
new_streams: &Vec<AudioStream>,
stored_volume_controls: &mut HashMap<AudioStreamType, StreamVolumeControl>,
) {
for stream in new_streams {
if let Some(volume_control) = stored_volume_controls.get_mut(&stream.stream_type) {
volume_control.set_volume(stream.clone()).await;
}
}
}
// Checks to see if |service_connected| contains true. If it is not, then
// connect to the audio core service. If the service connects successfully,
// set |service_connected| to true and create a StreamVolumeControl for each
// stream type.
async fn check_and_bind_volume_controls(
service_connected: Arc<RwLock<bool>>,
service_context_handle: ServiceContextHandle,
stream_volume_controls: &mut HashMap<AudioStreamType, StreamVolumeControl>,
) -> Result<(), Error> {
if *service_connected.read() {
return Ok(());
}
let service_result =
service_context_handle.lock().await.connect::<fidl_fuchsia_media::AudioCoreMarker>().await;
let audio_service = match service_result {
Ok(service) => {
*service_connected.write() = true;
service
}
Err(err) => {
fx_log_err!("failed to connect to audio core, {}", err);
return Err(err);
}
};
for stream in default_audio_info().streams.iter() {
stream_volume_controls.insert(
stream.stream_type.clone(),
StreamVolumeControl::create(&audio_service, stream.clone()),
);
}
Ok(())
}