blob: 8091658c7bb23005d4d605a82d822a5173d9c31a [file] [log] [blame]
// 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 {
anyhow::{format_err, Context as _, Error},
async_utils::hanging_get::client::HangingGetStream,
fidl::endpoints,
fidl_fuchsia_bluetooth_avrcp as avrcp, fidl_fuchsia_media as media,
fidl_fuchsia_settings as settings,
fuchsia_async::{self as fasync, DurationExt, Timer},
fuchsia_zircon as zx,
futures::{
channel::oneshot::Sender,
future::{Fuse, FusedFuture},
select, Future, FutureExt, StreamExt,
},
std::{fmt::Debug, pin::pin},
tracing::{info, trace, warn},
};
/// Represents set volume request.
/// All volume values are AVRCP volume values with range [0, 127].
struct SetVolumeRequest {
previous: u8, // System volume value before the request was made.
requested: u8, // Requested new volume value.
responder: Option<fidl_fuchsia_bluetooth_avrcp::AbsoluteVolumeHandlerSetVolumeResponder>,
}
impl SetVolumeRequest {
fn new(
current_volume: u8,
requested_volume: u8,
responder: Option<fidl_fuchsia_bluetooth_avrcp::AbsoluteVolumeHandlerSetVolumeResponder>,
) -> Self {
Self { previous: current_volume, requested: requested_volume, responder }
}
fn is_volume_changed(&self, new_volume: u8) -> bool {
self.previous != new_volume
}
fn is_increment_request(current_vol: u8, requested_vol: u8) -> bool {
requested_vol > current_vol
}
/// Regardless of if the new volume is the same or different from the
/// previous volume, send a response back using the SetVolumeResponder.
fn send(&mut self, new_volume: u8) {
if let None = self.responder.take().and_then(|responder| responder.send(new_volume).ok()) {
warn!("Could not send SetVolume response");
}
}
/// Sends a response back using the SetVolumeResponder if the
/// new volume is different from the previous volume.
/// Returns true if the response was sent. False if the response
/// wasn't sent due to volume being unchanged.
fn send_if_changed(&mut self, new_volume: u8) -> bool {
if !self.is_volume_changed(new_volume) {
return false;
}
if let None = self.responder.take().and_then(|responder| responder.send(new_volume).ok()) {
warn!("Could not send SetVolume response");
}
true
}
}
pub(crate) struct VolumeRelay {
/// A sender that when sent will cause the relay task to stop. None if the task is not running.
_stop: Option<Sender<()>>,
}
struct AvrcpVolume(u8);
impl AvrcpVolume {
/// Convert from a settings volume between 0.0 and 1.0 to a volume that can be sent
/// through AVRCP (0 to 127 as per the spec)
fn from_media_volume(value: settings::AudioSettings) -> Result<Self, anyhow::Error> {
let streams = value.streams.ok_or(format_err!("No streams in the AudioSettings"))?;
// Find the media stream volume
let volume =
match streams.iter().find(|&s| s.stream == Some(media::AudioRenderUsage::Media)) {
None => Err(format_err!("Couldn't find Media stream in settings")),
Some(settings::AudioStreamSettings { user_volume: None, .. }) => {
Err(format_err!("Volume not included in Media stream settings"))
}
Some(settings::AudioStreamSettings { user_volume: Some(vol), .. }) => Ok(vol),
};
let level = match volume? {
settings::Volume { muted: Some(true), .. } => 0.0,
settings::Volume { level: None, .. } => 0.0,
settings::Volume { level: Some(vol), .. } => *vol,
};
Ok(AvrcpVolume((level * 127.0) as u8))
}
/// Get an AudioSettings struct that can be sent to the Settings service to set the volume
/// to the same level as this. Converts from native AVRCP (0-127) to settings 0.0 - 1.0
/// ranges.
fn as_audio_settings(&self, stream: media::AudioRenderUsage) -> settings::AudioSettings {
let settings = settings::AudioStreamSettings {
stream: Some(stream),
source: Some(settings::AudioStreamSettingSource::User),
user_volume: Some(settings::Volume {
level: Some(self.0 as f32 / 127.0),
muted: Some(false),
..Default::default()
}),
..Default::default()
};
settings::AudioSettings { streams: Some(vec![settings]), ..Default::default() }
}
}
/// How long we will wait for the system to report a new volume after requesting a new volume.
/// This is chosen as 100 milliseconds as half of the required response time in the AVRCP Spec,
/// Section 6.2
const SETVOLUME_TIMEOUT: zx::Duration = zx::Duration::from_millis(100);
impl VolumeRelay {
/// Start a relay between AVRCP and Settings.Audio.
/// Media Volume is reported to AVRCP for Absolute Volume Controllers, and changes from AVRCP
/// are propagated to the system Media volume.
/// This starts the relay. The relay can be stopped by dropping it.
pub(crate) fn start() -> Result<Self, Error> {
let avrcp_svc =
fuchsia_component::client::connect_to_protocol::<avrcp::PeerManagerMarker>()
.context("Failed to connect to Bluetooth AVRCP interface")?;
let audio_settings_svc =
fuchsia_component::client::connect_to_protocol::<settings::AudioMarker>()
.context("Failed to connect to Audio settings interface")?;
let (sender, receiver) = futures::channel::oneshot::channel();
spawn_err("Volume", Self::volume_relay(avrcp_svc, audio_settings_svc, receiver.fuse()));
Ok(Self { _stop: Some(sender) })
}
async fn volume_relay(
mut avrcp: avrcp::PeerManagerProxy,
audio: settings::AudioProxy,
mut stop_signal: impl FusedFuture + Unpin,
) -> Result<(), Error> {
let mut volume_requests =
connect_avrcp_volume(&mut avrcp).await.context("connecting avrcp volume")?;
let audio_proxy_clone = audio.clone();
let mut audio_watch_stream =
HangingGetStream::new(audio_proxy_clone, settings::AudioProxy::watch);
// Wait for the first update from the settings app.
let mut current_volume = match audio_watch_stream.next().await {
None => return Err(format_err!("Volume watch response stream ended")),
Some(Err(e)) => return Err(format_err!("FIDL error polling audio watch: {:?}", e)),
Some(Ok(settings)) => match AvrcpVolume::from_media_volume(settings) {
Err(e) => return Err(format_err!("Can't get initial volume: {:?}", e)),
Ok(vol) => vol.0,
},
};
trace!("Initial system media volume level is {:?} in AVRCP", current_volume);
let mut staged_volume = Some(current_volume);
let mut last_onchanged = None;
// TODO(https://fxbug.dev/42131477): Change this to be a single responder when AVRCP correctly manages the
// lifetime of volume changed subscriptions.
let mut hanging_onchanged = Vec::new();
let mut hanging_setvolumes = Vec::new();
// Keeps track of most recent failed set volume request.
// Tuple of previous volume value and requested volume value.
let mut failed_setvolume: Option<SetVolumeRequest> = None;
// For now, we use minimum AVRCP volume step size to adjust volume when
// set volume request fails.
const AVRCP_VOLUME_STEP_SIZE: u8 = 1;
let setvolume_timeout = Fuse::terminated();
let mut setvolume_timeout = pin!(setvolume_timeout);
loop {
let mut sys_volume_watch_fut = audio_watch_stream.next();
let mut avrcp_request_fut = volume_requests.next();
select! {
_ = stop_signal => {
break Ok(());
},
avrcp_request = avrcp_request_fut => {
let request = match avrcp_request {
None => return Err(format_err!("AVRCP Volume Handler Channel Closed")),
Some(Err(e)) => return Err(format_err!("Volume Handler Request Error: {:?}", e)),
Some(Ok(req)) => req,
};
match request {
avrcp::AbsoluteVolumeHandlerRequest::SetVolume { mut requested_volume, responder } => {
if requested_volume == current_volume {
warn!("Requested AVRCP volume same as current volume: {requested_volume}");
let _ = responder.send(current_volume);
continue;
}
// TODO(dayeonglee): if the previously failed set volume request was requested
// with the same value, increment/decrement it by the step size.
if let Some(failed_req) = failed_setvolume.take() {
let is_increment = SetVolumeRequest::is_increment_request(current_volume, requested_volume);
if (is_increment && requested_volume <= failed_req.requested) || (!is_increment && requested_volume >= failed_req.requested) {
let before = requested_volume;
match is_increment {
true => requested_volume = failed_req.requested + AVRCP_VOLUME_STEP_SIZE,
false => requested_volume = failed_req.requested - AVRCP_VOLUME_STEP_SIZE,
};
info!("Requested volume adjusted from {before} to {requested_volume}");
}
}
let settings = AvrcpVolume(requested_volume).as_audio_settings(media::AudioRenderUsage::Media);
trace!("AVRCP Setting system volume to {} -> {:?}", requested_volume, settings);
if let Err(e) = audio.set(&settings).await {
warn!("Couldn't set media volume: {:?}", e);
let _ = responder.send(current_volume);
continue;
}
hanging_setvolumes.push(SetVolumeRequest::new(current_volume, requested_volume, Some(responder)));
if setvolume_timeout.is_terminated() {
setvolume_timeout.set(Timer::new(SETVOLUME_TIMEOUT.after_now()).fuse());
}
},
avrcp::AbsoluteVolumeHandlerRequest::OnVolumeChanged { responder } => {
hanging_onchanged.push(responder);
},
avrcp::AbsoluteVolumeHandlerRequest::GetCurrentVolume { responder } => {
let _ = responder.send(current_volume);
continue;
}
}
},
_ = setvolume_timeout => {
hanging_setvolumes.drain(..).for_each(|mut req| {
req.send(current_volume);
// TODO(b/250265882): convert the log back to trace once issue is resolved.
info!("SetVolume request timed out. Requested: {0}. Volume change: {1} -> {current_volume}", req.requested, req.previous);
if failed_setvolume.is_none() {
failed_setvolume = Some(SetVolumeRequest::new(req.previous, req.requested, None));
}
});
},
watch_response = sys_volume_watch_fut => {
let settings = match watch_response {
None => return Err(format_err!("Volume watch response stream ended")),
Some(Err(e)) => return Err(format_err!("FIDL error from watch: {:?}", e)),
Some(Ok(settings)) => settings,
};
current_volume = match AvrcpVolume::from_media_volume(settings) {
Err(e) => {
warn!("Volume Relay can't get volume: {:?}", e);
continue;
},
Ok(vol) => vol.0,
};
trace!("System media volume level now at {:?} in AVRCP", current_volume);
if hanging_setvolumes.len() > 0 {
hanging_setvolumes.retain_mut(|req| !req.send_if_changed(current_volume));
// When the change is the result of a setvolume command, the onchanged
// hanging is _not_ updated.
last_onchanged = Some(current_volume);
continue;
}
staged_volume = Some(current_volume);
},
}
if !hanging_onchanged.is_empty() && staged_volume.is_some() {
let next_volume = staged_volume.take().unwrap();
if Some(next_volume) == last_onchanged {
trace!("Not reporting unchanged volume {} to AVRCP", next_volume);
continue;
}
for responder in hanging_onchanged.drain(..) {
let _ = responder.send(next_volume);
}
trace!("Reporting changed system volume {} to AVRCP", next_volume);
last_onchanged = Some(next_volume);
}
}
}
}
fn spawn_err<F, E>(label: &'static str, future: F)
where
F: Future<Output = Result<(), E>> + Send + 'static,
E: Debug,
{
fasync::Task::spawn(async move {
if let Some(e) = future.await.err() {
info!("{} Completed with Error: {:?}", label, e);
}
})
.detach();
}
async fn connect_avrcp_volume(
avrcp: &mut avrcp::PeerManagerProxy,
) -> Result<avrcp::AbsoluteVolumeHandlerRequestStream, Error> {
let (client, request_stream) = endpoints::create_request_stream()?;
if let Err(e) = avrcp.set_absolute_volume_handler(client).await? {
info!("failed to set absolute volume handler");
return Err(format_err!("Failed setting absolute volume handler: {}", e));
}
Ok(request_stream)
}
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
use async_utils::PollExt;
use fuchsia_zircon::DurationNum;
use std::pin::Pin;
const INITIAL_MEDIA_VOLUME: f32 = 0.8;
const INITIAL_AVRCP_VOLUME: u8 = 101;
fn setup_avrcp_proxy() -> (avrcp::PeerManagerProxy, avrcp::PeerManagerRequestStream) {
endpoints::create_proxy_and_stream::<avrcp::PeerManagerMarker>().unwrap()
}
fn setup_settings_proxy() -> (settings::AudioProxy, settings::AudioRequestStream) {
endpoints::create_proxy_and_stream::<settings::AudioMarker>().unwrap()
}
/// Builds all of the Proxies and request streams involved with setting up a Volume Relay
/// test.
fn setup_volume_relay() -> (
settings::AudioRequestStream,
avrcp::PeerManagerRequestStream,
Sender<()>,
impl Future<Output = Result<(), Error>>,
) {
let (settings_proxy, settings_requests) = setup_settings_proxy();
let (avrcp_proxy, avrcp_requests) = setup_avrcp_proxy();
let (stop_sender, receiver) = futures::channel::oneshot::channel();
let relay_fut = VolumeRelay::volume_relay(avrcp_proxy, settings_proxy, receiver.fuse());
(settings_requests, avrcp_requests, stop_sender, relay_fut)
}
/// Expects a Watch() call to the `audio_request_stream`. Returns the handler to respond to
/// the watch call, or panics if that doesn't happen.
#[track_caller]
fn expect_audio_watch(
exec: &mut fasync::TestExecutor,
audio_request_stream: &mut settings::AudioRequestStream,
) -> settings::AudioWatchResponder {
let watch_request_fut = audio_request_stream.select_next_some();
let mut watch_request_fut = pin!(watch_request_fut);
match exec.run_until_stalled(&mut watch_request_fut).expect("should be ready") {
Ok(settings::AudioRequest::Watch { responder }) => responder,
x => panic!("Expected an Audio Watch Request, got {:?}", x),
}
}
#[track_caller]
fn respond_to_audio_watch(responder: settings::AudioWatchResponder, level: f32) {
responder
.send(&settings::AudioSettings {
streams: Some(vec![settings::AudioStreamSettings {
stream: Some(media::AudioRenderUsage::Media),
user_volume: Some(settings::Volume {
level: Some(level),
..Default::default()
}),
..Default::default()
}]),
..Default::default()
})
.expect("watch responder to send");
}
/// Confirms the setup of the Volume relay, which includes the registration of a volume
/// handler proxy with the AVRCP client, and the initial volume setting request to the Media
/// system.
fn finish_relay_setup<T: Future>(
mut relay_fut: &mut Pin<&mut T>,
mut exec: &mut fasync::TestExecutor,
mut avrcp_request_stream: avrcp::PeerManagerRequestStream,
audio_request_stream: &mut settings::AudioRequestStream,
) -> (avrcp::AbsoluteVolumeHandlerProxy, settings::AudioWatchResponder)
where
<T as Future>::Output: Debug,
{
// Expect registration of a AbsoluteVolumeHandler
let request_fut = avrcp_request_stream.select_next_some();
let mut request_fut = pin!(request_fut);
let handler = match exec.run_until_stalled(&mut request_fut).expect("should be ready") {
Ok(avrcp::PeerManagerRequest::SetAbsoluteVolumeHandler { handler, responder }) => {
responder.send(Ok(())).expect("response to handler set");
handler
}
x => panic!("Expected SetAbsoluteVolumeHandler, got: {:?}", x),
};
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
let audio_watch_responder = expect_audio_watch(&mut exec, audio_request_stream);
respond_to_audio_watch(audio_watch_responder, INITIAL_MEDIA_VOLUME);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
let audio_watch_responder = expect_audio_watch(&mut exec, audio_request_stream);
(handler.into_proxy().expect("absolute volume handler proxy"), audio_watch_responder)
}
/// Test that the relay sets up the connection to AVRCP and Sessions and stops on the stop
/// signal.
#[fuchsia::test]
fn relay_setup() {
let mut exec = fasync::TestExecutor::new();
let (mut settings_requests, avrcp_requests, stop_sender, relay_fut) = setup_volume_relay();
let mut relay_fut = pin!(relay_fut);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
let (volume_client, _watch_responder) =
finish_relay_setup(&mut relay_fut, &mut exec, avrcp_requests, &mut settings_requests);
// Sending a stop should drop all the things and the future should complete.
stop_sender.send(()).expect("should be able to send a stop");
let _ = exec.run_until_stalled(&mut relay_fut).expect("should be ready");
assert!(exec
.run_until_stalled(&mut settings_requests.next())
.expect("should be ready")
.is_none());
let mut current_volume_fut = volume_client.get_current_volume();
assert!(exec.run_until_stalled(&mut current_volume_fut).expect("should be ready").is_err());
}
/// Test that the relay calls the set volume command correctly and responds within an
/// appropriate amount of time.
#[fuchsia::test]
fn set_volume_command() {
let mut exec = fasync::TestExecutor::new_with_fake_time();
let (mut settings_requests, avrcp_requests, _stop_sender, relay_fut) = setup_volume_relay();
let mut relay_fut = pin!(relay_fut);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
let (volume_client, watch_responder) =
finish_relay_setup(&mut relay_fut, &mut exec, avrcp_requests, &mut settings_requests);
// Set volume request with the same volume value as the initial volume.
let volume_set_fut = volume_client.set_volume(INITIAL_AVRCP_VOLUME);
let mut volume_set_fut = pin!(volume_set_fut);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
let request_fut = settings_requests.select_next_some();
let mut request_fut = pin!(request_fut);
// Response is returned immediately with the initial value.
exec.run_until_stalled(&mut request_fut)
.expect_pending("audio settings change request should not have been sent");
assert_matches!(
exec.run_until_stalled(&mut volume_set_fut).expect("should be ready"),
Ok(INITIAL_AVRCP_VOLUME)
);
// Volume decrease request to 90.
let volume_set_fut = volume_client.set_volume(90);
let mut volume_set_fut = pin!(volume_set_fut);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
exec.run_until_stalled(&mut volume_set_fut).expect_pending("should be pending");
match exec.run_until_stalled(&mut request_fut).expect("should be ready") {
Ok(settings::AudioRequest::Set { settings, responder }) => {
assert!(
settings.streams.as_ref().unwrap()[0]
.user_volume
.as_ref()
.unwrap()
.level
.unwrap()
< INITIAL_MEDIA_VOLUME
);
let _ = responder.send(Ok(())).unwrap();
}
x => panic!("Expected Ready audio set request and got: {:?}", x),
};
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
// When a new volume happens as a result, it's returned.
respond_to_audio_watch(watch_responder, 0.7);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
assert_matches!(
exec.run_until_stalled(&mut volume_set_fut).expect("should be ready"),
Ok(88 /* 0.7 audio settings volume to AVRCP volume */)
);
}
#[fuchsia::test]
fn set_volume_command_retry() {
let mut exec = fasync::TestExecutor::new_with_fake_time();
let (mut settings_requests, avrcp_requests, _stop_sender, relay_fut) = setup_volume_relay();
let mut relay_fut = pin!(relay_fut);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
let (volume_client, watch_responder) =
finish_relay_setup(&mut relay_fut, &mut exec, avrcp_requests, &mut settings_requests);
// Volume increase request to 103.
let volume_set_fut = volume_client.set_volume(103);
let mut volume_set_fut = pin!(volume_set_fut);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
let request_fut = settings_requests.select_next_some();
let mut request_fut = pin!(request_fut);
exec.run_until_stalled(&mut volume_set_fut).expect_pending("should be pending");
let mut requested_audio_settings_vol: f32;
match exec.run_until_stalled(&mut request_fut).expect("should be ready") {
Ok(settings::AudioRequest::Set { settings, responder }) => {
requested_audio_settings_vol = settings.streams.as_ref().unwrap()[0]
.user_volume
.as_ref()
.unwrap()
.level
.unwrap();
assert_eq!(1, settings.streams.expect("a stream was set").len());
let _ = responder.send(Ok(())).unwrap();
}
x => panic!("Expected Ready audio set request and got: {:?}", x),
};
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
// Mimic no volume change by waiting out the maximum time we will wait for a new volume.
exec.set_fake_time(105.millis().after_now());
let _ = exec.wake_expired_timers();
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
// Because no change was sent from Media, the initial value from media is sent.
assert_matches!(
exec.run_until_stalled(&mut volume_set_fut).expect("should be ready"),
Ok(INITIAL_AVRCP_VOLUME)
);
// Second set volume request to 103.
let volume_set_fut = volume_client.set_volume(103);
let mut volume_set_fut = pin!(volume_set_fut);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
exec.run_until_stalled(&mut volume_set_fut).expect_pending("should be pending");
let request_fut = settings_requests.select_next_some();
let mut request_fut = pin!(request_fut);
match exec.run_until_stalled(&mut request_fut).expect("should be ready") {
Ok(settings::AudioRequest::Set { settings, responder }) => {
// We should have adjusted the volume value to be larger for the second request.
let new_requested = settings.streams.as_ref().unwrap()[0]
.user_volume
.as_ref()
.unwrap()
.level
.unwrap();
assert!(new_requested > requested_audio_settings_vol);
let _ = responder.send(Ok(())).unwrap();
requested_audio_settings_vol = new_requested;
}
x => panic!("Expected Ready audio set request and got: {:?}", x),
};
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
// Mimic no volume change by waiting out the maximum time we will wait for a new volume.
exec.set_fake_time(105.millis().after_now());
let _ = exec.wake_expired_timers();
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
// Because no change was sent from Media, the initial value from media is sent.
assert_matches!(
exec.run_until_stalled(&mut volume_set_fut).expect("should be ready"),
Ok(INITIAL_AVRCP_VOLUME)
);
// Third set volume request to 103.
let volume_set_fut = volume_client.set_volume(103);
let mut volume_set_fut = pin!(volume_set_fut);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
exec.run_until_stalled(&mut volume_set_fut).expect_pending("should be pending");
let request_fut = settings_requests.select_next_some();
let mut request_fut = pin!(request_fut);
match exec.run_until_stalled(&mut request_fut).expect("should be ready") {
Ok(settings::AudioRequest::Set { settings, responder }) => {
// We should have adjusted the volume value again.
let new_requested = settings.streams.as_ref().unwrap()[0]
.user_volume
.as_ref()
.unwrap()
.level
.unwrap();
assert!(new_requested > requested_audio_settings_vol);
let _ = responder.send(Ok(())).unwrap();
}
x => panic!("Expected Ready audio set request and got: {:?}", x),
};
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
// When a new volume happens as a result, it's returned.
respond_to_audio_watch(watch_responder, 0.82);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
assert_matches!(
exec.run_until_stalled(&mut volume_set_fut).expect("should be ready"),
Ok(104 /* 0.82 audio settings volume as AVRCP volume */)
);
}
/// Test that the relay returns the current volume when requested, and completes an
/// on_volume_changed request when the volume changes locally.
#[fuchsia::test]
fn on_volume_change_notification() {
let mut exec = fasync::TestExecutor::new();
let (mut settings_requests, avrcp_requests, _stop_sender, relay_fut) = setup_volume_relay();
let mut relay_fut = pin!(relay_fut);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
let (volume_client, watch_responder) =
finish_relay_setup(&mut relay_fut, &mut exec, avrcp_requests, &mut settings_requests);
let volume_get_fut = volume_client.get_current_volume();
let mut volume_get_fut = pin!(volume_get_fut);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
// Volume get should return immediately with the initial volume (0.8 -> 100)
assert_matches!(
exec.run_until_stalled(&mut volume_get_fut).expect("should be ready"),
Ok(INITIAL_AVRCP_VOLUME)
);
let volume_hanging_fut = volume_client.on_volume_changed();
let mut volume_hanging_fut = pin!(volume_hanging_fut);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
// The OnVolumeChanged request should return immediately the first time.
assert_matches!(
exec.run_until_stalled(&mut volume_hanging_fut).expect("should be ready"),
Ok(INITIAL_AVRCP_VOLUME)
);
let volume_hanging_fut = volume_client.on_volume_changed();
let mut volume_hanging_fut = pin!(volume_hanging_fut);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
// The next OnVolumeChanged request shouldn't resolve because the volume hasn't changed.
exec.run_until_stalled(&mut volume_hanging_fut).expect_pending("should be pending");
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
// When a new volume happens as a result, it's returned.
const CHANGED_MEDIA_VOLUME: f32 = 0.9;
const CHANGED_AVRCP_VOLUME: u8 = 114; // 0.9 audio settings volume as AVRCP volume.
respond_to_audio_watch(watch_responder, CHANGED_MEDIA_VOLUME);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
assert_matches!(
exec.run_until_stalled(&mut volume_hanging_fut).expect("should be ready"),
Ok(CHANGED_AVRCP_VOLUME)
);
let _watch_responder = expect_audio_watch(&mut exec, &mut settings_requests);
}
/// Tests the behavior of the VolumeRelay when multiple requests for OnVolumeChanged
/// updates are requested.
// TODO(https://fxbug.dev/42131477): This test should be updated to reflect the fact that the channel gets closed
// when OnVolumeChanged is called twice without a response.
#[fuchsia::test]
fn multiple_on_volume_change_notifications() {
let mut exec = fasync::TestExecutor::new();
let (mut settings_requests, avrcp_requests, _stop_sender, relay_fut) = setup_volume_relay();
let mut relay_fut = pin!(relay_fut);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
// Setup the relay and make two copies of the `volume_client`.
let (volume_client, watch_responder) =
finish_relay_setup(&mut relay_fut, &mut exec, avrcp_requests, &mut settings_requests);
let volume_hanging_fut1 = volume_client.on_volume_changed();
let mut volume_hanging_fut1 = pin!(volume_hanging_fut1);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
// The OnVolumeChanged request should return immediately the first time.
assert_matches!(
exec.run_until_stalled(&mut volume_hanging_fut1).expect("should be ready"),
Ok(INITIAL_AVRCP_VOLUME)
);
// Make another OnVolumeChanged request.
let volume_hanging_fut2 = volume_client.on_volume_changed();
let mut volume_hanging_fut2 = pin!(volume_hanging_fut2);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
// The next OnVolumeChanged request shouldn't resolve because the volume hasn't changed.
exec.run_until_stalled(&mut volume_hanging_fut2).expect_pending("should be pending");
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
// Another request for volume updates.
let volume_hanging_fut3 = volume_client.on_volume_changed();
let mut volume_hanging_fut3 = pin!(volume_hanging_fut3);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
// Respond with a new volume.
const CHANGED_MEDIA_VOLUME: f32 = 0.9;
const CHANGED_AVRCP_VOLUME: u8 = 114; // 0.9 audio settings volume as AVRCP volume.
respond_to_audio_watch(watch_responder, CHANGED_MEDIA_VOLUME);
exec.run_until_stalled(&mut relay_fut).expect_pending("should be pending");
// Both volume update futures should receive the updated avrcp volume.
assert_matches!(
exec.run_until_stalled(&mut volume_hanging_fut2).expect("should be ready"),
Ok(CHANGED_AVRCP_VOLUME)
);
assert_matches!(
exec.run_until_stalled(&mut volume_hanging_fut3).expect("should be ready"),
Ok(CHANGED_AVRCP_VOLUME)
);
let _watch_responder = expect_audio_watch(&mut exec, &mut settings_requests);
}
}