blob: dd707c094a7aea15e10960b204e147714860f2c8 [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},
fidl::endpoints,
fidl_fuchsia_bluetooth_avrcp as avrcp, fidl_fuchsia_media as media,
fidl_fuchsia_media_sessions2 as sessions2,
fidl_table_validation::ValidFidlTable,
fuchsia_async as fasync,
fuchsia_bluetooth::types::PeerId,
fuchsia_zircon as zx,
futures::{select, StreamExt},
log::{info, trace},
std::fmt::Debug,
};
#[derive(Debug, Clone, ValidFidlTable, PartialEq)]
#[fidl_table_src(sessions2::PlayerStatus)]
pub struct ValidPlayerStatus {
#[fidl_field_type(optional)]
pub duration: Option<i64>,
pub player_state: sessions2::PlayerState,
#[fidl_field_type(optional)]
pub timeline_function: Option<media::TimelineFunction>,
pub repeat_mode: sessions2::RepeatMode,
pub shuffle_on: bool,
pub content_type: sessions2::ContentType,
#[fidl_field_type(optional)]
pub error: Option<sessions2::Error>,
}
impl ValidPlayerStatus {
/// Sets the `player_state` given a state from AVRCP.
fn set_state_from_avrcp(&mut self, avrcp_status: avrcp::PlaybackStatus) {
self.player_state = match avrcp_status {
avrcp::PlaybackStatus::Stopped => sessions2::PlayerState::Idle,
avrcp::PlaybackStatus::Playing => sessions2::PlayerState::Playing,
avrcp::PlaybackStatus::Paused => sessions2::PlayerState::Paused,
avrcp::PlaybackStatus::FwdSeek => sessions2::PlayerState::Playing,
avrcp::PlaybackStatus::RevSeek => sessions2::PlayerState::Playing,
avrcp::PlaybackStatus::Error => sessions2::PlayerState::Idle,
};
}
/// Sets the TimelineFunction relating the current state and position given.
/// Sets the function to None if there is no correlation.
fn set_position(&mut self, position_millis: i64) {
let subject_delta = match self.player_state {
sessions2::PlayerState::Playing => 1,
sessions2::PlayerState::Paused => 0,
_ => {
self.timeline_function = None;
return;
}
};
self.timeline_function = Some(media::TimelineFunction {
subject_time: zx::Duration::from_millis(position_millis).into_nanos(),
reference_time: fasync::Time::now().into_nanos(),
subject_delta,
reference_delta: 1,
});
}
}
pub(crate) struct AvrcpRelay {}
impl AvrcpRelay {
/// Start a relay between AVRCP and MediaSession.
/// A MediaSession is published with the information from the AVRCP target.
/// This starts the relay. The relay can be stopped by dropping it.
pub(crate) fn start(
peer_id: PeerId,
player_request_stream: sessions2::PlayerRequestStream,
) -> Result<fasync::Task<()>, Error> {
let avrcp_svc = fuchsia_component::client::connect_to_service::<avrcp::PeerManagerMarker>()
.context("Failed to connect to Bluetooth AVRCP interface")?;
let session_fut = Self::session_relay(avrcp_svc, peer_id, player_request_stream);
Ok(fasync::Task::spawn(async move {
if let Err(e) = session_fut.await {
info!("Session completed with error: {:?}", e);
}
}))
}
async fn session_relay(
mut avrcp: avrcp::PeerManagerProxy,
peer_id: PeerId,
mut player_request_stream: sessions2::PlayerRequestStream,
) -> Result<(), Error> {
let controller =
connect_avrcp(&mut avrcp, peer_id).await.context("getting controller from AVRCP")?;
let mut staged_info = Some(sessions2::PlayerInfoDelta {
local: Some(true),
player_capabilities: Some(sessions2::PlayerCapabilities {
flags: Some(
sessions2::PlayerCapabilityFlags::Play
| sessions2::PlayerCapabilityFlags::Pause
| sessions2::PlayerCapabilityFlags::ChangeToNextItem
| sessions2::PlayerCapabilityFlags::ChangeToPrevItem,
),
..sessions2::PlayerCapabilities::EMPTY
}),
..sessions2::PlayerInfoDelta::EMPTY
});
let mut hanging_watcher = None;
let mut last_player_status = ValidPlayerStatus {
player_state: sessions2::PlayerState::Idle,
repeat_mode: sessions2::RepeatMode::Single,
shuffle_on: false,
content_type: sessions2::ContentType::Audio,
duration: None,
timeline_function: None,
error: None,
};
// Get the initial Media Attributes and status
let mut building = staged_info.as_mut().unwrap();
// TODO(fxbug.dev/42914): sometimes these initially fail with "Protocol Error". Avoid exiting the
// relay for now.
let _ = update_attributes(&controller, &mut building, &mut last_player_status).await;
let _ = update_status(&controller, &mut last_player_status).await;
building.player_status = Some(last_player_status.clone().into());
let mut avrcp_notify_stream = controller.take_event_stream();
loop {
let mut player_request_fut = player_request_stream.next();
let mut avrcp_notify_fut = avrcp_notify_stream.next();
select! {
request = player_request_fut => {
if request.is_none() {
trace!("Player request stream is closed, quitting AVRCP.");
break;
}
match request.unwrap().context("request from player")? {
sessions2::PlayerRequest::WatchInfoChange { responder } => {
if let Some(_) = hanging_watcher.take() {
return Err(format_err!("Concurrent watches issued: not allowed"));
}
hanging_watcher = Some(responder);
}
sessions2::PlayerRequest::Pause { .. } => {
let _ = controller.send_command(avrcp::AvcPanelCommand::Pause).await;
},
sessions2::PlayerRequest::Play { .. } => {
let _ = controller.send_command(avrcp::AvcPanelCommand::Play).await;
},
sessions2::PlayerRequest::Stop { .. } => {
let _ = controller.send_command(avrcp::AvcPanelCommand::Stop).await;
},
sessions2::PlayerRequest::NextItem { .. } => {
let _ = controller.send_command(avrcp::AvcPanelCommand::Forward).await;
},
sessions2::PlayerRequest::PrevItem { .. } => {
let _ = controller.send_command(avrcp::AvcPanelCommand::Backward).await;
},
sessions2::PlayerRequest::BindVolumeControl { .. } => {
// Drop incoming channel, we don't support this interface.
},
x => info!("Unhandled request from Player: {:?}", x),
}
}
event = avrcp_notify_fut => {
if event.is_none() {
info!("{} AVRCP relay stop: notification stream gone", peer_id);
break;
}
let avrcp::ControllerEvent::OnNotification { timestamp, notification } = event.unwrap()?;
trace!("Got Notification from AVRCP: {:?}", notification);
let mut player_status_updated = false;
if let Some(millis) = notification.pos {
last_player_status.set_position(millis as i64);
player_status_updated = true;
}
if notification.status.is_some() || notification.track_id.is_some() {
let mut building = staged_info.get_or_insert(sessions2::PlayerInfoDelta::EMPTY);
if let Err(e) = update_attributes(&controller, &mut building, &mut last_player_status).await {
info!("Couldn't update AVRCP attributes: {:?}", e);
}
player_status_updated = true;
}
if notification.status.is_some() {
if let Err(e) = update_status(&controller, &mut last_player_status).await {
info!("Couldn't update AVRCP status: {:?}", e);
}
player_status_updated = true;
}
if player_status_updated {
let building = staged_info.get_or_insert(sessions2::PlayerInfoDelta::EMPTY);
building.player_status = Some(last_player_status.clone().into());
}
// Notify that the notification is handled so we can receive another one.
let _ = controller.notify_notification_handled().context("acknowledging notification")?;
}
complete => unreachable!(),
}
if staged_info.is_some() && hanging_watcher.is_some() {
trace!("Sending watcher info: {:?}", staged_info.as_ref().unwrap());
hanging_watcher.take().unwrap().send(staged_info.take().unwrap())?;
}
}
Ok(())
}
}
async fn update_attributes(
controller: &avrcp::ControllerProxy,
info_delta: &mut sessions2::PlayerInfoDelta,
status: &mut ValidPlayerStatus,
) -> Result<(), Error> {
let attributes = controller
.get_media_attributes()
.await?
.or_else(|e| Err(format_err!("AVRCP error: {:?}", e)))?;
info_delta.metadata = Some(attributes_to_metadata(&attributes));
if let Some(playing_time) = attributes.playing_time {
if let Ok(millis) = playing_time.parse::<i64>() {
status.duration = Some(zx::Duration::from_millis(millis).into_nanos());
}
}
Ok(())
}
async fn update_status(
controller: &avrcp::ControllerProxy,
status: &mut ValidPlayerStatus,
) -> Result<(), Error> {
let avrcp_status = controller
.get_play_status()
.await?
.or_else(|e| Err(format_err!("AVRCP error: {:?}", e)))?;
let playback_status =
avrcp_status.playback_status.ok_or(format_err!("PlayStatus must have playback status"))?;
status.set_state_from_avrcp(playback_status);
status.duration =
avrcp_status.song_length.map(|m| zx::Duration::from_millis(m as i64).into_nanos());
avrcp_status.song_position.map(|m| status.set_position(m as i64));
Ok(())
}
macro_rules! nonempty_to_property {
( $source:expr, $prop_str:expr, $target:ident ) => {
if let Some(value) = $source {
$target.push(media::Property { label: $prop_str.to_string(), value: value.clone() });
}
};
}
fn attributes_to_metadata(attributes: &avrcp::MediaAttributes) -> media::Metadata {
let mut properties = Vec::new();
nonempty_to_property!(&attributes.title, media::METADATA_LABEL_TITLE, properties);
nonempty_to_property!(&attributes.artist_name, media::METADATA_LABEL_ARTIST, properties);
nonempty_to_property!(&attributes.album_name, media::METADATA_LABEL_ALBUM, properties);
nonempty_to_property!(&attributes.track_number, media::METADATA_LABEL_TRACK_NUMBER, properties);
nonempty_to_property!(&attributes.total_number_of_tracks, "total_number_of_tracks", properties);
nonempty_to_property!(&attributes.genre, media::METADATA_LABEL_GENRE, properties);
nonempty_to_property!(
&Some(String::from("Bluetooth")),
media::METADATA_SOURCE_TITLE,
properties
);
media::Metadata { properties }
}
async fn connect_avrcp(
avrcp: &mut avrcp::PeerManagerProxy,
peer_id: PeerId,
) -> Result<avrcp::ControllerProxy, Error> {
let (controller, server) = endpoints::create_proxy()?;
let _ = avrcp.get_controller_for_target(&peer_id.to_string(), server).await?;
controller.set_notification_filter(
avrcp::Notifications::PlaybackStatus
| avrcp::Notifications::Track
| avrcp::Notifications::TrackPos
| avrcp::Notifications::Connection,
5,
)?;
Ok(controller)
}
#[cfg(test)]
mod tests {
use super::*;
use fidl::endpoints::{self, RequestStream};
use fuchsia_async::pin_mut;
use futures::{task::Poll, Future};
use std::{convert::TryInto, pin::Pin};
fn setup_media_relay(
) -> Result<(sessions2::PlayerProxy, avrcp::PeerManagerRequestStream, impl Future), fidl::Error>
{
let (player_proxy, player_requests) =
endpoints::create_proxy_and_stream::<sessions2::PlayerMarker>()?;
let (avrcp_proxy, avrcp_requests) =
endpoints::create_proxy_and_stream::<avrcp::PeerManagerMarker>()?;
let peer_id = PeerId(0);
let relay_fut = AvrcpRelay::session_relay(avrcp_proxy, peer_id, player_requests);
Ok((player_proxy, avrcp_requests, relay_fut))
}
fn expect_media_attributes_request(
exec: &mut fasync::Executor,
controller_requests: &mut avrcp::ControllerRequestStream,
) -> Result<(), fidl::Error> {
// Should ask for the current media info and the status to return the correct results.
match exec.run_until_stalled(&mut controller_requests.next()) {
Poll::Ready(Some(Ok(avrcp::ControllerRequest::GetMediaAttributes { responder }))) => {
responder.send(&mut Ok(avrcp::MediaAttributes {
title: Some("Might Be Right".to_string()),
artist_name: Some("White Reaper".to_string()),
album_name: Some("You Deserve Love".to_string()),
track_number: Some("7".to_string()),
total_number_of_tracks: Some("10".to_string()),
genre: Some("Alternative".to_string()),
playing_time: Some("237000".to_string()),
..avrcp::MediaAttributes::EMPTY
}))
}
x => panic!("Expected a GetMediaAttributes request, got {:?}", x),
}
}
fn expect_play_status_request(
exec: &mut fasync::Executor,
controller_requests: &mut avrcp::ControllerRequestStream,
) -> Result<(), fidl::Error> {
match exec.run_until_stalled(&mut controller_requests.next()) {
Poll::Ready(Some(Ok(avrcp::ControllerRequest::GetPlayStatus { responder }))) => {
responder.send(&mut Ok(avrcp::PlayStatus {
song_length: Some(237000),
song_position: Some(1000),
playback_status: Some(avrcp::PlaybackStatus::Playing),
..avrcp::PlayStatus::EMPTY
}))
}
x => panic!("Expected a GetPlayStatus request, got {:?}", x),
}
}
fn finish_relay_setup(
mut relay_fut: &mut Pin<&mut impl Future>,
mut exec: &mut fasync::Executor,
mut avrcp_request_stream: avrcp::PeerManagerRequestStream,
) -> Result<avrcp::ControllerRequestStream, Error> {
// Connects to AVRCP.
let complete = exec.run_until_stalled(&mut avrcp_request_stream.select_next_some());
let mut controller_request_stream = match complete {
Poll::Ready(Ok(avrcp::PeerManagerRequest::GetControllerForTarget {
client,
responder,
..
})) => responder.send(&mut Ok(())).and_then(|_| client.into_stream())?,
x => panic!("Expected a GetController request, got {:?}", x),
};
let res = exec.run_until_stalled(&mut relay_fut);
assert!(res.is_pending());
let complete = exec.run_until_stalled(&mut controller_request_stream.select_next_some());
match complete {
Poll::Ready(Ok(avrcp::ControllerRequest::SetNotificationFilter { .. })) => {}
x => panic!("Expected notifications to be set, got {:?}", x),
};
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
expect_media_attributes_request(&mut exec, &mut controller_request_stream)?;
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
expect_play_status_request(&mut exec, &mut controller_request_stream)?;
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
// At this point, the relay is set up and should be waiting for requests from
// player_client, and sending commands / getting notifications from avrcp.
Ok(controller_request_stream)
}
fn run_to_stalled(exec: &mut fasync::Executor) {
let _ = exec.run_until_stalled(&mut futures::future::pending::<()>());
}
#[test]
/// Test that the relay sets up the connection to AVRCP and Sessions and stops on the stop
/// signal.
fn test_relay_setup() -> Result<(), Error> {
let mut exec = fasync::Executor::new().expect("executor needed");
let (player_client, avrcp_requests, relay_fut) = setup_media_relay()?;
let controller_requests;
{
pin_mut!(relay_fut);
let res = exec.run_until_stalled(&mut relay_fut);
assert!(res.is_pending());
controller_requests = finish_relay_setup(&mut relay_fut, &mut exec, avrcp_requests);
// Dropping the relay future drops all the connections.
}
run_to_stalled(&mut exec);
let mut controller_requests = controller_requests?;
match exec.run_until_stalled(&mut controller_requests.next()) {
Poll::Ready(None) => {}
x => panic!("Expected controller to be dropped, but got {:?}", x),
};
let mut watch_info_fut = player_client.watch_info_change();
match exec.run_until_stalled(&mut watch_info_fut) {
Poll::Ready(Err(_e)) => {}
x => panic!("Expected player to be disconnected, but got {:?} from watch_info", x),
};
Ok(())
}
#[test]
/// Relay will stop when AVRCP closes the notification channel.
fn test_relay_avrcp_ends() -> Result<(), Error> {
let mut exec = fasync::Executor::new().expect("executor needed");
let (player_client, avrcp_requests, relay_fut) = setup_media_relay()?;
pin_mut!(relay_fut);
let res = exec.run_until_stalled(&mut relay_fut);
assert!(res.is_pending());
let controller_requests = finish_relay_setup(&mut relay_fut, &mut exec, avrcp_requests)?;
// Closing the AVRCP controller should end the relay.
drop(controller_requests);
let res = exec.run_until_stalled(&mut relay_fut);
assert!(res.is_ready());
// The MediaSession should also drop based on this.
let mut watch_info_fut = player_client.watch_info_change();
match exec.run_until_stalled(&mut watch_info_fut) {
Poll::Ready(Err(_e)) => {}
x => panic!("Expected player to be disconnected, but got {:?} from watch_info", x),
};
Ok(())
}
#[test]
/// Relay will stop when Player stops asking for updates.
fn test_relay_player_ends() -> Result<(), Error> {
let mut exec = fasync::Executor::new().expect("executor needed");
let (player_client, avrcp_requests, relay_fut) = setup_media_relay()?;
pin_mut!(relay_fut);
let res = exec.run_until_stalled(&mut relay_fut);
assert!(res.is_pending());
let mut controller_requests =
finish_relay_setup(&mut relay_fut, &mut exec, avrcp_requests)?;
// Closing of the MediaSession should end the relay.
drop(player_client);
let res = exec.run_until_stalled(&mut relay_fut);
assert!(res.is_ready());
match exec.run_until_stalled(&mut controller_requests.next()) {
Poll::Ready(None) => {}
x => panic!("Expected controller to be dropped, but got {:?}", x),
};
Ok(())
}
#[test]
/// When mediasession initially asks for media info, a query of the remote AVRCP is made and
/// the data is translated.
fn test_relay_sends_correct_media_info() -> Result<(), Error> {
let mut exec = fasync::Executor::new_with_fake_time().expect("executor needed");
exec.set_fake_time(fasync::Time::from_nanos(7000));
let (player_client, avrcp_requests, relay_fut) = setup_media_relay()?;
pin_mut!(relay_fut);
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
let _controller_requests = finish_relay_setup(&mut relay_fut, &mut exec, avrcp_requests)?;
let mut watch_info_fut = player_client.watch_info_change();
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
// Should return to the player the initial data.
let info_delta = match exec.run_until_stalled(&mut watch_info_fut) {
Poll::Ready(Ok(delta)) => delta,
x => panic!("Expected WatchInfoChange to complete, instead: {:?}", x),
};
assert_eq!(
ValidPlayerStatus {
duration: Some(237000_000_000),
player_state: sessions2::PlayerState::Playing,
timeline_function: Some(media::TimelineFunction {
subject_time: 1000_000_000,
reference_time: 7000,
subject_delta: 1,
reference_delta: 1,
}),
repeat_mode: sessions2::RepeatMode::Single,
shuffle_on: false,
content_type: sessions2::ContentType::Audio,
error: None
},
info_delta.player_status.unwrap().try_into().expect("valid player status")
);
Ok(())
}
#[test]
/// When playback status changes the new track info is sent to the Player client.
fn test_relay_new_avrcp_track_info() -> Result<(), Error> {
let mut exec = fasync::Executor::new_with_fake_time().expect("executor needed");
exec.set_fake_time(fasync::Time::from_nanos(7000));
let (player_client, avrcp_requests, relay_fut) = setup_media_relay()?;
pin_mut!(relay_fut);
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
let mut controller_requests =
finish_relay_setup(&mut relay_fut, &mut exec, avrcp_requests)?;
let mut watch_info_fut = player_client.watch_info_change();
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
// Should return to the player the initial data.
let _info_delta = match exec.run_until_stalled(&mut watch_info_fut) {
Poll::Ready(Ok(delta)) => delta,
x => panic!("Expected WatchInfoChange to complete, instead: {:?}", x),
};
// Queueing up another one with no change should just hang.
let mut watch_info_fut = player_client.watch_info_change();
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
assert!(exec.run_until_stalled(&mut watch_info_fut).is_pending());
// When a play status change notification happens, we get new requests.
controller_requests.control_handle().send_on_notification(
7000,
avrcp::Notification {
status: Some(avrcp::PlaybackStatus::Paused),
..avrcp::Notification::EMPTY
},
)?;
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
// Should ask for the current media info and the status to return the correct results.
match exec.run_until_stalled(&mut controller_requests.next()) {
Poll::Ready(Some(Ok(avrcp::ControllerRequest::GetMediaAttributes { responder }))) => {
responder.send(&mut Ok(avrcp::MediaAttributes {
title: Some("Moneygrabber".to_string()),
artist_name: Some("Fitz and the Tantrums".to_string()),
album_name: Some("Pickin' Up the Pieces".to_string()),
track_number: Some("4".to_string()),
total_number_of_tracks: Some("11".to_string()),
genre: Some("Alternative".to_string()),
playing_time: Some("189000".to_string()),
..avrcp::MediaAttributes::EMPTY
}))?;
}
x => panic!("Expected a GetMediaAttributes request, got {:?}", x),
}
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
match exec.run_until_stalled(&mut controller_requests.next()) {
Poll::Ready(Some(Ok(avrcp::ControllerRequest::GetPlayStatus { responder }))) => {
responder.send(&mut Ok(avrcp::PlayStatus {
song_length: Some(189000),
song_position: Some(1000),
playback_status: Some(avrcp::PlaybackStatus::Paused),
..avrcp::PlayStatus::EMPTY
}))?;
}
x => panic!("Expected a GetPlayStatus request, got {:?}", x),
};
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
// After the AVRCP requests, the info should have the delta.
let info_delta = match exec.run_until_stalled(&mut watch_info_fut) {
Poll::Ready(Ok(delta)) => delta,
x => panic!("Expected WatchInfoChange to complete, instead: {:?}", x),
};
// After the notification is handled we should get an ack.
match exec.run_until_stalled(&mut controller_requests.next()) {
Poll::Ready(Some(Ok(avrcp::ControllerRequest::NotifyNotificationHandled {
..
}))) => {}
x => panic!("Expected ack of notification, but got {:?}", x),
};
assert_eq!(
ValidPlayerStatus {
duration: Some(189000_000_000),
player_state: sessions2::PlayerState::Paused,
timeline_function: Some(media::TimelineFunction {
subject_time: 1000_000_000,
reference_time: 7000,
subject_delta: 0,
reference_delta: 1,
}),
repeat_mode: sessions2::RepeatMode::Single,
shuffle_on: false,
content_type: sessions2::ContentType::Audio,
error: None
},
info_delta.player_status.unwrap().try_into().expect("valid player status")
);
Ok(())
}
#[test]
/// When the position update happens, the new position is updated for the Player.
fn test_relay_updates_position() -> Result<(), Error> {
let mut exec = fasync::Executor::new_with_fake_time().expect("executor needed");
exec.set_fake_time(fasync::Time::from_nanos(7000));
let (player_client, avrcp_requests, relay_fut) = setup_media_relay()?;
pin_mut!(relay_fut);
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
let mut controller_requests =
finish_relay_setup(&mut relay_fut, &mut exec, avrcp_requests)?;
let mut watch_info_fut = player_client.watch_info_change();
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
// Should return to the player the initial data.
let _info_delta = match exec.run_until_stalled(&mut watch_info_fut) {
Poll::Ready(Ok(delta)) => delta,
x => panic!("Expected WatchInfoChange to complete, instead: {:?}", x),
};
// Queueing up another one with no change should just hang.
let mut watch_info_fut = player_client.watch_info_change();
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
assert!(exec.run_until_stalled(&mut watch_info_fut).is_pending());
exec.set_fake_time(fasync::Time::from_nanos(9000));
// When a play status change notification happens, we get new requests.
controller_requests.control_handle().send_on_notification(
9000,
avrcp::Notification { pos: Some(3051), ..avrcp::Notification::EMPTY },
)?;
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
// After the notification is handled we should get an ack.
match exec.run_until_stalled(&mut controller_requests.next()) {
Poll::Ready(Some(Ok(avrcp::ControllerRequest::NotifyNotificationHandled {
..
}))) => {}
x => panic!("Expected ack of notification, but got {:?}", x),
};
// The info should have the delta.
let info_delta = match exec.run_until_stalled(&mut watch_info_fut) {
Poll::Ready(Ok(delta)) => delta,
x => panic!("Expected WatchInfoChange to complete, instead: {:?}", x),
};
assert_eq!(
ValidPlayerStatus {
duration: Some(237000_000_000),
player_state: sessions2::PlayerState::Playing,
timeline_function: Some(media::TimelineFunction {
subject_time: 3051_000_000,
reference_time: 9000,
subject_delta: 1,
reference_delta: 1,
}),
repeat_mode: sessions2::RepeatMode::Single,
shuffle_on: false,
content_type: sessions2::ContentType::Audio,
error: None
},
info_delta.player_status.unwrap().try_into().expect("valid player status")
);
Ok(())
}
fn expect_panel_command(
exec: &mut fasync::Executor,
controller_requests: &mut avrcp::ControllerRequestStream,
expected_command: avrcp::AvcPanelCommand,
) -> Result<(), fidl::Error> {
match exec.run_until_stalled(&mut controller_requests.next()) {
Poll::Ready(Some(Ok(avrcp::ControllerRequest::SendCommand { command, responder }))) => {
assert_eq!(expected_command, command);
responder.send(&mut Ok(()))
}
x => panic!("Expected a SendCommand({:?}) request, got {:?}", expected_command, x),
}
}
#[test]
/// When commands come from the Player, they are relayed to the AVRCP commands.
fn test_relay_sends_commands() -> Result<(), Error> {
let mut exec = fasync::Executor::new().expect("executor needed");
let (player_client, avrcp_requests, relay_fut) = setup_media_relay()?;
pin_mut!(relay_fut);
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
let mut controller_requests =
finish_relay_setup(&mut relay_fut, &mut exec, avrcp_requests)?;
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
player_client.pause()?;
player_client.play()?;
player_client.stop()?;
player_client.next_item()?;
player_client.prev_item()?;
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
expect_panel_command(&mut exec, &mut controller_requests, avrcp::AvcPanelCommand::Pause)?;
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
expect_panel_command(&mut exec, &mut controller_requests, avrcp::AvcPanelCommand::Play)?;
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
expect_panel_command(&mut exec, &mut controller_requests, avrcp::AvcPanelCommand::Stop)?;
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
expect_panel_command(&mut exec, &mut controller_requests, avrcp::AvcPanelCommand::Forward)?;
assert!(exec.run_until_stalled(&mut relay_fut).is_pending());
expect_panel_command(
&mut exec,
&mut controller_requests,
avrcp::AvcPanelCommand::Backward,
)?;
Ok(())
}
}