blob: b7a97d701aee4af9fe51abfc27f199d007a41f26 [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.
// TODO(armansito): Remove this once a server channel can be killed using a Controller
#![allow(unreachable_code)]
use bt::error::Error as BTError;
use common::gatt::start_gatt_loop;
use failure::Error;
use fidl::endpoints2;
use fidl_ble::{CentralEvent, CentralProxy, RemoteDevice};
use futures::future;
use futures::future::Either::{Left, Right};
use futures::prelude::*;
use parking_lot::RwLock;
use std::fmt;
use std::process::exit;
use std::string::String;
use std::sync::Arc;
type CentralStatePtr = Arc<RwLock<CentralState>>;
pub struct CentralState {
// If true, stop scanning and close the delegate handle after the first scan result.
pub scan_once: bool,
// If true, attempt to connect to the first scan result.
pub connect: bool,
// The proxy that we use to perform LE central requests.
svc: CentralProxy,
}
impl CentralState {
pub fn new(proxy: CentralProxy) -> CentralStatePtr {
Arc::new(RwLock::new(CentralState {
scan_once: false,
connect: false,
svc: proxy,
}))
}
pub fn get_svc(&self) -> &CentralProxy {
&self.svc
}
}
pub fn listen_central_events(
state: CentralStatePtr,
) -> impl Future<Item = (), Error = Never> + Send {
let evt_stream = state.read().get_svc().take_event_stream();
evt_stream
.for_each(move |evt| {
match evt {
CentralEvent::OnScanStateChanged { scanning } => {
eprintln!(" scan state changed: {}", scanning);
Left(future::ok(()))
}
CentralEvent::OnDeviceDiscovered { device } => {
let id = device.identifier.clone();
let connectable = device.connectable;
eprintln!(" {}", RemoteDeviceWrapper(device));
let central = state.read();
if !central.scan_once && !central.connect {
return Left(future::ok(()));
}
// Stop scanning.
if let Err(e) = central.svc.stop_scan() {
eprintln!("request to stop scan failed: {}", e);
// TODO(armansito): kill the channel here instead
exit(0);
Left(future::ok(()))
} else if central.connect && connectable {
Right(connect_peripheral(state.clone(), id).recover(|_| {
// TODO(armansito): kill the channel here instead
exit(0);
()
}))
} else {
// TODO(armansito): kill the channel here instead
exit(0);
Left(future::ok(()))
}
}
CentralEvent::OnPeripheralDisconnected { identifier } => {
eprintln!(" peer disconnected: {}", identifier);
// TODO(armansito): Close the channel here instead
exit(0);
Left(future::ok(()))
}
}
})
.map(|_| ())
.recover(|e| eprintln!("failed to subscribe to BLE Central events: {:?}", e))
}
// Attempts to connect to the peripheral with the given |id| and begins the
// GATT REPL if this succeeds.
fn connect_peripheral(
state: CentralStatePtr, mut id: String,
) -> impl Future<Item = (), Error = Error> {
let (proxy, server) = match endpoints2::create_endpoints() {
Err(_) => {
return Left(future::err(
BTError::new("Failed to create Client pair").into(),
));
}
Ok(res) => res,
};
Right(
state
.read()
.svc
.connect_peripheral(&mut id, server)
.map_err(|e| {
BTError::new(&format!("failed to initiate connect request: {}", e)).into()
})
.and_then(move |status| match status.error {
Some(e) => {
println!(
" failed to connect to peripheral: {}",
match e.description {
None => "unknown error",
Some(ref msg) => &msg,
}
);
Err(BTError::from(*e).into())
}
None => {
println!(" device connected: {}", id);
Ok(proxy)
}
})
.and_then(|proxy| start_gatt_loop(proxy)),
)
}
struct RemoteDeviceWrapper(RemoteDevice);
impl fmt::Display for RemoteDeviceWrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let connectable = if self.0.connectable {
"conn"
} else {
"non-conn"
};
write!(f, "[device({}), ", connectable)?;
if let Some(ref rssi) = self.0.rssi {
write!(f, "rssi: {}, ", rssi.value)?;
}
if let Some(ref ad) = self.0.advertising_data {
if let Some(ref name) = ad.name {
write!(f, "{}, ", name)?;
}
}
write!(f, "id: {}]", self.0.identifier)
}
}