blob: 013a99ede3566d8596bd04b83c553b574df4ab0b [file] [log] [blame]
// Copyright 2023 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::common_utils::common::macros::{fx_err_and_bail, with_line};
use crate::media_session::types::PlayerStatusWrapper;
use anyhow::Error;
use fidl::endpoints::create_request_stream;
use fidl_fuchsia_media_sessions2::{
self as sessions2, ActiveSessionMarker, ActiveSessionProxy, PlayerRegistration, PublisherMarker,
};
use fuchsia_async as fasync;
use fuchsia_component::client::connect_to_protocol;
use fuchsia_sync::RwLock;
use futures::StreamExt;
use std::sync::Arc;
use tracing::info;
#[derive(Debug)]
pub struct MediaSessionFacade {
active_session_proxy: Option<ActiveSessionProxy>,
player_task: RwLock<Option<fasync::Task<()>>>,
requests_received: Arc<RwLock<Vec<String>>>,
}
impl MediaSessionFacade {
pub fn new() -> Self {
Self {
active_session_proxy: None,
player_task: RwLock::new(None),
requests_received: Arc::new(RwLock::new(Vec::new())),
}
}
/// Returns the active session proxy.
fn get_active_session_proxy(&self) -> Result<ActiveSessionProxy, Error> {
if let Some(proxy) = &self.active_session_proxy {
Ok(proxy.clone())
} else {
match connect_to_protocol::<ActiveSessionMarker>() {
Ok(proxy) => Ok(proxy),
Err(e) => fx_err_and_bail!(
&with_line!("MediaSessionFacade::get_active_session_proxy"),
format_err!("Failed to create proxy: {:?}", e)
),
}
}
}
/// Returns the active media session's player status.
/// If there's no active session, it will return a None type value.
pub async fn watch_active_session_status(&self) -> Result<Option<PlayerStatusWrapper>, Error> {
let active_session_proxy = &self
.get_active_session_proxy()
.map_err(|e| format_err!("Failed to get active session proxy: {}", e))?;
let session = active_session_proxy
.watch_active_session()
.await
.map_err(|e| format_err!("Failed to watch active session: {}", e))?;
if let Some(session_control_marker) = session {
let info_delta = session_control_marker
.into_proxy()
.map_err(|e| format_err!("Failed to get session control proxy: {}", e))?
.watch_status()
.await
.map_err(|e| format_err!("Failed to watch session status: {}", e))?;
Ok(Some(info_delta.player_status.into()))
} else {
Ok(None)
}
}
/// Publishes a mock Media Player via the fuchsia.media.sessions2 Publisher protocol.
/// Clients can watch Media Sessions via the fuchsia.media.sessions2 Discovery protocol.
/// This mock player does not route requests; it just records the requests it receives.
/// The list of requests can be accessed through this facade's ListReceivedRequests command.
/// The Bluetooth AVRCP component forwards commands to the most recently activated player.
pub async fn publish_mock_player(&self) -> Result<(), Error> {
let requests_received = self.requests_received.clone();
let fut = async move {
let publisher = fuchsia_component::client::connect_to_protocol::<PublisherMarker>()
.expect("Failed to connect to Publisher");
let (player_client, mut player_request_stream) =
create_request_stream().expect("Failed to create request stream");
publisher
.publish(
player_client,
&PlayerRegistration {
domain: Some("domain://sl4f".to_string()),
..Default::default()
},
)
.await
.expect("Failed to publish Player");
let mut _player_info_change_responder = None;
let mut sent_response = false;
loop {
match player_request_stream.next().await {
Some(Ok(request)) => match request {
sessions2::PlayerRequest::WatchInfoChange { responder } => {
if !sent_response {
let _ = responder.send(&sessions2::PlayerInfoDelta::default());
sent_response = true;
} else {
// Save responder so request stream is not dropped
_player_info_change_responder = Some(responder);
}
}
request => {
requests_received.write().push(request.method_name().to_string());
}
},
Some(Err(e)) => {
info!(?e, "Mock player request stream error");
break;
}
None => {
info!("Mock player request stream terminated");
break;
}
}
}
};
if let Some(_task) = self.player_task.write().take() {
info!("Dropping old mock player");
}
*self.player_task.write() = Some(fasync::Task::spawn(fut));
info!("Publishing mock player");
Ok(())
}
/// Unpublishes a mock Media Player and resets the list of received requests.
/// Returns true if a mock Media Player was running and is now stopped.
pub async fn stop_mock_player(&self) -> Result<bool, Error> {
if let Some(_task) = self.player_task.write().take() {
info!("Stopping mock player");
self.requests_received.write().clear();
return Ok(true);
}
info!("Mock player stoppage requested, but no session exists");
Ok(false)
}
/// Returns a list of the player requests received by mock Media Players spawned by this
/// facade's PublishMockPlayer command.
pub async fn list_received_requests(&self) -> Result<Vec<String>, Error> {
Ok(self.requests_received.read().clone())
}
}