settings/audio/
audio_controller.rs

1// Copyright 2019 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4use super::AudioInfoLoader;
5use crate::audio::types::{
6    AudioInfo, AudioStream, AudioStreamType, SetAudioStream, AUDIO_STREAM_TYPE_COUNT,
7};
8use crate::audio::{create_default_modified_counters, ModifiedCounters, StreamVolumeControl};
9use crate::base::SettingType;
10use crate::handler::base::Request;
11use crate::handler::setting_handler::persist::{controller as data_controller, ClientProxy};
12use crate::handler::setting_handler::{
13    controller, ControllerError, ControllerStateResult, Event, SettingHandlerResult, State,
14};
15use crate::{trace, trace_guard};
16use async_trait::async_trait;
17use futures::lock::Mutex;
18use settings_storage::device_storage::{DeviceStorage, DeviceStorageCompatible};
19use settings_storage::storage_factory::{DefaultLoader, StorageAccess, StorageFactory};
20use settings_storage::UpdateState;
21use std::collections::HashMap;
22use std::marker::PhantomData;
23use std::rc::Rc;
24use {fuchsia_async as fasync, fuchsia_trace as ftrace};
25
26type VolumeControllerHandle = Rc<Mutex<VolumeController>>;
27
28pub(crate) struct VolumeController {
29    client: ClientProxy,
30    store: Rc<DeviceStorage>,
31    audio_service_connected: bool,
32    stream_volume_controls: HashMap<AudioStreamType, StreamVolumeControl>,
33    modified_counters: ModifiedCounters,
34    audio_info_loader: AudioInfoLoader,
35}
36
37enum UpdateFrom {
38    AudioInfo(AudioInfo),
39    NewStreams(Vec<SetAudioStream>),
40}
41
42impl VolumeController {
43    async fn create_with(
44        client: ClientProxy,
45        audio_info_loader: AudioInfoLoader,
46        storage_factory: Rc<impl StorageFactory<Storage = DeviceStorage>>,
47    ) -> VolumeControllerHandle {
48        let store = storage_factory.get_store().await;
49        Rc::new(Mutex::new(Self {
50            client,
51            store,
52            stream_volume_controls: HashMap::new(),
53            audio_service_connected: false,
54            modified_counters: create_default_modified_counters(),
55            audio_info_loader,
56        }))
57    }
58
59    /// Restores the necessary dependencies' state on boot.
60    async fn restore(&mut self, id: ftrace::Id) -> ControllerStateResult {
61        self.restore_volume_state(true, id).await
62    }
63
64    /// Extracts the audio state from persistent storage and restores it on
65    /// the local state. Also pushes the changes to the audio core if
66    /// `push_to_audio_core` is true.
67    async fn restore_volume_state(
68        &mut self,
69        push_to_audio_core: bool,
70        id: ftrace::Id,
71    ) -> ControllerStateResult {
72        let audio_info = self.store.get::<AudioInfo>().await;
73        // Ignore the notification triggered result.
74        let _ = self
75            .update_volume_streams(UpdateFrom::AudioInfo(audio_info), push_to_audio_core, id)
76            .await?;
77        Ok(())
78    }
79
80    async fn get_info(&self) -> Result<AudioInfo, ControllerError> {
81        let mut audio_info = self.store.get::<AudioInfo>().await;
82
83        audio_info.modified_counters = Some(self.modified_counters.clone());
84        Ok(audio_info)
85    }
86
87    async fn set_volume(
88        &mut self,
89        volume: Vec<SetAudioStream>,
90        id: ftrace::Id,
91    ) -> SettingHandlerResult {
92        let guard = trace_guard!(id, c"set volume updating counters");
93        // Update counters for changed streams.
94        for stream in &volume {
95            // We don't care what the value of the counter is, just that it is different from the
96            // previous value. We use wrapping_add to avoid eventual overflow of the counter.
97            let _ = self.modified_counters.insert(
98                stream.stream_type,
99                self.modified_counters
100                    .get(&stream.stream_type)
101                    .map_or(0, |flag| flag.wrapping_add(1)),
102            );
103        }
104        drop(guard);
105
106        if !(self.update_volume_streams(UpdateFrom::NewStreams(volume), true, id).await?) {
107            trace!(id, c"set volume notifying");
108            let info = self.get_info().await?.into();
109            self.client.notify(Event::Changed(info)).await;
110        }
111
112        Ok(None)
113    }
114
115    async fn get_streams_array_from_map(
116        &self,
117        stream_map: &HashMap<AudioStreamType, StreamVolumeControl>,
118    ) -> [AudioStream; AUDIO_STREAM_TYPE_COUNT] {
119        let mut streams: [AudioStream; AUDIO_STREAM_TYPE_COUNT] =
120            self.audio_info_loader.default_value().streams;
121        for stream in &mut streams {
122            if let Some(volume_control) = stream_map.get(&stream.stream_type) {
123                *stream = volume_control.stored_stream;
124            }
125        }
126
127        streams
128    }
129
130    /// Updates the state with the given streams' volume levels.
131    ///
132    /// If `push_to_audio_core` is true, pushes the changes to the audio core.
133    /// If not, just sets it on the local stored state. Should be called with
134    /// true on first restore and on volume changes, and false otherwise.
135    /// Returns whether the change triggered a notification.
136    async fn update_volume_streams(
137        &mut self,
138        update_from: UpdateFrom,
139        push_to_audio_core: bool,
140        id: ftrace::Id,
141    ) -> Result<bool, ControllerError> {
142        let mut new_vec = vec![];
143        trace!(id, c"update volume streams");
144        let calculating_guard = trace_guard!(id, c"check and bind");
145        let (stored_value, new_streams) = match &update_from {
146            UpdateFrom::AudioInfo(audio_info) => (None, audio_info.streams.iter()),
147            UpdateFrom::NewStreams(streams) => {
148                trace!(id, c"reading setting");
149                let stored_value = self.store.get::<AudioInfo>().await;
150                for set_stream in streams.iter() {
151                    let stored_stream = stored_value
152                        .streams
153                        .iter()
154                        .find(|stream| stream.stream_type == set_stream.stream_type)
155                        .ok_or_else(|| {
156                            ControllerError::InvalidArgument(
157                                SettingType::Audio,
158                                "stream".into(),
159                                format!("{set_stream:?}").into(),
160                            )
161                        })?;
162                    new_vec.push(AudioStream {
163                        stream_type: stored_stream.stream_type,
164                        source: set_stream.source,
165                        user_volume_level: set_stream
166                            .user_volume_level
167                            .unwrap_or(stored_stream.user_volume_level),
168                        user_volume_muted: set_stream
169                            .user_volume_muted
170                            .unwrap_or(stored_stream.user_volume_muted),
171                    });
172                }
173                (Some(stored_value), new_vec.iter())
174            }
175        };
176
177        if push_to_audio_core {
178            let guard = trace_guard!(id, c"push to core");
179            self.check_and_bind_volume_controls(
180                id,
181                self.audio_info_loader.default_value().streams.iter(),
182            )
183            .await?;
184            drop(guard);
185
186            trace!(id, c"setting core");
187            for stream in new_streams {
188                if let Some(volume_control) =
189                    self.stream_volume_controls.get_mut(&stream.stream_type)
190                {
191                    volume_control.set_volume(id, *stream).await?;
192                }
193            }
194        } else {
195            trace!(id, c"without push to core");
196            self.check_and_bind_volume_controls(id, new_streams).await?;
197        }
198        drop(calculating_guard);
199
200        if let Some(mut stored_value) = stored_value {
201            let guard = trace_guard!(id, c"updating streams and counters");
202            stored_value.streams =
203                self.get_streams_array_from_map(&self.stream_volume_controls).await;
204            stored_value.modified_counters = Some(self.modified_counters.clone());
205            drop(guard);
206
207            let guard = trace_guard!(id, c"writing setting");
208            let write_result = self.client.storage_write(&self.store, stored_value, id).await;
209            drop(guard);
210            Ok(write_result
211                .as_ref()
212                .map_or(false, |update_state| UpdateState::Updated == *update_state))
213        } else {
214            Ok(false)
215        }
216    }
217
218    /// Populates the local state with the given `streams` and binds it to the audio core service.
219    async fn check_and_bind_volume_controls(
220        &mut self,
221        id: ftrace::Id,
222        streams: impl Iterator<Item = &AudioStream>,
223    ) -> ControllerStateResult {
224        trace!(id, c"check and bind fn");
225        if self.audio_service_connected {
226            return Ok(());
227        }
228
229        let guard = trace_guard!(id, c"connecting to service");
230        let service_result = self
231            .client
232            .get_service_context()
233            .connect::<fidl_fuchsia_media::AudioCoreMarker>()
234            .await;
235
236        let audio_service = service_result.map_err(|e| {
237            ControllerError::ExternalFailure(
238                SettingType::Audio,
239                "fuchsia.media.audio".into(),
240                "connect for audio_core".into(),
241                format!("{e:?}").into(),
242            )
243        })?;
244
245        // The stream_volume_controls are generated in two steps instead of
246        // one so that if one of the bindings fails during the first loop,
247        // none of the streams are modified.
248        drop(guard);
249        let mut stream_tuples = Vec::new();
250        for stream in streams {
251            trace!(id, c"create stream volume control");
252            let client = self.client.clone();
253
254            // Generate a tuple with stream type and StreamVolumeControl.
255            stream_tuples.push((
256                stream.stream_type,
257                StreamVolumeControl::create(
258                    id,
259                    &audio_service,
260                    *stream,
261                    Some(Rc::new(move || {
262                        // When the StreamVolumeControl exits early, inform the
263                        // proxy we have exited. The proxy will then cleanup this
264                        // AudioController.
265                        let client = client.clone();
266                        fasync::Task::local(async move {
267                            trace!(id, c"stream exit");
268                            client
269                                .notify(Event::Exited(Err(ControllerError::UnexpectedError(
270                                    "stream_volume_control exit".into(),
271                                ))))
272                                .await;
273                        })
274                        .detach();
275                    })),
276                    None,
277                )
278                .await?,
279            ));
280        }
281
282        stream_tuples.into_iter().for_each(|(stream_type, stream_volume_control)| {
283            // Ignore the previous value, if any.
284            let _ = self.stream_volume_controls.insert(stream_type, stream_volume_control);
285        });
286        self.audio_service_connected = true;
287
288        Ok(())
289    }
290}
291
292pub(crate) struct AudioController<F> {
293    volume: VolumeControllerHandle,
294    _phantom: PhantomData<F>,
295}
296
297impl<F> StorageAccess for AudioController<F> {
298    type Storage = DeviceStorage;
299    type Data = AudioInfo;
300    const STORAGE_KEY: &'static str = AudioInfo::KEY;
301}
302
303#[async_trait(?Send)]
304impl<F> data_controller::CreateWithAsync for AudioController<F>
305where
306    F: StorageFactory<Storage = DeviceStorage>,
307{
308    type Data = (Rc<F>, AudioInfoLoader);
309    async fn create_with(client: ClientProxy, data: Self::Data) -> Result<Self, ControllerError> {
310        Ok(AudioController {
311            volume: VolumeController::create_with(client, data.1, data.0).await,
312            _phantom: PhantomData,
313        })
314    }
315}
316
317#[async_trait(?Send)]
318impl<F> controller::Handle for AudioController<F> {
319    async fn handle(&self, request: Request) -> Option<SettingHandlerResult> {
320        match request {
321            Request::Restore => Some({
322                let id = ftrace::Id::new();
323                trace!(id, c"controller restore");
324                self.volume.lock().await.restore(id).await.map(|_| None)
325            }),
326            Request::SetVolume(volume, id) => {
327                trace!(id, c"controller set");
328                // Validate volume contains valid volume level numbers.
329                for audio_stream in &volume {
330                    if !audio_stream.has_valid_volume_level() {
331                        return Some(Err(ControllerError::InvalidArgument(
332                            SettingType::Audio,
333                            "stream".into(),
334                            format!("{audio_stream:?}").into(),
335                        )));
336                    }
337                }
338                Some(self.volume.lock().await.set_volume(volume, id).await)
339            }
340            Request::Get => {
341                Some(self.volume.lock().await.get_info().await.map(|info| Some(info.into())))
342            }
343            _ => None,
344        }
345    }
346
347    async fn change_state(&mut self, state: State) -> Option<ControllerStateResult> {
348        match state {
349            State::Startup => {
350                // Restore the volume state locally but do not push to the audio core.
351                Some({
352                    let id = ftrace::Id::new();
353                    trace!(id, c"controller startup");
354                    self.volume.lock().await.restore_volume_state(false, id).await
355                })
356            }
357            _ => None,
358        }
359    }
360}