ckpt

Change-Id: If4b07dfb6819276d9d0e506ecfeaed8eb585897a
diff --git a/bin/bluetooth/bt-mgr/src/bond_defs.rs b/bin/bluetooth/bt-mgr/src/bond_defs.rs
index da401ce..86ced23 100644
--- a/bin/bluetooth/bt-mgr/src/bond_defs.rs
+++ b/bin/bluetooth/bt-mgr/src/bond_defs.rs
@@ -101,12 +101,47 @@
 #[derive(Serialize, Deserialize)]
 pub struct BondMap(HashMap<String, VecBondingData>);
 
-#[derive(Serialize, Deserialize)]
-struct VecBondingData {
-    #[serde(with = "VecBondData")]
-    inner: Vec<BondingData>,
+impl BondMap {
+    pub fn inner(&self) -> &HashMap<String, VecBondingData> {
+        &self.0
+    }
+
+    pub fn inner_mut(&mut self) -> &mut HashMap<String, VecBondingData> {
+        &mut self.0
+    }
 }
 
+#[derive(Serialize, Deserialize)]
+pub struct VecBondingData {
+    #[serde(with = "VecBondData")]
+    pub inner: Vec<BondingData>,
+}
+
+//impl VecBondingData {
+//    pub fn inner(&self) -> VecBondingData {
+//        &self.inner
+//    }
+//}
+
+// TODO
+struct VecBondIter {}
+//
+//impl Iterator for VecBondIter {
+//    type Item = BondingData;
+//
+//    fn next(&mut self) -> Option<Self::Item> {
+//        self.inner.iter().next()
+//    }
+//}
+//impl IntoIterator for VecBondingData {
+//    type Item = BondingData;
+//    //type IntoIterator = VecBondIter;
+//
+//    //fn into_iter(self) -> Self::IntoIterator {
+//
+//    //}
+//}
+
 mod VecBondData {
     use super::{BondingData, BondingDataDef};
     use serde::Serializer;
diff --git a/bin/bluetooth/bt-mgr/src/bond_store.rs b/bin/bluetooth/bt-mgr/src/bond_store.rs
new file mode 100644
index 0000000..5210e17
--- /dev/null
+++ b/bin/bluetooth/bt-mgr/src/bond_store.rs
@@ -0,0 +1,109 @@
+// 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 app::client::App;
+use failure::{Error, ResultExt};
+use futures::future::ok as fok;
+use futures::{Future, FutureExt, StreamExt};
+use std::fs::File;
+use std::fs::OpenOptions;
+use std::io::{Read, Write};
+use std::path::PathBuf;
+use std::sync::{Arc, Mutex};
+use std::{thread, time};
+use toml;
+
+use app::client::Launcher;
+use fidl_bluetooth_bonder::{BonderEvent, BonderEventStream, BonderMarker, BondingData};
+use fidl_bluetooth_control::ControlMarker;
+use fidl_bluetooth_gatt::Server_Marker;
+use fidl_bluetooth_low_energy::{CentralMarker, PeripheralMarker};
+
+use bond_defs::BondMap;
+
+static BT_MGR_DIR: &'static str = "data/bt-mgr";
+
+pub struct BondStore {
+    bonds: BondMap,
+    bond_store: File,
+}
+
+impl BondStore {
+    pub fn load_store() -> Result<Self, Error> {
+        let store_path: PathBuf = [BT_MGR_DIR, "/bonds.toml"].iter().collect();
+
+        let mut bond_store = OpenOptions::new()
+            .read(true)
+            .write(true)
+            .create(true)
+            .open(store_path)?;
+
+        let mut contents = String::new();
+        bond_store
+            .read_to_string(&mut contents)
+            .expect("The bond storage file is corrupted");
+        let bonds: BondMap = toml::from_str(contents.as_str()).unwrap();
+
+        Ok(BondStore { bonds, bond_store })
+    }
+
+    pub fn add(&mut self, local_id: String, bond_data: BondingData) {
+        self.bonds.inner_mut().entry(local_id)
+            //            TODO look up syntax, push if not there
+//            .or_insert(VecBondingData{inner: vec![]})
+            .and_modify(move |entry| {
+                entry.inner.push(bond_data);
+            });
+        let _ = self.save_state();
+    }
+
+    pub fn bonds(&self) -> &BondMap {
+        &self.bonds
+    }
+
+    pub fn save_state(&mut self) -> Result<(), Error> {
+        let toml = toml::to_string_pretty(&self.bonds)?;
+        self.bond_store.write_all(toml.as_bytes())?;
+        self.bond_store.sync_data()?;
+        Ok(())
+    }
+}
+
+pub fn bond(bond_store: Arc<Mutex<BondStore>>, bt_gap: App) -> Result<(), Error> {
+    let bond_svc = bt_gap.connect_to_service(BonderMarker)?;
+
+    let bond_store = bond_store.lock().unwrap();
+    for (bond_key, bond_data) in bond_store.bonds.inner().iter() {
+        // TODO make the iter work
+        //        bond_svc.add_bonded_devices(bond_key, bond_data.into_iter());
+    }
+
+    Ok(())
+}
+
+pub fn watch_bonds(
+    bond_store: Arc<Mutex<BondStore>>,
+) -> impl Future<Item = BonderEventStream, Error = Error> {
+    let launcher = Launcher::new()
+        .context("Failed to open launcher service")
+        .unwrap();
+    let app = launcher
+        .launch(String::from("bt-gap"), None)
+        .context("Failed to launch bt-gap (bluetooth) service")
+        .unwrap();
+    thread::sleep(time::Duration::from_millis(2000));
+    let app = app.connect_to_service(BonderMarker);
+    let stream = app.unwrap().take_event_stream();
+    stream
+        .for_each(move |evt| {
+            match evt {
+                BonderEvent::OnNewBondingData { local_id, data } => {
+                    let mut bond_store = bond_store.lock().unwrap();
+                    bond_store.add(local_id, data)
+                }
+            }
+            fok(())
+        })
+        .err_into()
+}
diff --git a/bin/bluetooth/bt-mgr/src/main.rs b/bin/bluetooth/bt-mgr/src/main.rs
index 802dea3..12fc077 100644
--- a/bin/bluetooth/bt-mgr/src/main.rs
+++ b/bin/bluetooth/bt-mgr/src/main.rs
@@ -10,9 +10,13 @@
 extern crate fidl;
 extern crate fidl_bluetooth_bonder;
 extern crate fidl_bluetooth_control;
+extern crate fidl_bluetooth_gatt;
+extern crate fidl_bluetooth_low_energy;
 extern crate fuchsia_app as app;
 extern crate fuchsia_async as async;
 extern crate fuchsia_zircon as zx;
+#[macro_use]
+extern crate fuchsia_bluetooth as bt;
 extern crate futures;
 extern crate parking_lot;
 #[macro_use]
@@ -22,73 +26,47 @@
 extern crate serde;
 extern crate toml;
 
-use std::io::Read;
-use std::path::PathBuf;
-
 use fidl::endpoints2::ServiceMarker;
 use std::{thread, time};
 
 use app::client::App;
 use std::sync::{Arc, Mutex};
 
-use std::fs::File;
-use std::fs::OpenOptions;
-
-use std::io::Write;
+use futures::future::ok as fok;
+use futures::{Future, FutureExt, StreamExt};
 
 use app::client::Launcher;
 use app::server::ServicesServer;
 use failure::{Error, ResultExt};
-use fidl_bluetooth_bonder::BonderMarker;
 use fidl_bluetooth_control::ControlMarker;
+use fidl_bluetooth_gatt::Server_Marker;
+use fidl_bluetooth_low_energy::{CentralMarker, PeripheralMarker};
 
 mod bond_defs;
+mod bond_store;
 mod logger;
 
 use bond_defs::*;
+use bond_store::BondStore;
 
 const MAX_LOG_LEVEL: log::LevelFilter = log::LevelFilter::Info;
 static LOGGER: logger::Logger = logger::Logger;
 
-static BT_MGR_DIR: &'static str = "data/bt-mgr";
-
-struct BondStore {
-    bonds: BondMap,
-    bond_store: File,
-}
-
-impl BondStore {
-    fn load_store() -> Result<Self, Error> {
-        let store_path: PathBuf = [BT_MGR_DIR, "/bonds.toml"].iter().collect();
-
-        let mut bond_store = OpenOptions::new()
-            .read(true)
-            .write(true)
-            .create(true)
-            .open(store_path)?;
-
-        let mut contents = String::new();
-        bond_store
-            .read_to_string(&mut contents)
-            .expect("The bond storage file is corrupted");
-        let bonds: BondMap = toml::from_str(contents.as_str()).unwrap();
-
-        Ok(BondStore { bonds, bond_store })
-    }
-
-    fn save_state(&mut self) -> Result<(), Error> {
-        let toml = toml::to_string_pretty(&self.bonds)?;
-        self.bond_store.write_all(toml.as_bytes())?;
-        self.bond_store.sync_data()?;
-        Ok(())
-    }
-}
-
-fn bond(_bond_store: Arc<Mutex<BondStore>>, bt_gap: App) -> Result<(), Error> {
-    let _bond_svc = bt_gap.connect_to_service(BonderMarker)?;
-
-    //bond_svc.add_bonded_devices();
-    Ok(())
+fn launch_bt_gap<S: ServiceMarker>(
+    marker: S, bond_store: Arc<Mutex<BondStore>>, chan: async::Channel,
+) {
+    let launcher = Launcher::new()
+        .context("Failed to open launcher service")
+        .unwrap();
+    let app = launcher
+        .launch(String::from("bt-gap"), None)
+        .context("Failed to launch bt-gap (bluetooth) service")
+        .unwrap();
+    // TODO check if we need to launch a service?
+    thread::sleep(time::Duration::from_millis(2000));
+    let _ = app.pass_to_service(marker, chan.into());
+    // TODO cap the number of times this is done?
+    let _ = bond_store::bond(bond_store.clone(), app);
 }
 
 fn main() -> Result<(), Error> {
@@ -99,25 +77,33 @@
     let mut executor = async::Executor::new().context("Error creating executor")?;
 
     let bond_store = Arc::new(Mutex::new(BondStore::load_store()?));
+    make_clones!(bond_store => control_bs, central_bs, peripheral_bs, server_bs, bond_bs);
+
+    let bond_watcher = bond_store::watch_bonds(bond_bs);
+
     let server = ServicesServer::new()
         .add_service((ControlMarker::NAME, move |chan: async::Channel| {
             info!("Passing Control Handle to bt-gap");
-            let launcher = Launcher::new()
-                .context("Failed to open launcher service")
-                .unwrap();
-            let app = launcher
-                .launch(String::from("bt-gap"), None)
-                .context("Failed to launch bt-gap (bluetooth) service")
-                .unwrap();
-            thread::sleep(time::Duration::from_millis(2000));
-            let _ = app.pass_to_service(ControlMarker, chan.into());
-            let _ = bond(bond_store.clone(), app);
+            launch_bt_gap(ControlMarker, control_bs.clone(), chan);
+        }))
+        .add_service((CentralMarker::NAME, move |chan: async::Channel| {
+            info!("Passing LE Central Handle to bt-gap");
+            launch_bt_gap(CentralMarker, central_bs.clone(), chan);
+        }))
+        .add_service((PeripheralMarker::NAME, move |chan: async::Channel| {
+            info!("Passing Peripheral Handle to bt-gap");
+            launch_bt_gap(PeripheralMarker, peripheral_bs.clone(), chan);
+        }))
+        .add_service((Server_Marker::NAME, move |chan: async::Channel| {
+            info!("Passing GATT Handle to bt-gap");
+            launch_bt_gap(Server_Marker, server_bs.clone(), chan);
         }))
         .start()
         .map_err(|e| e.context("error starting service server"))?;
 
     executor
-        .run_singlethreaded(server)
+        .run_singlethreaded(server.join(bond_watcher))
         .context("bt-mgr failed to execute future")
+        .map(|_| ())
         .map_err(|e| e.into())
 }