sl4f_lib/bluetooth/
avrcp_facade.rs

1// Copyright 2021 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.
4
5use super::types::{
6    CustomAvcPanelCommand, CustomBatteryStatus, CustomPlayStatus, CustomPlayerApplicationSettings,
7    CustomPlayerApplicationSettingsAttributeIds,
8};
9use crate::common_utils::common::macros::{fx_err_and_bail, with_line};
10use anyhow::Error;
11use fidl::endpoints::create_endpoints;
12use fidl_fuchsia_bluetooth::PeerId;
13use fidl_fuchsia_bluetooth_avrcp::{
14    ControllerMarker, ControllerProxy, Notifications, PeerManagerMarker, PeerManagerProxy,
15};
16use fuchsia_component::client;
17use fuchsia_sync::RwLock;
18use log::info;
19/// AvrcpFacadeInner contains the proxies used by the AvrcpFacade.
20#[derive(Debug)]
21struct AvrcpFacadeInner {
22    /// Proxy for the PeerManager service.
23    avrcp_service_proxy: Option<PeerManagerProxy>,
24    /// Proxy for the Controller service.
25    controller_proxy: Option<ControllerProxy>,
26}
27
28#[derive(Debug)]
29pub struct AvrcpFacade {
30    inner: RwLock<AvrcpFacadeInner>,
31}
32
33impl AvrcpFacade {
34    pub fn new() -> AvrcpFacade {
35        AvrcpFacade {
36            inner: RwLock::new(AvrcpFacadeInner {
37                avrcp_service_proxy: None,
38                controller_proxy: None,
39            }),
40        }
41    }
42
43    /// Creates the proxy to the PeerManager service.
44    async fn create_avrcp_service_proxy(&self) -> Result<PeerManagerProxy, Error> {
45        let tag = "AvrcpFacade::create_avrcp_service_proxy";
46        match self.inner.read().avrcp_service_proxy.clone() {
47            Some(avrcp_service_proxy) => {
48                info!(
49                    tag = &with_line!(tag);
50                    "Current AVRCP service proxy: {:?}", avrcp_service_proxy
51                );
52                Ok(avrcp_service_proxy)
53            }
54            None => {
55                let avrcp_service_proxy = client::connect_to_protocol::<PeerManagerMarker>();
56                if let Err(err) = avrcp_service_proxy {
57                    fx_err_and_bail!(
58                        &with_line!(tag),
59                        format_err!("Failed to create AVRCP service proxy: {}", err)
60                    );
61                }
62                avrcp_service_proxy
63            }
64        }
65    }
66
67    /// Initialize the AVRCP service and retrieve the controller for the provided Bluetooth target id.
68    ///
69    /// # Arguments
70    /// * `id` - A u64 representing the device ID.
71    pub async fn init_avrcp(&self, id: u64) -> Result<(), Error> {
72        let tag = "AvrcpFacade::init_avrcp";
73        self.inner.write().avrcp_service_proxy = Some(self.create_avrcp_service_proxy().await?);
74        let avrcp_service_proxy = match &self.inner.read().avrcp_service_proxy {
75            Some(p) => p.clone(),
76            None => fx_err_and_bail!(&with_line!(tag), "No AVRCP service proxy created"),
77        };
78        let (cont_client, cont_server) = create_endpoints::<ControllerMarker>();
79        let _status = avrcp_service_proxy
80            .get_controller_for_target(&PeerId { value: id }, cont_server)
81            .await?;
82        self.inner.write().controller_proxy = Some(cont_client.into_proxy());
83        Ok(())
84    }
85
86    /// Returns the media attributes from the controller.
87    pub async fn get_media_attributes(&self) -> Result<String, Error> {
88        let tag = "AvrcpFacade::get_media_attributes";
89        match self.inner.read().controller_proxy.clone() {
90            Some(proxy) => match proxy.get_media_attributes().await? {
91                Ok(media_attribs) => Ok(format!("Media attributes: {:#?}", media_attribs)),
92                Err(e) => fx_err_and_bail!(
93                    &with_line!(tag),
94                    format!("Error fetching media attributes: {:?}", e)
95                ),
96            },
97            None => fx_err_and_bail!(&with_line!(tag), "No AVRCP service proxy available"),
98        }
99    }
100
101    /// Returns the play status from the controller.
102    pub async fn get_play_status(&self) -> Result<CustomPlayStatus, Error> {
103        let tag = "AvrcpFacade::get_play_status";
104        match self.inner.read().controller_proxy.clone() {
105            Some(proxy) => match proxy.get_play_status().await? {
106                Ok(play_status) => Ok(CustomPlayStatus::new(&play_status)),
107                Err(e) => fx_err_and_bail!(
108                    &with_line!(tag),
109                    format!("Error fetching play status: {:?}", e)
110                ),
111            },
112            None => fx_err_and_bail!(&with_line!(tag), "No AVRCP service proxy available"),
113        }
114    }
115
116    /// Sends an AVCPanelCommand to the controller.
117    ///
118    /// # Arguments
119    /// * `command` - an enum representing the AVCPanelCommand.
120    pub async fn send_command(&self, command: CustomAvcPanelCommand) -> Result<(), Error> {
121        let tag = "AvrcpFacade::send_command";
122        let result = match self.inner.read().controller_proxy.clone() {
123            Some(proxy) => proxy.send_command(command.into()).await?,
124            None => fx_err_and_bail!(&with_line!(tag), "No AVRCP service proxy available"),
125        };
126        match result {
127            Ok(res) => Ok(res),
128            Err(err) => {
129                fx_err_and_bail!(&with_line!(tag), format!("Error sending command:{:?}", err))
130            }
131        }
132    }
133
134    /// Sends an AVCPanelCommand to the controller.
135    ///
136    /// # Arguments
137    /// * `absolute_volume` - the value to which the volume is set.
138    pub async fn set_absolute_volume(&self, absolute_volume: u8) -> Result<u8, Error> {
139        let tag = "AvrcpFacade::set_absolute_volume";
140        let result = match self.inner.read().controller_proxy.clone() {
141            Some(proxy) => proxy.set_absolute_volume(absolute_volume).await?,
142            None => fx_err_and_bail!(&with_line!(tag), "No AVRCP service proxy available"),
143        };
144        match result {
145            Ok(res) => Ok(res),
146            Err(err) => {
147                fx_err_and_bail!(&with_line!(tag), format!("Error setting volume:{:?}", err))
148            }
149        }
150    }
151
152    /// Returns the player application settings from the controller.
153    ///
154    /// # Arguments
155    /// * `attribute_ids` - the attribute ids for the application settings to return.  If empty, returns all.
156    pub async fn get_player_application_settings(
157        &self,
158        attribute_ids: CustomPlayerApplicationSettingsAttributeIds,
159    ) -> Result<CustomPlayerApplicationSettings, Error> {
160        let tag = "AvrcpFacade::get_player_application_settings";
161        match self.inner.read().controller_proxy.clone() {
162            Some(proxy) => {
163                match proxy.get_player_application_settings(&attribute_ids.to_vec()).await? {
164                    Ok(player_application_settings) => Ok(player_application_settings.into()),
165                    Err(e) => fx_err_and_bail!(
166                        &with_line!(tag),
167                        format!("Error fetching player application settings: {:?}", e)
168                    ),
169                }
170            }
171            None => fx_err_and_bail!(&with_line!(tag), "No AVRCP service proxy available"),
172        }
173    }
174
175    /// Sets the player application settings on the controller.
176    ///
177    /// # Arguments
178    /// * `settings` - the player application settings to set.
179    pub async fn set_player_application_settings(
180        &self,
181        settings: CustomPlayerApplicationSettings,
182    ) -> Result<CustomPlayerApplicationSettings, Error> {
183        let tag = "AvrcpFacade::set_player_application_settings";
184        match self.inner.read().controller_proxy.clone() {
185            Some(proxy) => match proxy.set_player_application_settings(&settings.into()).await? {
186                Ok(player_application_settings) => Ok(player_application_settings.into()),
187                Err(e) => fx_err_and_bail!(
188                    &with_line!(tag),
189                    format!("Error fetching player application settings: {:?}", e)
190                ),
191            },
192            None => fx_err_and_bail!(&with_line!(tag), "No AVRCP service proxy available"),
193        }
194    }
195
196    /// Informs battery status on the controller.
197    ///
198    /// # Arguments
199    /// * `battery_status` - the battery status to inform.
200    pub async fn inform_battery_status(
201        &self,
202        battery_status: CustomBatteryStatus,
203    ) -> Result<(), Error> {
204        let tag = "AvrcpFacade::inform_battery_status";
205        match self.inner.read().controller_proxy.clone() {
206            Some(proxy) => match proxy.inform_battery_status(battery_status.into()).await? {
207                Ok(()) => Ok(()),
208                Err(e) => fx_err_and_bail!(
209                    &with_line!(tag),
210                    format!("Error informing battery status: {:?}", e)
211                ),
212            },
213            None => fx_err_and_bail!(&with_line!(tag), "No AVRCP service proxy available"),
214        }
215    }
216
217    /// Sets addressed player on the controller.
218    ///
219    /// # Arguments
220    /// * `player_id` - the player id to set as the addressed player.
221    pub async fn set_addressed_player(&self, player_id: u16) -> Result<(), Error> {
222        let tag = "AvrcpFacade::set_addressed_player";
223        match self.inner.read().controller_proxy.clone() {
224            Some(proxy) => match proxy.set_addressed_player(player_id).await? {
225                Ok(()) => Ok(()),
226                Err(e) => fx_err_and_bail!(
227                    &with_line!(tag),
228                    format!("Error setting addressed player: {:?}", e)
229                ),
230            },
231            None => fx_err_and_bail!(&with_line!(tag), "No AVRCP service proxy available"),
232        }
233    }
234
235    /// Sets notification filter on the controller.
236    ///
237    /// # Arguments
238    /// * `notifications_filter` - the notification ids as bit flags for which to filter.
239    /// * `position_change_interval` - the interval in seconds that the controller client would
240    ///  like to be notified of `TRACK_POS_CHANGED` events.  It is ignored if 'TRACK_POS' bit flag is not set.
241    pub async fn set_notification_filter(
242        &self,
243        notifications_filter: u32,
244        position_change_interval: u32,
245    ) -> Result<(), Error> {
246        let tag = "AvrcpFacade::set_notification_filter";
247        let notifications = match Notifications::from_bits(notifications_filter) {
248            Some(notifications) => notifications,
249            _ => fx_err_and_bail!(
250                &with_line!(tag),
251                format!(
252                    "Invalid bit flags value for notifications filter: {:?}",
253                    notifications_filter
254                )
255            ),
256        };
257        match self.inner.read().controller_proxy.clone() {
258            Some(proxy) => {
259                match proxy.set_notification_filter(notifications, position_change_interval) {
260                    Ok(()) => Ok(()),
261                    Err(e) => fx_err_and_bail!(
262                        &with_line!(tag),
263                        format!("Error setting notification filter: {:?}", e)
264                    ),
265                }
266            }
267            None => fx_err_and_bail!(&with_line!(tag), "No AVRCP service proxy available"),
268        }
269    }
270
271    /// Notifies that the OnNotification event was handled.
272    pub async fn notify_notification_handled(&self) -> Result<(), Error> {
273        let tag = "AvrcpFacade::notify_notification_handled";
274        match self.inner.read().controller_proxy.clone() {
275            Some(proxy) => match proxy.notify_notification_handled() {
276                Ok(()) => Ok(()),
277                Err(e) => fx_err_and_bail!(
278                    &with_line!(tag),
279                    format!("Error setting notification filter: {:?}", e)
280                ),
281            },
282            None => fx_err_and_bail!(&with_line!(tag), "No AVRCP service proxy available"),
283        }
284    }
285
286    /// A function to remove the profile service proxy and clear connected devices.
287    fn clear(&self) {
288        self.inner.write().avrcp_service_proxy = None;
289        self.inner.write().controller_proxy = None;
290    }
291
292    /// Cleanup any Profile Server related objects.
293    pub async fn cleanup(&self) -> Result<(), Error> {
294        self.clear();
295        Ok(())
296    }
297}
298
299#[cfg(test)]
300mod tests {
301
302    use super::super::types::{
303        CustomCustomAttributeValue, CustomCustomPlayerApplicationSetting, CustomEqualizer,
304        CustomRepeatStatusMode, CustomScanMode,
305    };
306    use super::*;
307    use assert_matches::assert_matches;
308    use fidl::endpoints::create_proxy_and_stream;
309    use fidl_fuchsia_bluetooth_avrcp::{BatteryStatus, ControllerRequest, PlayStatus};
310    use fuchsia_async as fasync;
311    use futures::prelude::*;
312    use lazy_static::lazy_static;
313
314    lazy_static! {
315        static ref PLAY_STATUS: CustomPlayStatus = CustomPlayStatus {
316            song_length: Some(120),
317            song_position: Some(10),
318            playback_status: Some(4),
319        };
320        static ref PLAYER_APPLICATION_SETTINGS: CustomPlayerApplicationSettings =
321            CustomPlayerApplicationSettings {
322                equalizer: Some(CustomEqualizer::Off),
323                repeat_status_mode: Some(CustomRepeatStatusMode::AllTrackRepeat),
324                shuffle_mode: None,
325                scan_mode: Some(CustomScanMode::GroupScan),
326                custom_settings: Some(vec![CustomCustomPlayerApplicationSetting {
327                    attribute_id: Some(1),
328                    attribute_name: Some("attribute".to_string()),
329                    possible_values: Some(vec![CustomCustomAttributeValue {
330                        description: "description".to_string(),
331                        value: 5
332                    }]),
333                    current_value: Some(5),
334                }])
335            };
336        static ref PLAYER_APPLICATION_SETTINGS_INPUT: CustomPlayerApplicationSettings =
337            CustomPlayerApplicationSettings {
338                equalizer: Some(CustomEqualizer::Off),
339                repeat_status_mode: None,
340                shuffle_mode: None,
341                scan_mode: None,
342                custom_settings: Some(vec![CustomCustomPlayerApplicationSetting {
343                    attribute_id: Some(1),
344                    attribute_name: Some("attribute".to_string()),
345                    possible_values: Some(vec![CustomCustomAttributeValue {
346                        description: "description".to_string(),
347                        value: 5
348                    }]),
349                    current_value: Some(5),
350                }])
351            };
352        static ref PLAYER_APPLICATION_SETTINGS_ATTRIBUTE_IDS: CustomPlayerApplicationSettingsAttributeIds =
353            CustomPlayerApplicationSettingsAttributeIds { attribute_ids: Some(vec![1]) };
354    }
355    struct MockAvrcpTester {
356        expected_state: Vec<Box<dyn FnOnce(ControllerRequest) + Send + 'static>>,
357    }
358
359    impl MockAvrcpTester {
360        fn new() -> Self {
361            Self { expected_state: vec![] }
362        }
363
364        fn push(mut self, request: impl FnOnce(ControllerRequest) + Send + 'static) -> Self {
365            self.expected_state.push(Box::new(request));
366            self
367        }
368
369        fn build_controller(self) -> (AvrcpFacade, impl Future<Output = ()>) {
370            let (proxy, mut stream) = create_proxy_and_stream::<ControllerMarker>();
371            let fut = async move {
372                for expected in self.expected_state {
373                    expected(stream.next().await.unwrap().unwrap());
374                }
375                assert_matches!(stream.next().await, None);
376            };
377            (
378                AvrcpFacade {
379                    inner: RwLock::new(AvrcpFacadeInner {
380                        controller_proxy: Some(proxy),
381                        avrcp_service_proxy: None,
382                    }),
383                },
384                fut,
385            )
386        }
387
388        fn expect_get_play_status(self, result: CustomPlayStatus) -> Self {
389            self.push(move |req| match req {
390                ControllerRequest::GetPlayStatus { responder } => {
391                    responder.send(Ok(&PlayStatus::from(result))).unwrap();
392                }
393                _ => {}
394            })
395        }
396
397        fn expect_get_player_application_settings(
398            self,
399            result: CustomPlayerApplicationSettings,
400            input: &'static CustomPlayerApplicationSettingsAttributeIds,
401        ) -> Self {
402            self.push(move |req| match req {
403                ControllerRequest::GetPlayerApplicationSettings { attribute_ids, responder } => {
404                    assert_eq!(attribute_ids, input.to_vec());
405                    responder.send(Ok(&result.into())).unwrap();
406                }
407                _ => {}
408            })
409        }
410
411        fn expect_set_player_application_settings(
412            self,
413            result: CustomPlayerApplicationSettings,
414            input: &'static CustomPlayerApplicationSettings,
415        ) -> Self {
416            self.push(move |req| match req {
417                ControllerRequest::SetPlayerApplicationSettings {
418                    requested_settings,
419                    responder,
420                } => {
421                    let player_application_settings: CustomPlayerApplicationSettings =
422                        requested_settings.into();
423                    assert_eq!(player_application_settings, *input);
424                    responder.send(Ok(&result.into())).unwrap();
425                }
426                _ => {}
427            })
428        }
429
430        fn expect_inform_battery_status(self, input: CustomBatteryStatus) -> Self {
431            self.push(move |req| match req {
432                ControllerRequest::InformBatteryStatus { battery_status, responder } => {
433                    let battery_status_expected: BatteryStatus = input.into();
434                    assert_eq!(battery_status_expected, battery_status);
435                    responder.send(Ok(())).unwrap();
436                }
437                _ => {}
438            })
439        }
440
441        fn expect_set_addressed_player(self, input: u16) -> Self {
442            self.push(move |req| match req {
443                ControllerRequest::SetAddressedPlayer { player_id, responder } => {
444                    assert_eq!(input, player_id);
445                    responder.send(Ok(())).unwrap();
446                }
447                _ => {}
448            })
449        }
450
451        fn expect_set_notification_filter(
452            self,
453            input_notification_filter: u32,
454            input_position_change_interval: u32,
455        ) -> Self {
456            self.push(move |req| match req {
457                ControllerRequest::SetNotificationFilter {
458                    notifications,
459                    position_change_interval,
460                    ..
461                } => {
462                    assert_eq!(
463                        Notifications::from_bits(input_notification_filter).unwrap(),
464                        notifications
465                    );
466                    assert_eq!(input_position_change_interval, position_change_interval);
467                }
468                _ => {}
469            })
470        }
471    }
472    #[fasync::run_singlethreaded(test)]
473    async fn test_get_play_status() {
474        let (facade, play_status_fut) =
475            MockAvrcpTester::new().expect_get_play_status(*PLAY_STATUS).build_controller();
476        let facade_fut = async move {
477            let play_status = facade.get_play_status().await.unwrap();
478            assert_eq!(play_status, *PLAY_STATUS);
479        };
480        future::join(facade_fut, play_status_fut).await;
481    }
482
483    #[fasync::run_singlethreaded(test)]
484    async fn test_get_player_application_settings() {
485        let (facade, application_settings_fut) = MockAvrcpTester::new()
486            .expect_get_player_application_settings(
487                PLAYER_APPLICATION_SETTINGS.clone(),
488                &PLAYER_APPLICATION_SETTINGS_ATTRIBUTE_IDS,
489            )
490            .build_controller();
491        let facade_fut = async move {
492            let application_settings = facade
493                .get_player_application_settings(PLAYER_APPLICATION_SETTINGS_ATTRIBUTE_IDS.clone())
494                .await
495                .unwrap();
496            assert_eq!(application_settings, *PLAYER_APPLICATION_SETTINGS);
497        };
498        future::join(facade_fut, application_settings_fut).await;
499    }
500
501    #[fasync::run_singlethreaded(test)]
502    async fn test_set_player_application_settings() {
503        let (facade, application_settings_fut) = MockAvrcpTester::new()
504            .expect_set_player_application_settings(
505                PLAYER_APPLICATION_SETTINGS.clone(),
506                &PLAYER_APPLICATION_SETTINGS_INPUT,
507            )
508            .build_controller();
509        let facade_fut = async move {
510            let application_settings = facade
511                .set_player_application_settings(PLAYER_APPLICATION_SETTINGS_INPUT.clone())
512                .await
513                .unwrap();
514            assert_eq!(application_settings, *PLAYER_APPLICATION_SETTINGS);
515        };
516        future::join(facade_fut, application_settings_fut).await;
517    }
518
519    #[fasync::run_singlethreaded(test)]
520    async fn test_inform_battery_status() {
521        let (facade, battery_status_fut) = MockAvrcpTester::new()
522            .expect_inform_battery_status(CustomBatteryStatus::Normal)
523            .build_controller();
524        let facade_fut = async move {
525            facade.inform_battery_status(CustomBatteryStatus::Normal).await.unwrap();
526        };
527        future::join(facade_fut, battery_status_fut).await;
528    }
529
530    #[fasync::run_singlethreaded(test)]
531    async fn test_set_addressed_player() {
532        let addressed_player = 5;
533        let (facade, addressed_player_fut) =
534            MockAvrcpTester::new().expect_set_addressed_player(addressed_player).build_controller();
535        let facade_fut = async move {
536            facade.set_addressed_player(addressed_player).await.unwrap();
537        };
538        future::join(facade_fut, addressed_player_fut).await;
539    }
540
541    #[fasync::run_singlethreaded(test)]
542    async fn test_set_notification_filter() {
543        let input_notification_filter = 3;
544        let input_position_change_interval = 1;
545        let (facade, notification_filter_fut) = MockAvrcpTester::new()
546            .expect_set_notification_filter(
547                input_notification_filter,
548                input_position_change_interval,
549            )
550            .build_controller();
551        let facade_fut = async move {
552            facade
553                .set_notification_filter(input_notification_filter, input_position_change_interval)
554                .await
555                .unwrap();
556        };
557        future::join(facade_fut, notification_filter_fut).await;
558    }
559}