settings/audio/
stream_volume_control.rsuse crate::audio::types::AudioStream;
use crate::audio::utils::round_volume_level;
use crate::base::SettingType;
use crate::event::{Event, Publisher};
use crate::handler::setting_handler::ControllerError;
use crate::service_context::{ExternalServiceEvent, ExternalServiceProxy};
use crate::{call, clock, trace, trace_guard};
use fidl::endpoints::create_proxy;
use fidl_fuchsia_media::{AudioRenderUsage, Usage};
use fidl_fuchsia_media_audio::VolumeControlProxy;
use futures::channel::oneshot::Sender;
use futures::TryStreamExt;
use std::rc::Rc;
use {fuchsia_async as fasync, fuchsia_trace as ftrace};
const PUBLISHER_EVENT_NAME: &str = "volume_control_events";
const CONTROLLER_ERROR_DEPENDENCY: &str = "fuchsia.media.audio";
const UNKNOWN_INSPECT_STRING: &str = "unknown";
pub(crate) type ExitAction = Rc<dyn Fn()>;
pub struct StreamVolumeControl {
pub stored_stream: AudioStream,
proxy: Option<VolumeControlProxy>,
audio_service: ExternalServiceProxy<fidl_fuchsia_media::AudioCoreProxy>,
publisher: Option<Publisher>,
early_exit_action: Option<ExitAction>,
listen_exit_tx: Option<Sender<()>>,
}
impl Drop for StreamVolumeControl {
fn drop(&mut self) {
if let Some(exit_tx) = self.listen_exit_tx.take() {
if exit_tx.is_canceled() {
return;
}
exit_tx.send(()).unwrap_or_else(|_| {
tracing::warn!("StreamVolumeControl::drop, exit_tx failed to send exit signal")
});
}
}
}
impl StreamVolumeControl {
pub(crate) async fn create(
id: ftrace::Id,
audio_service: &ExternalServiceProxy<fidl_fuchsia_media::AudioCoreProxy>,
stream: AudioStream,
early_exit_action: Option<ExitAction>,
publisher: Option<Publisher>,
) -> Result<Self, ControllerError> {
assert!(stream.has_valid_volume_level());
trace!(id, c"StreamVolumeControl ctor");
let mut control = StreamVolumeControl {
stored_stream: stream,
proxy: None,
audio_service: audio_service.clone(),
publisher,
listen_exit_tx: None,
early_exit_action,
};
control.bind_volume_control(id).await?;
Ok(control)
}
pub(crate) async fn set_volume(
&mut self,
id: ftrace::Id,
stream: AudioStream,
) -> Result<(), ControllerError> {
assert_eq!(self.stored_stream.stream_type, stream.stream_type);
assert!(stream.has_valid_volume_level());
if self.proxy.is_none() {
self.bind_volume_control(id).await?;
}
let mut new_stream_value = stream;
new_stream_value.user_volume_level = round_volume_level(stream.user_volume_level);
let proxy = self.proxy.as_ref().expect("no volume control proxy");
if (self.stored_stream.user_volume_level - new_stream_value.user_volume_level).abs()
> f32::EPSILON
{
if let Err(e) = proxy.set_volume(new_stream_value.user_volume_level) {
self.stored_stream = new_stream_value;
return Err(ControllerError::ExternalFailure(
SettingType::Audio,
CONTROLLER_ERROR_DEPENDENCY.into(),
"set volume".into(),
format!("{e:?}").into(),
));
}
}
if self.stored_stream.user_volume_muted != new_stream_value.user_volume_muted {
if let Err(e) = proxy.set_mute(stream.user_volume_muted) {
self.stored_stream = new_stream_value;
return Err(ControllerError::ExternalFailure(
SettingType::Audio,
CONTROLLER_ERROR_DEPENDENCY.into(),
"set mute".into(),
format!("{e:?}").into(),
));
}
}
self.stored_stream = new_stream_value;
Ok(())
}
async fn bind_volume_control(&mut self, id: ftrace::Id) -> Result<(), ControllerError> {
trace!(id, c"bind volume control");
if self.proxy.is_some() {
return Ok(());
}
let (vol_control_proxy, server_end) = create_proxy();
let stream_type = self.stored_stream.stream_type;
let usage = Usage::RenderUsage(AudioRenderUsage::from(stream_type));
let guard = trace_guard!(id, c"bind usage volume control");
if let Err(e) = call!(self.audio_service => bind_usage_volume_control(&usage, server_end)) {
return Err(ControllerError::ExternalFailure(
SettingType::Audio,
CONTROLLER_ERROR_DEPENDENCY.into(),
format!("bind_usage_volume_control for audio_core {usage:?}").into(),
format!("{e:?}").into(),
));
}
drop(guard);
let guard = trace_guard!(id, c"set values");
if let Err(e) = vol_control_proxy.set_volume(self.stored_stream.user_volume_level) {
return Err(ControllerError::ExternalFailure(
SettingType::Audio,
CONTROLLER_ERROR_DEPENDENCY.into(),
format!("set_volume for vol_control {stream_type:?}").into(),
format!("{e:?}").into(),
));
}
if let Err(e) = vol_control_proxy.set_mute(self.stored_stream.user_volume_muted) {
return Err(ControllerError::ExternalFailure(
SettingType::Audio,
CONTROLLER_ERROR_DEPENDENCY.into(),
"set_mute for vol_control".into(),
format!("{e:?}").into(),
));
}
drop(guard);
if let Some(exit_tx) = self.listen_exit_tx.take() {
exit_tx.send(()).expect(
"StreamVolumeControl::bind_volume_control, listen_exit_tx failed to send exit \
signal",
);
}
trace!(id, c"setup listener");
let (exit_tx, mut exit_rx) = futures::channel::oneshot::channel::<()>();
let publisher_clone = self.publisher.clone();
let mut volume_events = vol_control_proxy.take_event_stream();
let early_exit_action = self.early_exit_action.clone();
fasync::Task::local(async move {
let id = ftrace::Id::new();
trace!(id, c"bind volume handler");
loop {
futures::select! {
_ = exit_rx => {
trace!(id, c"exit");
if let Some(publisher) = publisher_clone {
publisher.send_event(
Event::ExternalServiceEvent(
ExternalServiceEvent::Closed(
PUBLISHER_EVENT_NAME,
UNKNOWN_INSPECT_STRING.into(),
UNKNOWN_INSPECT_STRING.into(),
clock::inspect_format_now().into(),
)
)
);
}
return;
}
volume_event = volume_events.try_next() => {
trace!(id, c"volume_event");
if volume_event.is_err() ||
volume_event.expect("should not be error").is_none()
{
if let Some(action) = early_exit_action {
(action)();
}
return;
}
}
}
}
})
.detach();
self.listen_exit_tx = Some(exit_tx);
self.proxy = Some(vol_control_proxy);
Ok(())
}
}