settings/audio/
stream_volume_control.rs1use std::rc::Rc;
6
7use crate::audio::types::{AudioError, AudioStream};
8use crate::audio::utils::round_volume_level;
9#[cfg(test)]
10use crate::clock;
11use crate::{trace, trace_guard};
12use fidl::endpoints::create_proxy;
13use fidl_fuchsia_media::Usage2;
14use fidl_fuchsia_media_audio::VolumeControlProxy;
15use futures::TryStreamExt;
16#[cfg(test)]
17use futures::channel::mpsc::UnboundedSender;
18use futures::channel::oneshot::Sender;
19use settings_common::call;
20use settings_common::inspect::event::ExternalEventPublisher;
21#[cfg(test)]
22use settings_common::service_context::ExternalServiceEvent;
23use settings_common::service_context::ExternalServiceProxy;
24use {fuchsia_async as fasync, fuchsia_trace as ftrace};
25
26#[cfg(test)]
27const PUBLISHER_EVENT_NAME: &str = "volume_control_events";
28const CONTROLLER_ERROR_DEPENDENCY: &str = "fuchsia.media.audio";
29#[cfg(test)]
30const UNKNOWN_INSPECT_STRING: &str = "unknown";
31
32pub(crate) type ExitAction = Rc<dyn Fn()>;
34
35pub struct StreamVolumeControl {
40 pub stored_stream: AudioStream,
41 proxy: Option<VolumeControlProxy>,
42 audio_service: ExternalServiceProxy<fidl_fuchsia_media::AudioCoreProxy, ExternalEventPublisher>,
43 early_exit_action: Option<ExitAction>,
44 #[cfg(test)]
45 publisher: Option<UnboundedSender<ExternalServiceEvent>>,
46 listen_exit_tx: Option<Sender<()>>,
47}
48
49impl Drop for StreamVolumeControl {
50 fn drop(&mut self) {
51 if let Some(exit_tx) = self.listen_exit_tx.take() {
52 if exit_tx.is_canceled() {
54 return;
55 }
56
57 exit_tx.send(()).unwrap_or_else(|_| {
60 log::warn!("StreamVolumeControl::drop, exit_tx failed to send exit signal")
61 });
62 }
63 }
64}
65
66impl StreamVolumeControl {
67 pub(crate) async fn create(
68 id: ftrace::Id,
69 audio_service: ExternalServiceProxy<
70 fidl_fuchsia_media::AudioCoreProxy,
71 ExternalEventPublisher,
72 >,
73 stream: AudioStream,
74 early_exit_action: Option<ExitAction>,
75 #[cfg(test)] publisher: Option<UnboundedSender<ExternalServiceEvent>>,
76 ) -> Result<Self, AudioError> {
77 assert!(stream.has_valid_volume_level());
80
81 trace!(id, c"StreamVolumeControl ctor");
82 let mut control = StreamVolumeControl {
83 stored_stream: stream,
84 proxy: None,
85 audio_service: audio_service,
86 listen_exit_tx: None,
87 early_exit_action,
88 #[cfg(test)]
89 publisher,
90 };
91
92 control.bind_volume_control(id).await?;
93 Ok(control)
94 }
95
96 pub(crate) async fn set_volume(
97 &mut self,
98 id: ftrace::Id,
99 stream: AudioStream,
100 ) -> Result<(), AudioError> {
101 assert_eq!(self.stored_stream.stream_type, stream.stream_type);
102 assert!(stream.has_valid_volume_level());
105
106 if self.proxy.is_none() {
108 self.bind_volume_control(id).await?;
109 }
110
111 let mut new_stream_value = stream;
113 new_stream_value.user_volume_level = round_volume_level(stream.user_volume_level);
114
115 let proxy = self.proxy.as_ref().expect("no volume control proxy");
116
117 if (self.stored_stream.user_volume_level - new_stream_value.user_volume_level).abs()
118 > f32::EPSILON
119 {
120 if let Err(e) = proxy.set_volume(new_stream_value.user_volume_level) {
121 self.stored_stream = new_stream_value;
122 return Err(AudioError::ExternalFailure(
123 CONTROLLER_ERROR_DEPENDENCY,
124 "set volume".into(),
125 format!("{e:?}"),
126 ));
127 }
128 }
129
130 if self.stored_stream.user_volume_muted != new_stream_value.user_volume_muted {
131 if let Err(e) = proxy.set_mute(stream.user_volume_muted) {
132 self.stored_stream = new_stream_value;
133 return Err(AudioError::ExternalFailure(
134 CONTROLLER_ERROR_DEPENDENCY,
135 "set mute".into(),
136 format!("{e:?}"),
137 ));
138 }
139 }
140
141 self.stored_stream = new_stream_value;
142 Ok(())
143 }
144
145 async fn bind_volume_control(&mut self, id: ftrace::Id) -> Result<(), AudioError> {
146 trace!(id, c"bind volume control");
147 if self.proxy.is_some() {
148 return Ok(());
149 }
150
151 let (vol_control_proxy, server_end) = create_proxy();
152 let stream_type = self.stored_stream.stream_type;
153 let usage = Usage2::RenderUsage(stream_type.into());
154
155 let guard = trace_guard!(id, c"bind usage volume control");
156 if let Err(e) = call!(self.audio_service => bind_usage_volume_control2(&usage, server_end))
157 {
158 return Err(AudioError::ExternalFailure(
159 CONTROLLER_ERROR_DEPENDENCY,
160 format!("bind_usage_volume_control2 for audio_core {usage:?}").into(),
161 format!("{e:?}"),
162 ));
163 }
164 drop(guard);
165
166 let guard = trace_guard!(id, c"set values");
167 if let Err(e) = vol_control_proxy.set_volume(self.stored_stream.user_volume_level) {
169 return Err(AudioError::ExternalFailure(
170 CONTROLLER_ERROR_DEPENDENCY,
171 format!("set_volume for vol_control {stream_type:?}").into(),
172 format!("{e:?}"),
173 ));
174 }
175
176 if let Err(e) = vol_control_proxy.set_mute(self.stored_stream.user_volume_muted) {
177 return Err(AudioError::ExternalFailure(
178 CONTROLLER_ERROR_DEPENDENCY,
179 "set_mute for vol_control".into(),
180 format!("{e:?}"),
181 ));
182 }
183 drop(guard);
184
185 if let Some(exit_tx) = self.listen_exit_tx.take() {
186 exit_tx.send(()).expect(
188 "StreamVolumeControl::bind_volume_control, listen_exit_tx failed to send exit \
189 signal",
190 );
191 }
192
193 trace!(id, c"setup listener");
194
195 let (exit_tx, mut exit_rx) = futures::channel::oneshot::channel::<()>();
196 let mut volume_events = vol_control_proxy.take_event_stream();
197 let early_exit_action = self.early_exit_action.clone();
198 fasync::Task::local({
199 #[cfg(test)]
200 let publisher = self.publisher.clone();
201 async move {
202 let id = ftrace::Id::new();
203 trace!(id, c"bind volume handler");
204 loop {
205 futures::select! {
206 _ = exit_rx => {
207 trace!(id, c"exit");
208 #[cfg(test)]
209 {
210 if let Some(publisher) = publisher {
213 let _ = publisher.unbounded_send(
214 ExternalServiceEvent::Closed(
215 PUBLISHER_EVENT_NAME,
216 UNKNOWN_INSPECT_STRING.into(),
217 UNKNOWN_INSPECT_STRING.into(),
218 clock::inspect_format_now().into(),
219 )
220 );
221 }
222 }
223 return;
224 }
225 volume_event = volume_events.try_next() => {
226 trace!(id, c"volume_event");
227 if let Err(_) | Ok(None) = volume_event {
228 if let Some(action) = early_exit_action {
229 (action)();
230 }
231 return;
232 }
233 }
234 }
235 }
236 }
237 })
238 .detach();
239
240 self.listen_exit_tx = Some(exit_tx);
241 self.proxy = Some(vol_control_proxy);
242 Ok(())
243 }
244}