blob: 80ecff342cae53feb0365c2d2e68b3664f990c44 [file] [log] [blame]
// Copyright 2018 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};
use fidl;
use fidl::endpoints;
use fidl_fuchsia_bluetooth_gatt::{Characteristic, ReliableMode, RemoteServiceProxy, WriteOptions};
use fidl_fuchsia_bluetooth_gatt::{ClientProxy, ServiceInfo};
use fidl_fuchsia_bluetooth_le::RemoteDevice;
use fidl_fuchsia_bluetooth_le::{
CentralEvent, CentralMarker, CentralProxy, ConnectionOptions, ScanFilter,
};
use fuchsia_async as fasync;
use fuchsia_component as app;
use fuchsia_syslog::macros::*;
use futures::future::{Future, TryFutureExt};
use futures::stream::TryStreamExt;
use futures::task::Waker;
use parking_lot::RwLock;
use slab::Slab;
use std::collections::HashMap;
use std::str::FromStr;
use std::sync::Arc;
use fidl_fuchsia_bluetooth;
use fuchsia_bluetooth::types::Uuid;
use crate::bluetooth::types::{BleScanResponse, SerializableReadByTypeResult};
use crate::common_utils::common::macros::{fx_err_and_bail, with_line};
use crate::common_utils::error::Sl4fError;
#[derive(Debug)]
pub struct InnerGattClientFacade {
// FIDL proxy to the currently connected service, if any.
active_proxy: Option<RemoteServiceProxy>,
// central: CentralProxy used for Bluetooth connections
central: Option<CentralProxy>,
// devices: HashMap of key = device id and val = RemoteDevice structs discovered from a scan
devices: HashMap<String, RemoteDevice>,
// Pending requests to obtain a host
host_requests: Slab<Waker>,
// peripheral_ids: The identifier for the peripheral of a ConnectPeripheral FIDL call
// Key = peripheral id, value = ClientProxy
peripheral_ids: HashMap<String, ClientProxy>,
}
/// Perform Gatt Client operations.
///
/// Note this object is shared among all threads created by server.
///
#[derive(Debug)]
pub struct GattClientFacade {
inner: Arc<RwLock<InnerGattClientFacade>>,
}
impl GattClientFacade {
pub fn new() -> GattClientFacade {
GattClientFacade {
inner: Arc::new(RwLock::new(InnerGattClientFacade {
central: None,
devices: HashMap::new(),
host_requests: Slab::new(),
peripheral_ids: HashMap::new(),
active_proxy: None,
})),
}
}
pub async fn start_scan(&self, mut filter: Option<ScanFilter>) -> Result<(), Error> {
self.cleanup_devices();
// Set the central proxy if necessary and start a central_listener
GattClientFacade::set_central_proxy(self.inner.clone());
match &self.inner.read().central {
Some(c) => {
let status = c.start_scan(filter.as_mut()).await?;
match status.error {
Some(e) => {
return Err(format_err!("Failed to start scan: {}", Sl4fError::from(*e)))
}
None => Ok(()),
}
}
None => return Err(format_err!("No central proxy created.")),
}
}
pub async fn gattc_connect_to_service(
&self,
periph_id: String,
service_id: u64,
) -> Result<(), Error> {
let tag = "GattClientFacade::gattc_connect_to_service";
let client_proxy = self.get_client_from_peripherals(periph_id);
let (proxy, server) = endpoints::create_proxy()?;
// First close the connection to the currently active service.
if self.inner.read().active_proxy.is_some() {
self.inner.write().active_proxy = None;
}
match client_proxy {
Some(c) => {
c.connect_to_service(service_id, server)?;
self.inner.write().active_proxy = Some(proxy);
Ok(())
}
None => {
fx_log_err!(tag: &with_line!(tag), "Unable to connect to service.");
return Err(format_err!("No peripheral proxy created."));
}
}
}
pub async fn gattc_discover_characteristics(&self) -> Result<Vec<Characteristic>, Error> {
let tag = "GattClientFacade::gattc_discover_characteristics";
let discover_characteristics = match &self.inner.read().active_proxy {
Some(proxy) => proxy.discover_characteristics(),
None => fx_err_and_bail!(&with_line!(tag), "Central proxy not available."),
};
let (status, chrcs) =
discover_characteristics.await.map_err(|_| Sl4fError::new("Failed to send message"))?;
if let Some(e) = status.error {
let err_msg = format!("Failed to read characteristics: {}", Sl4fError::from(*e));
fx_err_and_bail!(&with_line!(tag), err_msg)
}
Ok(chrcs)
}
pub async fn gattc_write_char_by_id(&self, id: u64, write_value: Vec<u8>) -> Result<(), Error> {
let tag = "GattClientFacade::gattc_write_char_by_id";
let write_characteristic = match &self.inner.read().active_proxy {
Some(proxy) => proxy.write_characteristic(id, &write_value),
None => fx_err_and_bail!(&with_line!(tag), "Central proxy not available."),
};
let status =
write_characteristic.await.map_err(|_| Sl4fError::new("Failed to send message"))?;
match status.error {
Some(e) => {
let err_msg = format!("Failed to write to characteristic: {}", Sl4fError::from(*e));
fx_err_and_bail!(&with_line!(tag), err_msg)
}
None => Ok(()),
}
}
pub async fn gattc_write_long_char_by_id(
&self,
id: u64,
offset: u16,
write_value: Vec<u8>,
reliable_mode: bool,
) -> Result<(), Error> {
let tag = "GattClientFacade::gattc_write_long_char_by_id";
let reliable_mode = match reliable_mode {
true => ReliableMode::Enabled,
false => ReliableMode::Disabled,
};
let write_long_characteristic = match &self.inner.read().active_proxy {
Some(proxy) => proxy.write_long_characteristic(
id,
offset,
&write_value,
WriteOptions { reliable_mode: Some(reliable_mode), ..WriteOptions::EMPTY },
),
None => fx_err_and_bail!(&with_line!(tag), "Central proxy not available."),
};
let status = write_long_characteristic
.await
.map_err(|_| Sl4fError::new("Failed to send message"))?;
match status.error {
Some(e) => {
let err_msg =
format!("Failed to write long characteristic: {}", Sl4fError::from(*e));
fx_err_and_bail!(&with_line!(tag), err_msg)
}
None => Ok(()),
}
}
pub async fn gattc_write_char_by_id_without_response(
&self,
id: u64,
write_value: Vec<u8>,
) -> Result<(), Error> {
let tag = "GattClientFacade::gattc_write_char_by_id_without_response";
match &self.inner.read().active_proxy {
Some(proxy) => proxy
.write_characteristic_without_response(id, &write_value)
.map_err(|_| Sl4fError::new("Failed to send message").into()),
None => fx_err_and_bail!(&with_line!(tag), "Central proxy not available."),
}
}
pub async fn gattc_read_char_by_id(&self, id: u64) -> Result<Vec<u8>, Error> {
let tag = "GattClientFacade::gattc_read_char_by_id";
let read_characteristic = match &self.inner.read().active_proxy {
Some(proxy) => proxy.read_characteristic(id),
None => fx_err_and_bail!(&with_line!(tag), "Central proxy not available."),
};
let (status, value) =
read_characteristic.await.map_err(|_| Sl4fError::new("Failed to send message"))?;
match status.error {
Some(e) => {
let err_msg = format!("Failed to read characteristic: {}", Sl4fError::from(*e));
fx_err_and_bail!(&with_line!(tag), err_msg)
}
None => Ok(value),
}
}
pub async fn gattc_read_char_by_type(
&self,
raw_uuid: String,
) -> Result<Vec<SerializableReadByTypeResult>, Error> {
let tag = "GattClientFacade::gattc_read_char_by_type";
let uuid = match Uuid::from_str(&raw_uuid) {
Ok(uuid) => uuid,
Err(e) => {
fx_err_and_bail!(
&with_line!(tag),
format_err!("Unable to convert to Uuid: {:?}", e)
);
}
};
let mut fidl_uuid = fidl_fuchsia_bluetooth::Uuid::from(uuid);
match &self.inner.read().active_proxy {
Some(proxy) => match proxy.read_by_type(&mut fidl_uuid).await {
Ok(value) => {
let responses = value.unwrap();
let mut read_by_type_response_list = Vec::new();
for response in responses {
read_by_type_response_list
.push(SerializableReadByTypeResult::new(&response));
}
Ok(read_by_type_response_list)
}
Err(e) => {
let err_msg = format!("Failed to read characteristic by type: {}", e);
fx_err_and_bail!(&with_line!(tag), err_msg)
}
},
None => fx_err_and_bail!(&with_line!(tag), "Central proxy not available."),
}
}
pub async fn gattc_read_long_char_by_id(
&self,
id: u64,
offset: u16,
max_bytes: u16,
) -> Result<Vec<u8>, Error> {
let tag = "GattClientFacade::gattc_read_long_char_by_id";
let read_long_characteristic = match &self.inner.read().active_proxy {
Some(proxy) => proxy.read_long_characteristic(id, offset, max_bytes),
None => fx_err_and_bail!(&with_line!(tag), "Central proxy not available."),
};
let (status, value) =
read_long_characteristic.await.map_err(|_| Sl4fError::new("Failed to send message"))?;
match status.error {
Some(e) => {
let err_msg =
format!("Failed to read long characteristic: {}", Sl4fError::from(*e));
fx_err_and_bail!(&with_line!(tag), err_msg)
}
None => Ok(value),
}
}
pub async fn gattc_read_desc_by_id(&self, id: u64) -> Result<Vec<u8>, Error> {
let tag = "GattClientFacade::gattc_read_desc_by_id";
let read_descriptor = match &self.inner.read().active_proxy {
Some(proxy) => proxy.read_descriptor(id),
None => fx_err_and_bail!(&with_line!(tag), "Central proxy not available."),
};
let (status, value) =
read_descriptor.await.map_err(|_| Sl4fError::new("Failed to send message"))?;
match status.error {
Some(e) => {
let err_msg = format!("Failed to read descriptor: {}", Sl4fError::from(*e));
fx_err_and_bail!(&with_line!(tag), err_msg)
}
None => Ok(value),
}
}
pub async fn gattc_read_long_desc_by_id(
&self,
id: u64,
offset: u16,
max_bytes: u16,
) -> Result<Vec<u8>, Error> {
let tag = "GattClientFacade::gattc_read_long_desc_by_id";
let read_long_descriptor = match &self.inner.read().active_proxy {
Some(proxy) => proxy.read_long_descriptor(id, offset, max_bytes),
None => fx_err_and_bail!(&with_line!(tag), "Central proxy not available."),
};
let (status, value) =
read_long_descriptor.await.map_err(|_| Sl4fError::new("Failed to send message"))?;
match status.error {
Some(e) => {
let err_msg = format!("Failed to read long descriptor: {}", Sl4fError::from(*e));
fx_err_and_bail!(&with_line!(tag), err_msg)
}
None => Ok(value),
}
}
pub async fn gattc_write_desc_by_id(&self, id: u64, write_value: Vec<u8>) -> Result<(), Error> {
let tag = "GattClientFacade::gattc_write_desc_by_id";
let write_descriptor = match &self.inner.read().active_proxy {
Some(proxy) => proxy.write_descriptor(id, &write_value),
None => fx_err_and_bail!(&with_line!(tag), "Central proxy not available."),
};
let status =
write_descriptor.await.map_err(|_| Sl4fError::new("Failed to send message"))?;
match status.error {
Some(e) => {
let err_msg = format!("Failed to write to descriptor: {}", Sl4fError::from(*e));
fx_err_and_bail!(&with_line!(tag), err_msg)
}
None => Ok(()),
}
}
pub async fn gattc_write_long_desc_by_id(
&self,
id: u64,
offset: u16,
write_value: Vec<u8>,
) -> Result<(), Error> {
let tag = "GattClientFacade::gattc_write_long_desc_by_id";
let write_long_descriptor = match &self.inner.read().active_proxy {
Some(proxy) => proxy.write_long_descriptor(id, offset, &write_value),
None => fx_err_and_bail!(&with_line!(tag), "Central proxy not available."),
};
let status =
write_long_descriptor.await.map_err(|_| Sl4fError::new("Failed to send message"))?;
match status.error {
Some(e) => {
let err_msg = format!("Failed to write long descriptor: {}", Sl4fError::from(*e));
fx_err_and_bail!(&with_line!(tag), err_msg)
}
None => Ok(()),
}
}
pub async fn gattc_toggle_notify_characteristic(
&self,
id: u64,
value: bool,
) -> Result<(), Error> {
let tag = "GattClientFacade::gattc_toggle_notify_characteristic";
let notify_characteristic = match &self.inner.read().active_proxy {
Some(proxy) => proxy.notify_characteristic(id, value),
None => fx_err_and_bail!(&with_line!(tag), "Central proxy not available."),
};
let status =
notify_characteristic.await.map_err(|_| Sl4fError::new("Failed to send message"))?;
match status.error {
Some(e) => {
let err_msg = format!("Failed to enable notifications: {}", Sl4fError::from(*e));
fx_err_and_bail!(&with_line!(tag), err_msg)
}
None => {}
};
Ok(())
}
pub async fn list_services(&self, id: String) -> Result<Vec<ServiceInfo>, Error> {
let tag = "GattClientFacade::list_services";
let client_proxy = self.get_client_from_peripherals(id);
match client_proxy {
Some(c) => {
let (status, services) = c.list_services(None).await?;
match status.error {
None => {
fx_log_info!(tag: &with_line!(tag), "Found services: {:?}", services);
Ok(services)
}
Some(e) => {
let err_msg = format!(
"Error found while listing services: {:?}",
Sl4fError::from(*e)
);
fx_err_and_bail!(&with_line!(tag), err_msg)
}
}
}
None => bail!("No client exists with provided device id"),
}
}
// Given a device id, return its ClientProxy, if existing, otherwise None
pub fn get_client_from_peripherals(&self, id: String) -> Option<ClientProxy> {
self.inner.read().peripheral_ids.get(&id).map(|c| c.clone())
}
// Given a device id, insert it into the map. If it exists, don't overwrite
// TODO(aniramakri): Is this right behavior? If the device id already exists, don't
// overwrite ClientProxy?
pub fn update_peripheral_id(&self, id: &String, client: ClientProxy) {
let tag = "GattClientFacade::update_peripheral_id";
if self.inner.read().peripheral_ids.contains_key(id) {
fx_log_warn!(tag: &with_line!(tag), "Overwriting existing id: {}", id);
self.inner.write().peripheral_ids.insert(id.clone(), client);
} else {
fx_log_info!(tag: &with_line!(tag), "Added {:?} to peripheral ids", id);
self.inner.write().peripheral_ids.insert(id.clone(), client);
}
fx_log_info!(
tag: &with_line!(tag),
"Peripheral ids: {:?}",
self.inner.read().peripheral_ids
);
}
pub fn remove_peripheral_id(&self, id: &String) {
let tag = "GattClientFacade::remove_peripheral_id";
self.inner.write().peripheral_ids.remove(id);
fx_log_info!(
tag: &with_line!(tag),
"After removing peripheral id: {:?}",
self.inner.read().peripheral_ids
);
}
// Update the central proxy if none exists, otherwise raise error
// If no proxy exists, set up central server to listen for events. This central listener will
// wake up any wakers who may be interested in RemoteDevices discovered
pub fn set_central_proxy(inner: Arc<RwLock<InnerGattClientFacade>>) {
let tag = "GattClientFacade::set_central_proxy";
let mut central_modified = false;
let new_central = match inner.read().central.clone() {
Some(c) => {
fx_log_warn!(tag: &with_line!(tag), "Current central: {:?}.", c);
central_modified = true;
Some(c)
}
None => {
let central_svc: CentralProxy = app::client::connect_to_service::<CentralMarker>()
.context("Failed to connect to BLE Central service.")
.unwrap();
Some(central_svc)
}
};
// Update the central with the (potentially) newly created proxy
inner.write().central = new_central;
// Only spawn if a central hadn't been created
if !central_modified {
fasync::Task::spawn(GattClientFacade::listen_central_events(inner.clone())).detach()
}
}
// Update the devices dictionary with a discovered RemoteDevice
pub fn update_devices(
inner: &Arc<RwLock<InnerGattClientFacade>>,
id: String,
device: RemoteDevice,
) {
let tag = "GattClientFacade::update_devices";
if inner.read().devices.contains_key(&id) {
fx_log_warn!(tag: &with_line!(tag), "Already discovered: {:?}", id);
} else {
inner.write().devices.insert(id, device);
}
}
pub fn listen_central_events(
inner: Arc<RwLock<InnerGattClientFacade>>,
) -> impl Future<Output = ()> {
let tag = "GattClientFacade::listen_central_events";
let evt_stream = match inner.read().central.clone() {
Some(c) => c.take_event_stream(),
None => panic!("No central created!"),
};
evt_stream
.map_ok(move |evt| {
match evt {
CentralEvent::OnScanStateChanged { scanning } => {
fx_log_info!(tag: &with_line!(tag), "Scan state changed: {:?}", scanning);
}
CentralEvent::OnDeviceDiscovered { device } => {
let id = device.identifier.clone();
let name = device.advertising_data.as_ref().map(|adv| &adv.name);
// Update the device discovered list
fx_log_info!(
tag: &with_line!(tag),
"Device discovered: id: {:?}, name: {:?}",
id,
name
);
GattClientFacade::update_devices(&inner, id, device);
// In the event that we need to short-circuit the stream, wake up all
// wakers in the host_requests Slab
for waker in &inner.read().host_requests {
waker.1.wake_by_ref();
}
}
CentralEvent::OnPeripheralDisconnected { identifier } => {
fx_log_info!(tag: &with_line!(tag), "Peer disconnected: {:?}", identifier);
}
}
})
.try_collect::<()>()
.unwrap_or_else(|e| {
fx_log_err!(
tag: &with_line!("GattClientFacade::listen_central_events"),
"Failed to subscribe to BLE Central events: {:?}",
e
)
})
}
pub async fn connect_peripheral(&self, id: String) -> Result<(), Error> {
let tag = "GattClientFacade::connect_peripheral";
// Set the central proxy if necessary
GattClientFacade::set_central_proxy(self.inner.clone());
// TODO(fxbug.dev/875): Move to private method?
// Create server endpoints
let (proxy, server_end) = match fidl::endpoints::create_proxy() {
Err(e) => {
let err_msg = format!("Failed to create proxy endpoint: {:?}", e);
fx_err_and_bail!(&with_line!(tag), err_msg)
}
Ok(x) => x,
};
let mut identifier = id.clone();
self.update_peripheral_id(&identifier, proxy);
match &self.inner.read().central {
Some(c) => {
let conn_opts = ConnectionOptions {
bondable_mode: Some(true),
service_filter: None,
..ConnectionOptions::EMPTY
};
let status = c.connect_peripheral(&mut identifier, conn_opts, server_end).await?;
match status.error {
Some(e) => {
let err_msg =
format!("Failed to connect to peripheral: {:?}", Sl4fError::from(*e));
fx_err_and_bail!(&with_line!(tag), err_msg)
}
None => {}
}
}
None => fx_err_and_bail!(&with_line!(tag), "No central proxy created."),
};
Ok(())
}
pub async fn disconnect_peripheral(&self, id: String) -> Result<(), Error> {
let tag = "GattClientFacade::disconnect_peripheral";
match &self.inner.read().central {
Some(c) => {
let status = c.disconnect_peripheral(&id).await?;
match status.error {
None => {}
Some(e) => {
fx_log_err!(tag: &with_line!(tag), "Failed to disconnect: {:?}", e);
bail!("Failed to disconnect: {:?}", e)
}
}
}
None => fx_err_and_bail!(&with_line!(tag), "Failed to disconnect from perpheral."),
};
// Remove current id from map of peripheral_ids
self.remove_peripheral_id(&id);
Ok(())
}
// Return the central proxy
pub fn get_central_proxy(&self) -> Option<CentralProxy> {
self.inner.read().central.clone()
}
pub fn get_periph_ids(&self) -> HashMap<String, ClientProxy> {
self.inner.read().peripheral_ids.clone()
}
// Given the devices accrued from scan, returns list of (id, name) devices
// TODO(fxbug.dev/869): Return list of RemoteDevices (unsupported right now
// because Clone() not implemented for RemoteDevice)
pub fn get_devices(&self) -> Vec<BleScanResponse> {
const EMPTY_DEVICE: &str = "";
let mut devices = Vec::new();
for val in self.inner.read().devices.keys() {
let name = match &self.inner.read().devices[val].advertising_data {
Some(adv) => adv.name.clone().unwrap_or(EMPTY_DEVICE.to_string()),
None => EMPTY_DEVICE.to_string(),
};
let connectable = self.inner.read().devices[val].connectable;
devices.push(BleScanResponse::new(val.clone(), name, connectable));
}
devices
}
pub fn cleanup_central_proxy(&self) {
self.inner.write().central = None
}
pub fn cleanup_devices(&self) {
self.inner.write().devices.clear()
}
pub fn cleanup_central(&self) {
self.cleanup_central_proxy();
self.cleanup_devices();
}
pub fn cleanup_peripheral_ids(&self) {
self.inner.write().peripheral_ids.clear()
}
pub fn print(&self) {
let tag = "GattClientFacade::print";
fx_log_info!(
tag: &with_line!(tag),
"BluetoothFacade: Central: {:?}, Devices: {:?}, Periph_ids: {:?}",
self.get_central_proxy(),
self.get_devices(),
self.get_periph_ids(),
);
}
// Close both central proxies
pub fn cleanup(&self) {
self.cleanup_central();
self.cleanup_peripheral_ids();
}
}