Skip to main content

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