sl4f_lib/bluetooth/
ble_advertise_facade.rs

1// Copyright 2018 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 anyhow::{format_err, Context as _, Error};
6use fidl::endpoints::create_request_stream;
7use fidl_fuchsia_bluetooth_le::{
8    AdvertisedPeripheralMarker, AdvertisedPeripheralRequest, AdvertisedPeripheralRequestStream,
9    AdvertisingParameters, ConnectionProxy, PeripheralMarker, PeripheralProxy,
10};
11use fuchsia_bluetooth::types::PeerId;
12use fuchsia_sync::RwLock;
13use futures::{pin_mut, select, FutureExt, StreamExt};
14use log::{debug, error, info, warn};
15use serde_json::Value;
16use std::collections::HashMap;
17use std::sync::Arc;
18use {fuchsia_async as fasync, fuchsia_component as app};
19
20// Sl4f-Constants and Ble advertising related functionality
21use crate::bluetooth::types::FacadeArg;
22use crate::common_utils::common::macros::with_line;
23
24#[derive(Debug)]
25struct Connection {
26    // Note: `_connection` appears unused, but must not be dropped in order to
27    // keep the FIDL connection alive.
28    _connection: ConnectionProxy,
29    // Note: `_task` appears unused, but is polled in the background by the
30    // executor.
31    _task: fasync::Task<()>,
32}
33
34#[derive(Debug)]
35struct InnerBleAdvertiseFacade {
36    /// Advertised peripheral server of the facade, only one advertisement at a time.
37    advertise_task: Option<fasync::Task<()>>,
38
39    // Active connections.
40    connections: HashMap<PeerId, Connection>,
41
42    ///PeripheralProxy used for Bluetooth Connections
43    peripheral: Option<PeripheralProxy>,
44}
45
46/// Starts and stops device BLE advertisement(s).
47/// Note this object is shared among all threads created by server.
48#[derive(Debug)]
49pub struct BleAdvertiseFacade {
50    inner: Arc<RwLock<InnerBleAdvertiseFacade>>,
51}
52
53impl BleAdvertiseFacade {
54    pub fn new() -> BleAdvertiseFacade {
55        BleAdvertiseFacade {
56            inner: Arc::new(RwLock::new(InnerBleAdvertiseFacade {
57                advertise_task: None,
58                connections: HashMap::new(),
59                peripheral: None,
60            })),
61        }
62    }
63
64    fn set_advertise_task(
65        inner: &Arc<RwLock<InnerBleAdvertiseFacade>>,
66        task: Option<fasync::Task<()>>,
67    ) {
68        let tag = "BleAdvertiseFacade::set_advertise_task";
69        if task.is_some() {
70            info!(tag = &with_line!(tag); "Assigned new advertise task");
71        } else if inner.read().advertise_task.is_some() {
72            info!(tag = &with_line!(tag); "Cleared advertise task");
73        }
74        inner.write().advertise_task = task;
75    }
76
77    pub fn print(&self) {
78        let adv_status = match &self.inner.read().advertise_task {
79            Some(_) => "Valid",
80            None => "None",
81        };
82        info!(tag = &with_line!("BleAdvertiseFacade::print"),
83            adv_status:%,
84            peripheral:? = self.get_peripheral_proxy();
85            "BleAdvertiseFacade",
86        );
87    }
88
89    // Set the peripheral proxy only if none exists, otherwise, use existing
90    pub fn set_peripheral_proxy(&self) {
91        let tag = "BleAdvertiseFacade::set_peripheral_proxy";
92
93        let new_peripheral = match self.inner.read().peripheral.clone() {
94            Some(p) => {
95                warn!(tag = &with_line!(tag), current_peripheral:? = p; "");
96                Some(p)
97            }
98            None => {
99                let peripheral_svc: PeripheralProxy =
100                    app::client::connect_to_protocol::<PeripheralMarker>()
101                        .context("Failed to connect to BLE Peripheral service.")
102                        .unwrap();
103                Some(peripheral_svc)
104            }
105        };
106
107        self.inner.write().peripheral = new_peripheral
108    }
109
110    /// Start BLE advertisement
111    ///
112    /// # Arguments
113    /// * `args`: A JSON input representing advertisement parameters.
114    pub async fn start_adv(&self, args: Value) -> Result<(), Error> {
115        self.set_peripheral_proxy();
116        let parameters: AdvertisingParameters = FacadeArg::new(args).try_into()?;
117        let periph = &self.inner.read().peripheral.clone();
118        match &periph {
119            Some(p) => {
120                // Clear any existing advertisement.
121                BleAdvertiseFacade::set_advertise_task(&self.inner, None);
122
123                let advertise_task = fasync::Task::spawn(BleAdvertiseFacade::advertise(
124                    self.inner.clone(),
125                    p.clone(),
126                    parameters,
127                ));
128                info!(tag = "start_adv"; "Started advertising");
129                BleAdvertiseFacade::set_advertise_task(&self.inner, Some(advertise_task));
130                Ok(())
131            }
132            None => {
133                error!(tag = "start_adv"; "No peripheral created.");
134                return Err(format_err!("No peripheral proxy created."));
135            }
136        }
137    }
138
139    fn process_new_connection(
140        inner: Arc<RwLock<InnerBleAdvertiseFacade>>,
141        proxy: ConnectionProxy,
142        peer_id: PeerId,
143    ) {
144        let tag = "BleAdvertiseFacade::process_new_connection";
145
146        let mut stream = proxy.take_event_stream();
147
148        let inner_clone = inner.clone();
149        let stream_fut = async move {
150            while let Some(event) = stream.next().await {
151                match event {
152                    Ok(_) => debug!(tag = &with_line!(tag); "ignoring event for Connection"),
153                    Err(err) => {
154                        info!(tag = &with_line!(tag); "Connection ({}) error: {:?}", peer_id, err)
155                    }
156                }
157            }
158            info!(tag = &with_line!(tag); "peer {} disconnected", peer_id);
159            inner_clone.write().connections.remove(&peer_id);
160        };
161        let event_task = fasync::Task::spawn(stream_fut);
162        inner
163            .write()
164            .connections
165            .insert(peer_id, Connection { _connection: proxy, _task: event_task });
166    }
167
168    async fn process_advertised_peripheral_stream(
169        inner: Arc<RwLock<InnerBleAdvertiseFacade>>,
170        mut stream: AdvertisedPeripheralRequestStream,
171    ) {
172        let tag = "BleAdvertiseFacade::process_advertised_peripheral_stream";
173        while let Some(request) = stream.next().await {
174            match request {
175                Ok(AdvertisedPeripheralRequest::OnConnected { peer, connection, responder }) => {
176                    if let Err(err) = responder.send() {
177                        warn!(
178                            tag = &with_line!(tag);
179                            "error sending response to AdvertisedPeripheral::OnConnected: {}", err
180                        );
181                    }
182
183                    let proxy = connection.into_proxy();
184                    let peer_id: PeerId = peer.id.unwrap().into();
185                    BleAdvertiseFacade::process_new_connection(inner.clone(), proxy, peer_id);
186                }
187                Err(err) => {
188                    info!(tag = &with_line!(tag); "AdvertisedPeripheral error: {:?}", err);
189                }
190            }
191        }
192        info!(tag = &with_line!(tag); "AdvertisedPeripheral closed, stopping advertising");
193        BleAdvertiseFacade::set_advertise_task(&inner, None);
194    }
195
196    async fn advertise(
197        inner: Arc<RwLock<InnerBleAdvertiseFacade>>,
198        peripheral: PeripheralProxy,
199        parameters: AdvertisingParameters,
200    ) {
201        let tag = "BleAdvertiseFacade::advertise";
202        let (client_end, server_request_stream) =
203            create_request_stream::<AdvertisedPeripheralMarker>();
204
205        // advertise() only returns after advertising has been terminated, so we can't await here.
206        let advertise_fut = peripheral.advertise(&parameters, client_end);
207
208        let server_fut = BleAdvertiseFacade::process_advertised_peripheral_stream(
209            inner.clone(),
210            server_request_stream,
211        );
212
213        let advertise_fut_fused = advertise_fut.fuse();
214        let server_fut_fused = server_fut.fuse();
215        pin_mut!(advertise_fut_fused, server_fut_fused);
216        select! {
217             result = advertise_fut_fused => {
218                info!(tag = &with_line!(tag); "advertise() returned with result {:?}", result);
219             }
220             _ = server_fut_fused => {
221                info!(tag = &with_line!(tag); "AdvertisedPeripheral closed");
222             }
223        };
224
225        // Stop advertising.
226        inner.write().advertise_task.take();
227    }
228
229    pub fn stop_adv(&self) {
230        info!(tag = &with_line!("BleAdvertiseFacade::stop_adv"); "Stop advertising");
231        BleAdvertiseFacade::set_advertise_task(&self.inner, None);
232    }
233
234    pub fn get_peripheral_proxy(&self) -> Option<PeripheralProxy> {
235        self.inner.read().peripheral.clone()
236    }
237
238    // Close peripheral proxy
239    pub fn cleanup_peripheral_proxy(&self) {
240        self.inner.write().peripheral = None;
241    }
242
243    // Cancel all tasks and close all protocols.
244    pub fn cleanup(&self) {
245        self.inner.write().connections.clear();
246        self.stop_adv();
247        self.cleanup_peripheral_proxy();
248    }
249}