settings/agent/earcons/
utils.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// Copyright 2020 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::call_async;
use crate::event::Publisher;
use crate::service_context::{ExternalServiceProxy, ServiceContext};
use anyhow::{anyhow, Context as _, Error};
use fidl::endpoints::Proxy as _;
use fidl_fuchsia_media::AudioRenderUsage;
use fidl_fuchsia_media_sounds::{PlayerMarker, PlayerProxy};
use futures::lock::Mutex;
use std::collections::HashSet;
use std::rc::Rc;
use {fidl_fuchsia_io as fio, fuchsia_async as fasync};

/// Creates a file-based sound from a resource file.
fn resource_file(name: &str) -> Result<fidl::endpoints::ClientEnd<fio::FileMarker>, Error> {
    let path = format!("/config/data/{name}");
    fuchsia_fs::file::open_in_namespace(&path, fio::PERM_READABLE)
        .with_context(|| format!("opening resource file: {path}"))?
        .into_client_end()
        .map_err(|_: fio::FileProxy| {
            anyhow!("failed to convert new Proxy to ClientEnd for resource file {path}")
        })
}

/// Establish a connection to the sound player and return the proxy representing the service.
/// Will not do anything if the sound player connection is already established.
pub(super) async fn connect_to_sound_player(
    publisher: Publisher,
    service_context_handle: Rc<ServiceContext>,
    sound_player_connection: Rc<Mutex<Option<ExternalServiceProxy<PlayerProxy>>>>,
) {
    let mut sound_player_connection_lock = sound_player_connection.lock().await;
    if sound_player_connection_lock.is_none() {
        *sound_player_connection_lock = service_context_handle
            .connect_with_publisher::<PlayerMarker>(publisher)
            .await
            .context("Connecting to fuchsia.media.sounds.Player")
            .map_err(|e| tracing::error!("Failed to connect to fuchsia.media.sounds.Player: {}", e))
            .ok()
    }
}

/// Plays a sound with the given `id` and `file_name` via the `sound_player_proxy`.
///
/// The `id` and `file_name` are expected to be unique and mapped 1:1 to each other. This allows
/// the sound file to be reused without having to load it again.
pub(super) async fn play_sound<'a>(
    sound_player_proxy: &ExternalServiceProxy<PlayerProxy>,
    file_name: &'a str,
    id: u32,
    added_files: Rc<Mutex<HashSet<&'a str>>>,
) -> Result<(), Error> {
    // New sound, add it to the sound player set.
    if added_files.lock().await.insert(file_name) {
        let sound_file_channel = match resource_file(file_name) {
            Ok(file) => Some(file),
            Err(e) => return Err(anyhow!("[earcons] Failed to convert sound file: {}", e)),
        };
        if let Some(file_channel) = sound_file_channel {
            match call_async!(sound_player_proxy => add_sound_from_file(id, file_channel)).await {
                Ok(_) => tracing::debug!("[earcons] Added sound to Player: {}", file_name),
                Err(e) => {
                    return Err(anyhow!("[earcons] Unable to add sound to Player: {}", e));
                }
            };
        }
    }

    let sound_player_proxy = sound_player_proxy.clone();
    // This fasync thread is needed so that the earcons sounds can play rapidly and not wait
    // for the previous sound to finish to send another request.
    fasync::Task::local(async move {
        if let Err(e) =
            call_async!(sound_player_proxy => play_sound(id, AudioRenderUsage::Background)).await
        {
            tracing::error!("[earcons] Unable to Play sound from Player: {}", e);
        };
    })
    .detach();
    Ok(())
}