1use std::collections::{HashMap, HashSet};
6
7use crate::trace;
8use anyhow::Error;
9use futures::StreamExt;
10use {fuchsia_async as fasync, fuchsia_trace as ftrace};
11
12use crate::agent::earcons::agent::CommonEarconsParams;
13use crate::agent::earcons::sound_ids::{VOLUME_CHANGED_SOUND_ID, VOLUME_MAX_SOUND_ID};
14use crate::agent::earcons::utils::{connect_to_sound_player, play_sound};
15use crate::audio::types::{
16 AudioInfo, AudioSettingSource, AudioStream, AudioStreamType, SetAudioStream,
17 AUDIO_STREAM_TYPE_COUNT,
18};
19use crate::audio::{create_default_modified_counters, ModifiedCounters};
20use crate::base::{SettingInfo, SettingType};
21use crate::handler::base::{Payload, Request};
22use crate::message::base::Audience;
23use crate::message::receptor::extract_payload;
24use crate::{event, service};
25
26pub(super) struct VolumeChangeHandler {
28 common_earcons_params: CommonEarconsParams,
29 last_user_volumes: HashMap<AudioStreamType, f32>,
30 modified_counters: ModifiedCounters,
31 publisher: event::Publisher,
32 messenger: service::message::Messenger,
33}
34
35const MAX_VOLUME: f32 = 1.0;
37
38const VOLUME_MAX_FILE_PATH: &str = "volume-max.wav";
40
41const VOLUME_CHANGED_FILE_PATH: &str = "volume-changed.wav";
43
44impl VolumeChangeHandler {
45 pub(super) async fn create(
46 publisher: event::Publisher,
47 params: CommonEarconsParams,
48 messenger: service::message::Messenger,
49 ) -> Result<(), Error> {
50 let mut receptor = messenger.message(
51 Payload::Request(Request::Get).into(),
52 Audience::Address(service::Address::Handler(SettingType::Audio)),
53 );
54
55 let last_user_volumes =
57 if let Ok((Payload::Response(Ok(Some(SettingInfo::Audio(info)))), _)) =
58 receptor.next_of::<Payload>().await
59 {
60 info.streams
62 .iter()
63 .filter(|x| {
64 x.stream_type == AudioStreamType::Media
65 || x.stream_type == AudioStreamType::Interruption
66 })
67 .map(|stream| (stream.stream_type, stream.user_volume_level))
68 .collect()
69 } else {
70 HashMap::new()
72 };
73
74 fasync::Task::local(async move {
75 let mut handler = Self {
76 common_earcons_params: params,
77 last_user_volumes,
78 modified_counters: create_default_modified_counters(),
79 publisher,
80 messenger: messenger.clone(),
81 };
82
83 let listen_receptor = messenger
84 .message(
85 Payload::Request(Request::Listen).into(),
86 Audience::Address(service::Address::Handler(SettingType::Audio)),
87 )
88 .fuse();
89 futures::pin_mut!(listen_receptor);
90
91 loop {
92 futures::select! {
93 volume_change_event = listen_receptor.next() => {
94 if let Some(
95 service::Payload::Setting(Payload::Response(Ok(Some(
96 SettingInfo::Audio(audio_info)))))
97 ) = extract_payload(volume_change_event) {
98 handler.on_audio_info(audio_info).await;
99 }
100 }
101 complete => break,
102 }
103 }
104 })
105 .detach();
106
107 Ok(())
108 }
109
110 fn calculate_changed_streams(
114 &mut self,
115 all_streams: [AudioStream; AUDIO_STREAM_TYPE_COUNT],
116 new_modified_counters: ModifiedCounters,
117 ) -> Vec<AudioStream> {
118 let mut changed_stream_types = HashSet::new();
119 for (stream_type, timestamp) in new_modified_counters {
120 if self.modified_counters.get(&stream_type) != Some(×tamp) {
121 let _ = changed_stream_types.insert(stream_type);
122 let _ = self.modified_counters.insert(stream_type, timestamp);
123 }
124 }
125
126 IntoIterator::into_iter(all_streams)
127 .filter(|stream| changed_stream_types.contains(&stream.stream_type))
128 .collect()
129 }
130
131 fn get_user_volume(
133 &self,
134 changed_streams: Vec<AudioStream>,
135 stream_type: AudioStreamType,
136 ) -> Option<f32> {
137 changed_streams.iter().find(|&&x| x.stream_type == stream_type).map(|x| x.user_volume_level)
138 }
139
140 fn get_change_source(
142 &self,
143 changed_streams: Vec<AudioStream>,
144 stream_type: AudioStreamType,
145 ) -> Option<AudioSettingSource> {
146 changed_streams.iter().find(|&&x| x.stream_type == stream_type).map(|x| x.source)
147 }
148
149 async fn on_audio_info_for_stream(
152 &mut self,
153 new_user_volume: f32,
154 stream_type: AudioStreamType,
155 change_source: Option<AudioSettingSource>,
156 ) {
157 let volume_is_max = new_user_volume == MAX_VOLUME;
158 let last_user_volume = self.last_user_volumes.get(&stream_type);
159
160 log::debug!(
162 "[earcons_agent] New {:?} user volume: {:?}, Last {:?} user volume: {:?}",
163 stream_type,
164 new_user_volume,
165 stream_type,
166 last_user_volume,
167 );
168
169 if last_user_volume != Some(&new_user_volume) || volume_is_max {
170 if last_user_volume.is_some() && change_source != Some(AudioSettingSource::System) {
179 let id = ftrace::Id::new();
180 trace!(id, c"volume_change_handler set background");
181 let mut receptor = self.messenger.message(
182 Payload::Request(Request::SetVolume(
183 vec![SetAudioStream {
184 stream_type: AudioStreamType::Background,
185 source: AudioSettingSource::System,
186 user_volume_level: Some(new_user_volume),
187 user_volume_muted: None,
188 }],
189 id,
190 ))
191 .into(),
192 Audience::Address(service::Address::Handler(SettingType::Audio)),
193 );
194 if let Err(e) = receptor.next_payload().await {
195 log::error!("Failed to play sound after waiting for message response: {e:?}");
196 } else {
197 self.play_volume_sound(new_user_volume);
198 }
199 }
200
201 let _ = self.last_user_volumes.insert(stream_type, new_user_volume);
202 }
203 }
204
205 async fn on_audio_info(&mut self, audio_info: AudioInfo) {
208 let changed_streams = match audio_info.modified_counters {
209 None => Vec::new(),
210 Some(counters) => self.calculate_changed_streams(audio_info.streams, counters),
211 };
212
213 let media_user_volume =
214 self.get_user_volume(changed_streams.clone(), AudioStreamType::Media);
215 let interruption_user_volume =
216 self.get_user_volume(changed_streams.clone(), AudioStreamType::Interruption);
217 let media_change_source =
218 self.get_change_source(changed_streams.clone(), AudioStreamType::Media);
219
220 if let Some(media_user_volume) = media_user_volume {
221 self.on_audio_info_for_stream(
222 media_user_volume,
223 AudioStreamType::Media,
224 media_change_source,
225 )
226 .await;
227 }
228 if let Some(interruption_user_volume) = interruption_user_volume {
229 self.on_audio_info_for_stream(
230 interruption_user_volume,
231 AudioStreamType::Interruption,
232 None,
233 )
234 .await;
235 }
236 }
237
238 fn play_volume_sound(&self, volume: f32) {
240 let common_earcons_params = self.common_earcons_params.clone();
241
242 let publisher = self.publisher.clone();
243 fasync::Task::local(async move {
244 connect_to_sound_player(
246 publisher,
247 common_earcons_params.service_context.clone(),
248 common_earcons_params.sound_player_connection.clone(),
249 )
250 .await;
251
252 let sound_player_connection_clone =
253 common_earcons_params.sound_player_connection.clone();
254 let sound_player_connection = sound_player_connection_clone.lock().await;
255 let sound_player_added_files = common_earcons_params.sound_player_added_files;
256
257 if let (Some(sound_player_proxy), volume_level) =
258 (sound_player_connection.as_ref(), volume)
259 {
260 let play_sound_result = if volume_level >= 1.0 {
261 play_sound(
262 sound_player_proxy,
263 VOLUME_MAX_FILE_PATH,
264 VOLUME_MAX_SOUND_ID,
265 sound_player_added_files.clone(),
266 )
267 .await
268 } else if volume_level > 0.0 {
269 play_sound(
270 sound_player_proxy,
271 VOLUME_CHANGED_FILE_PATH,
272 VOLUME_CHANGED_SOUND_ID,
273 sound_player_added_files.clone(),
274 )
275 .await
276 } else {
277 Ok(())
278 };
279 if let Err(e) = play_sound_result {
280 log::warn!("Failed to play sound: {:?}", e);
281 }
282 }
283 })
284 .detach();
285 }
286}
287
288#[cfg(test)]
289mod tests {
290 use fuchsia_inspect::component;
291 use futures::lock::Mutex;
292 use std::rc::Rc;
293
294 use crate::audio::build_audio_default_settings;
295 use crate::inspect::config_logger::InspectConfigLogger;
296 use crate::message::base::MessengerType;
297 use crate::service_context::ServiceContext;
298
299 use super::*;
300
301 fn fake_values() -> (
302 [AudioStream; AUDIO_STREAM_TYPE_COUNT], ModifiedCounters, ModifiedCounters, Vec<AudioStream>, ) {
307 let config_logger =
308 Rc::new(std::sync::Mutex::new(InspectConfigLogger::new(component::inspector().root())));
309 let mut settings = build_audio_default_settings(config_logger);
310 let settings = settings
311 .load_default_value()
312 .expect("config data should exist and be parseable for tests")
313 .unwrap();
314 let fake_streams = settings.streams;
315 let old_timestamps = create_default_modified_counters();
316 let new_timestamps = [
317 (AudioStreamType::Background, 0),
318 (AudioStreamType::Media, 1),
319 (AudioStreamType::Interruption, 0),
320 (AudioStreamType::SystemAgent, 2),
321 (AudioStreamType::Communication, 3),
322 (AudioStreamType::Accessibility, 4),
323 ]
324 .iter()
325 .cloned()
326 .collect();
327 let expected_changed_streams =
328 [fake_streams[1], fake_streams[3], fake_streams[4], fake_streams[5]].to_vec();
329 (fake_streams, old_timestamps, new_timestamps, expected_changed_streams)
330 }
331
332 #[fuchsia::test(allow_stalls = false)]
333 async fn test_changed_streams() {
334 let (fake_streams, old_timestamps, new_timestamps, expected_changed_streams) =
335 fake_values();
336 let delegate = service::MessageHub::create_hub();
337 let (messenger, _) = delegate.create(MessengerType::Unbound).await.expect("messenger");
338 let publisher = event::Publisher::create(&delegate, MessengerType::Unbound).await;
339 let last_user_volumes: HashMap<_, _> =
340 [(AudioStreamType::Media, 1.0), (AudioStreamType::Interruption, 0.5)].into();
341
342 let mut handler = VolumeChangeHandler {
343 common_earcons_params: CommonEarconsParams {
344 service_context: Rc::new(ServiceContext::new(None, None)),
345 sound_player_added_files: Rc::new(Mutex::new(HashSet::new())),
346 sound_player_connection: Rc::new(Mutex::new(None)),
347 },
348 last_user_volumes,
349 modified_counters: old_timestamps,
350 publisher,
351 messenger,
352 };
353 let changed_streams = handler.calculate_changed_streams(fake_streams, new_timestamps);
354 assert_eq!(changed_streams, expected_changed_streams);
355 }
356}