[bt] bt-emulator library abstraction (Part 1)

- The FakeHciDevice type from the fuchsia-bluetooth crate now connects
  to a bt-emulator device and provides a proxy to its HciEmulator FIDL
  implementation.
- The bt-fake-hci driver binding and test device management logic has
  been moved to fake_hci.rs. Old copies in hci.rs will be removed once
  all users go through FakeHciDevice.
- Added a unit test for device creation.
- Fixed a number of bugs in the bt-host driver's shut-down logic that
  were surfaced by the new unit test:
  * Fixed deadlocks triggered by DDK "unbind" received while a
    gap::Adapter is initializing.
  * Fixed use-after-free when the clean up functions are called multiple
    times.
   * Added a unit test for calling gap::Adapter::ShutDown() before
     gap::Adapter::Initialize() has returned, to enforce the latter's
     "abort" contract.

Bug: BT-229
Test: 1. bt-host-unittests --gtest_filter=GAP_AdapterTest.*
      2. bt-integration-tests
      3. bluetooth-crate-unittests
Change-Id: I9d58e2542792151897ae76b720df1119ed3dc6b3
diff --git a/src/connectivity/bluetooth/core/bt-host/gap/adapter.cc b/src/connectivity/bluetooth/core/bt-host/gap/adapter.cc
index cfcfe72..c83c878 100644
--- a/src/connectivity/bluetooth/core/bt-host/gap/adapter.cc
+++ b/src/connectivity/bluetooth/core/bt-host/gap/adapter.cc
@@ -651,6 +651,11 @@
 void Adapter::CleanUp() {
   ZX_DEBUG_ASSERT(thread_checker_.IsCreationThreadCurrent());
 
+  if (init_state_ == State::kNotInitialized) {
+    bt_log(TRACE, "gap", "clean up: not initialized");
+    return;
+  }
+
   init_state_ = State::kNotInitialized;
   state_ = AdapterState();
   transport_closed_cb_ = nullptr;
@@ -669,7 +674,10 @@
   le_address_manager_ = nullptr;
 
   // Clean up the data domain as it gets initialized by the Adapter.
-  data_domain_->ShutDown();
+  if (data_domain_) {
+    data_domain_->ShutDown();
+    data_domain_ = nullptr;
+  }
 
   // TODO(armansito): hci::Transport::ShutDown() should send a shutdown message
   // to the bt-hci device, which would be responsible for sending HCI_Reset upon
diff --git a/src/connectivity/bluetooth/core/bt-host/gap/adapter_unittest.cc b/src/connectivity/bluetooth/core/bt-host/gap/adapter_unittest.cc
index 9b64ad7..6820338 100644
--- a/src/connectivity/bluetooth/core/bt-host/gap/adapter_unittest.cc
+++ b/src/connectivity/bluetooth/core/bt-host/gap/adapter_unittest.cc
@@ -213,6 +213,35 @@
   EXPECT_TRUE(transport_closed_called());
 }
 
+// TODO(BT-919): Add a unit test for Adapter::ShutDown() and update
+// ShutDownDuringInitialize() with the same expectations.
+
+TEST_F(GAP_AdapterTest, ShutDownDuringInitialize) {
+  bool success;
+  int init_cb_count = 0;
+  auto init_cb = [&](bool result) {
+    success = result;
+    init_cb_count++;
+  };
+
+  FakeController::Settings settings;
+  settings.ApplyLEOnlyDefaults();
+  test_device()->set_settings(settings);
+
+  adapter()->Initialize(std::move(init_cb), [] {});
+  EXPECT_TRUE(adapter()->IsInitializing());
+  adapter()->ShutDown();
+
+  EXPECT_EQ(1, init_cb_count);
+  EXPECT_FALSE(success);
+  EXPECT_FALSE(adapter()->IsInitializing());
+  EXPECT_FALSE(adapter()->IsInitialized());
+
+  // Further calls to ShutDown() should have no effect.
+  adapter()->ShutDown();
+  RunLoopUntilIdle();
+}
+
 TEST_F(GAP_AdapterTest, SetNameError) {
   std::string kNewName = "something";
   bool success;
@@ -308,8 +337,8 @@
   // Mark the peer as bonded and advance the scan period.
   sm::PairingData pdata;
   pdata.ltk = sm::LTK();
-  adapter()->peer_cache()->AddBondedPeer(BondingData{kPeerId, kTestAddr, {},
-                                                     pdata, {}});
+  adapter()->peer_cache()->AddBondedPeer(
+      BondingData{kPeerId, kTestAddr, {}, pdata, {}});
   EXPECT_EQ(1u, adapter()->peer_cache()->count());
   RunLoopFor(kTestScanPeriod);
 
diff --git a/src/connectivity/bluetooth/core/bt-host/host.cc b/src/connectivity/bluetooth/core/bt-host/host.cc
index 399e2e7..587d4a5 100644
--- a/src/connectivity/bluetooth/core/bt-host/host.cc
+++ b/src/connectivity/bluetooth/core/bt-host/host.cc
@@ -4,13 +4,12 @@
 
 #include "host.h"
 
+#include "fidl/host_server.h"
+#include "gatt_host.h"
 #include "src/connectivity/bluetooth/core/bt-host/common/log.h"
 #include "src/connectivity/bluetooth/core/bt-host/hci/device_wrapper.h"
 #include "src/connectivity/bluetooth/core/bt-host/hci/transport.h"
 
-#include "fidl/host_server.h"
-#include "gatt_host.h"
-
 using namespace bt;
 
 namespace bthost {
@@ -53,7 +52,8 @@
   // L2CAP services.
   auto gap_init_callback = [gatt_host = gatt_host_,
                             callback = std::move(callback)](bool success) {
-    bt_log(TRACE, "bt-host", "GAP initialized");
+    bt_log(TRACE, "bt-host", "GAP init complete (%s)",
+           (success ? "success" : "failure"));
 
     if (success) {
       gatt_host->Initialize();
@@ -72,14 +72,21 @@
   ZX_DEBUG_ASSERT(thread_checker_.IsCreationThreadCurrent());
   bt_log(TRACE, "bt-host", "shutting down");
 
+  if (!gap_) {
+    bt_log(TRACE, "bt-host", "already shut down");
+    return;
+  }
+
   // Closes all FIDL channels owned by |host_server_|.
   host_server_ = nullptr;
 
   // This shuts down the GATT profile and all of its clients.
   gatt_host_->ShutDown();
+  gap_->ShutDown();
+  data_domain_->ShutDown();
 
   // Make sure that |gap_| gets shut down and destroyed on its creation thread
-  // as it is not thread-safe. This shuts down the data domain.
+  // as it is not thread-safe.
   gap_ = nullptr;
   data_domain_ = nullptr;
   gatt_host_ = nullptr;
diff --git a/src/connectivity/bluetooth/core/bt-host/host_device.cc b/src/connectivity/bluetooth/core/bt-host/host_device.cc
index 28de9e9..86c895e 100644
--- a/src/connectivity/bluetooth/core/bt-host/host_device.cc
+++ b/src/connectivity/bluetooth/core/bt-host/host_device.cc
@@ -90,8 +90,10 @@
         std::lock_guard<std::mutex> lock(mtx_);
 
         // Abort if CleanUp has been called.
-        if (!host_)
+        if (!host_) {
+          bt_log(SPEW, "bt-host", "host already removed; nothing to do");
           return;
+        }
 
         if (success) {
           bt_log(TRACE, "bt-host", "adapter initialized; make device visible");
@@ -101,12 +103,12 @@
           return;
         }
 
-        bt_log(ERROR, "bt-host", "failed to initialize adapter");
+        bt_log(ERROR, "bt-host", "failed to initialize adapter; cleaning up");
+
+        host->ShutDown();
+        loop_.Shutdown();
         CleanUp();
       }
-
-      host->ShutDown();
-      loop_.Shutdown();
     });
   });
 
@@ -116,23 +118,32 @@
 void HostDevice::Unbind() {
   bt_log(TRACE, "bt-host", "unbind");
 
-  std::lock_guard<std::mutex> lock(mtx_);
+  {
+    std::lock_guard<std::mutex> lock(mtx_);
 
-  if (!host_)
-    return;
+    if (!host_)
+      return;
 
-  // Do this immediately to stop receiving new service callbacks.
-  host_->gatt_host()->SetRemoteServiceWatcher({});
+    // Do this immediately to stop receiving new service callbacks.
+    host_->gatt_host()->SetRemoteServiceWatcher({});
 
-  async::PostTask(loop_.dispatcher(), [this, host = host_] {
-    host->ShutDown();
-    loop_.Quit();
-  });
+    // Tear down the bt-host device and all of its GATT children. Make a copy of
+    // |host_| first since CleanUp() clears it.
+    auto host = host_;
+    CleanUp();
 
-  // Make sure that the ShutDown task runs before this returns.
+    async::PostTask(loop_.dispatcher(), [this, host] {
+      host->ShutDown();
+      loop_.Quit();
+    });
+
+    // Don't hold lock waiting on the loop to terminate.
+  }
+
+  // Make sure that the ShutDown task runs before this returns. We re
   loop_.JoinThreads();
 
-  CleanUp();
+  bt_log(TRACE, "bt-host", "GAP has been shut down");
 }
 
 void HostDevice::Release() {
@@ -178,13 +189,16 @@
 }
 
 void HostDevice::CleanUp() {
+  bt_log(TRACE, "bt-host", "clean up");
   host_ = nullptr;
 
   // Removing the devices explicitly instead of letting unbind handle it for us.
   gatt_devices_.clear();
-  device_remove(dev_);
 
-  dev_ = nullptr;
+  if (dev_) {
+    device_remove(dev_);
+    dev_ = nullptr;
+  }
 }
 
 }  // namespace bthost
diff --git a/src/connectivity/bluetooth/lib/fuchsia-bluetooth/BUILD.gn b/src/connectivity/bluetooth/lib/fuchsia-bluetooth/BUILD.gn
index 293483b..28cda4f 100644
--- a/src/connectivity/bluetooth/lib/fuchsia-bluetooth/BUILD.gn
+++ b/src/connectivity/bluetooth/lib/fuchsia-bluetooth/BUILD.gn
@@ -13,15 +13,20 @@
   edition = "2018"
   with_unit_tests = true
 
+  # TODO(armansito): Split out testing code into a separate library and move
+  # test-specific dependencies out.
   deps = [
     "//garnet/public/lib/fidl/rust/fidl",
     "//garnet/public/rust/fdio",
     "//garnet/public/rust/fuchsia-async",
+    "//garnet/public/rust/fuchsia-syslog",
+    "//garnet/public/rust/fuchsia-vfs/fuchsia-vfs-watcher",
     "//garnet/public/rust/fuchsia-zircon",
     "//sdk/fidl/fuchsia.bluetooth:fuchsia.bluetooth-rustc",
     "//sdk/fidl/fuchsia.bluetooth.control:fuchsia.bluetooth.control-rustc",
     "//sdk/fidl/fuchsia.bluetooth.host:fuchsia.bluetooth.host-rustc",
     "//sdk/fidl/fuchsia.bluetooth.le:fuchsia.bluetooth.le-rustc",
+    "//sdk/fidl/fuchsia.bluetooth.test:fuchsia.bluetooth.test-rustc",
     "//third_party/rust_crates:failure",
     "//third_party/rust_crates:futures-preview",
     "//third_party/rust_crates:lazy_static",
@@ -38,6 +43,11 @@
 
   deps = [
     ":fuchsia-bluetooth",
+
+    # TODO(armansito): The bt-fake-hci driver is a runtime dependency of the test utilities
+    # contained within the crate. We should separate them out into a testing library have that
+    # depend on this driver.
+    "//src/connectivity/bluetooth/hci/fake",
   ]
 
   tests = [
diff --git a/src/connectivity/bluetooth/lib/fuchsia-bluetooth/meta/bluetooth-crate-unittests.cmx b/src/connectivity/bluetooth/lib/fuchsia-bluetooth/meta/bluetooth-crate-unittests.cmx
index 4ef1cc7..901062c 100644
--- a/src/connectivity/bluetooth/lib/fuchsia-bluetooth/meta/bluetooth-crate-unittests.cmx
+++ b/src/connectivity/bluetooth/lib/fuchsia-bluetooth/meta/bluetooth-crate-unittests.cmx
@@ -1,5 +1,14 @@
 {
     "program": {
         "binary": "test/bluetooth-crate-unittests"
+    },
+    "sandbox": {
+        "dev": [
+            "class/bt-emulator",
+            "test/test"
+        ],
+        "system": [
+            "driver/bt-hci-fake.so"
+        ]
     }
 }
diff --git a/src/connectivity/bluetooth/lib/fuchsia-bluetooth/src/fake_hci.rs b/src/connectivity/bluetooth/lib/fuchsia-bluetooth/src/fake_hci.rs
deleted file mode 100644
index ef84b3c..0000000
--- a/src/connectivity/bluetooth/lib/fuchsia-bluetooth/src/fake_hci.rs
+++ /dev/null
@@ -1,29 +0,0 @@
-// 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 {failure::Error, std::fs::File};
-
-use crate::hci;
-
-/// Represents a fake bt-hci device. Closes the underlying device when it goes out of scope.
-pub struct FakeHciDevice(File);
-
-impl FakeHciDevice {
-    /// Publishes a new fake bt-hci device and constructs a FakeHciDevice with it.
-    pub fn new(name: &str) -> Result<FakeHciDevice, Error> {
-        let (dev, _) = hci::create_and_bind_device(name)?;
-        Ok(FakeHciDevice(dev))
-    }
-
-    /// Returns a reference to the underlying file.
-    pub fn file(&self) -> &File {
-        &self.0
-    }
-}
-
-impl Drop for FakeHciDevice {
-    fn drop(&mut self) {
-        hci::destroy_device(&self.0).unwrap();
-    }
-}
diff --git a/src/connectivity/bluetooth/lib/fuchsia-bluetooth/src/hci.rs b/src/connectivity/bluetooth/lib/fuchsia-bluetooth/src/hci.rs
index 402c915..e44347b 100644
--- a/src/connectivity/bluetooth/lib/fuchsia-bluetooth/src/hci.rs
+++ b/src/connectivity/bluetooth/lib/fuchsia-bluetooth/src/hci.rs
@@ -21,6 +21,9 @@
 pub const DEV_TEST: &str = CONTROL_DEVICE;
 pub const BTHCI_DRIVER_NAME: &str = "/system/driver/bt-hci-fake.so";
 
+// TODO(BT-229): Remove this function and all of its helpers from this file once the bt-fake-hci
+// tool uses hci_emulator::Emulator from this library.
+
 // Returns the name of the fake device and a File representing the device on success.
 pub fn create_and_bind_device(name: &str) -> Result<(File, String), Error> {
     if name.len() > (MAX_DEVICE_NAME_LEN as usize) {
@@ -45,7 +48,7 @@
     Ok((dev, name.to_string()))
 }
 
-pub fn create_fake_device(test_path: &str, dev_name: &str) -> Result<String, Error> {
+fn create_fake_device(test_path: &str, dev_name: &str) -> Result<String, Error> {
     let test_dev = open_rdwr(test_path)?;
     let channel = fdio::clone_channel(&test_dev)?;
     let mut interface = RootDeviceSynchronousProxy::new(channel);
@@ -55,7 +58,7 @@
     devpath.ok_or(format_err!("RootDevice.CreateDevice received no devpath?"))
 }
 
-pub fn bind_fake_device(device: &File) -> Result<(), Error> {
+fn bind_fake_device(device: &File) -> Result<(), Error> {
     let channel = fdio::clone_channel(device)?;
     let mut interface = ControllerSynchronousProxy::new(channel);
     let status = interface.bind(BTHCI_DRIVER_NAME, zx::Time::INFINITE)?;
diff --git a/src/connectivity/bluetooth/lib/fuchsia-bluetooth/src/hci_emulator.rs b/src/connectivity/bluetooth/lib/fuchsia-bluetooth/src/hci_emulator.rs
new file mode 100644
index 0000000..09c6d10
--- /dev/null
+++ b/src/connectivity/bluetooth/lib/fuchsia-bluetooth/src/hci_emulator.rs
@@ -0,0 +1,179 @@
+// 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 {
+    failure::{bail, format_err, Error},
+    fidl_fuchsia_bluetooth_test::HciEmulatorProxy,
+    fidl_fuchsia_device::ControllerSynchronousProxy,
+    fidl_fuchsia_device_test::{
+        DeviceSynchronousProxy, RootDeviceSynchronousProxy, CONTROL_DEVICE, MAX_DEVICE_NAME_LEN,
+    },
+    fidl_fuchsia_hardware_bluetooth::EmulatorProxy,
+    fuchsia_async::{self as fasync, TimeoutExt},
+    fuchsia_syslog::fx_log_err,
+    fuchsia_vfs_watcher::{WatchEvent as VfsWatchEvent, Watcher as VfsWatcher},
+    fuchsia_zircon as zx,
+    futures::TryStreamExt,
+    std::{
+        fs::{File, OpenOptions},
+        path::{Path, PathBuf},
+    },
+};
+
+const FAKE_HCI_DRIVER_PATH: &str = "/system/driver/bt-hci-fake.so";
+const EMULATOR_DEVICE_DIR: &str = "/dev/class/bt-emulator";
+
+fn watch_timeout() -> zx::Duration {
+    zx::Duration::from_seconds(10)
+}
+
+/// Represents a bt-hci device emulator. Instances of this type can be used manage the bt-hci-fake
+/// driver within the test device hierarchy. The associated driver instance gets unbound and all
+/// bt-hci and bt-emulator device instances destroyed when a Emulator goes out of scope.
+pub struct Emulator {
+    dev: TestDevice,
+    emulator: HciEmulatorProxy,
+}
+
+impl Emulator {
+    /// Publishes a new fake bt-hci device and constructs a Emulator with it.
+    pub async fn new(name: &str) -> Result<Emulator, Error> {
+        let dev = TestDevice::create(name)?;
+        let emulator = await!(dev.bind())?;
+        Ok(Emulator { dev: dev, emulator: emulator })
+    }
+
+    /// Returns a reference to the underlying file.
+    pub fn file(&self) -> &File {
+        &self.dev.0
+    }
+
+    /// Returns a reference to the fuchsia.bluetooth.test.HciEmulator protocol proxy.
+    pub fn emulator(&self) -> &HciEmulatorProxy {
+        &self.emulator
+    }
+}
+
+// Represents the test device. Destroys the underlying device when it goes out of scope.
+struct TestDevice(File);
+
+impl TestDevice {
+    // Creates a new device as a child of the root test device. This device will act as the parent
+    // of our fake HCI device. If successful, `name` will act as the final fragment of the device
+    // path, for example "/dev/test/test/{name}".
+    fn create(name: &str) -> Result<TestDevice, Error> {
+        if name.len() > (MAX_DEVICE_NAME_LEN as usize) {
+            bail!(
+                "Device name '{}' too long (must be {} or fewer chars)",
+                name,
+                MAX_DEVICE_NAME_LEN
+            );
+        }
+
+        // Connect to the test control device and obtain a channel to the RootDevice capability.
+        let control_dev = open_rdwr(CONTROL_DEVICE)?;
+        let mut root_device = RootDeviceSynchronousProxy::new(fdio::clone_channel(&control_dev)?);
+
+        // Create a device with the requested name.
+        let (status, path) = root_device.create_device(name, zx::Time::INFINITE)?;
+        zx::Status::ok(status)?;
+        let path = path.ok_or(format_err!("RootDevice.CreateDevice returned null path"))?;
+
+        // Open the device that was just created.
+        Ok(TestDevice(open_rdwr(&path)?))
+    }
+
+    // Send the test device a destroy message which will unbind the driver.
+    fn destroy(&mut self) -> Result<(), Error> {
+        let channel = fdio::clone_channel(&self.0)?;
+        let mut device = DeviceSynchronousProxy::new(channel);
+        Ok(device.destroy()?)
+    }
+
+    // Bind the bt-hci-fake driver and obtain the HciEmulator protocol channel.
+    async fn bind(&self) -> Result<HciEmulatorProxy, Error> {
+        let channel = fdio::clone_channel(&self.0)?;
+        let mut controller = ControllerSynchronousProxy::new(channel);
+        let status = controller.bind(FAKE_HCI_DRIVER_PATH, zx::Time::INFINITE)?;
+        zx::Status::ok(status)?;
+
+        // Wait until a bt-emulator device gets published under our test device.
+        let topo_path = fdio::device_get_topo_path(&self.0)?;
+        let emulator_dev = await!(watch_for_emulator_device(topo_path)
+            .on_timeout(watch_timeout().after_now(), || Err(format_err!(
+                "could not find bt-emulator device"
+            ))))?;
+
+        // Connect to the bt-emulator device.
+        let channel = fdio::clone_channel(&emulator_dev)?;
+        let emulator = EmulatorProxy::new(fasync::Channel::from_channel(channel)?);
+
+        // Open a HciEmulator protocol channel.
+        let (proxy, remote) = zx::Channel::create()?;
+        emulator.open(remote)?;
+        Ok(HciEmulatorProxy::new(fasync::Channel::from_channel(proxy)?))
+    }
+}
+
+impl Drop for TestDevice {
+    fn drop(&mut self) {
+        if let Err(e) = self.destroy() {
+            fx_log_err!("error while destroying test device: {:?}", e);
+        }
+    }
+}
+
+// Asynchronously returns the first available bt-emulator device under the given topological path.
+// The returned Future does not terminates until a bt-emulator device is found under the requested
+// topology.
+async fn watch_for_emulator_device(parent_topo_path: String) -> Result<File, Error> {
+    let dir = File::open(&EMULATOR_DEVICE_DIR)?;
+    let mut watcher = VfsWatcher::new(&dir)?;
+    while let Some(msg) = await!(watcher.try_next())? {
+        match msg.event {
+            VfsWatchEvent::EXISTING | VfsWatchEvent::ADD_FILE => {
+                let path = PathBuf::from(format!(
+                    "{}/{}",
+                    EMULATOR_DEVICE_DIR,
+                    msg.filename.to_string_lossy()
+                ));
+                let dev = open_rdwr(&path)?;
+                let topo_path = fdio::device_get_topo_path(&dev)?;
+                if topo_path.starts_with(parent_topo_path.as_str()) {
+                    return Ok(dev);
+                }
+            }
+            _ => (),
+        }
+    }
+    unreachable!();
+}
+
+fn open_rdwr<P: AsRef<Path>>(path: P) -> Result<File, Error> {
+    OpenOptions::new().read(true).write(true).open(path).map_err(|e| e.into())
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use fidl_fuchsia_bluetooth_test::{EmulatorError, EmulatorSettings};
+
+    #[fuchsia_async::run_singlethreaded(test)]
+    async fn test_publish() {
+        let fake_dev = await!(Emulator::new("publish-test-0")).unwrap();
+
+        let settings = EmulatorSettings {
+            address: None,
+            hci_config: None,
+            extended_advertising: None,
+            acl_buffer_settings: None,
+            le_acl_buffer_settings: None,
+        };
+
+        // TODO(BT-229): Test for success when publish is implemented.
+        let result = await!(fake_dev.emulator().publish(settings))
+            .expect("Failed to send Publish message to emulator device");
+        assert_eq!(Err(EmulatorError::HciAlreadyPublished), result);
+    }
+}
diff --git a/src/connectivity/bluetooth/lib/fuchsia-bluetooth/src/lib.rs b/src/connectivity/bluetooth/lib/fuchsia-bluetooth/src/lib.rs
index 286ccc0..df0f4a3 100644
--- a/src/connectivity/bluetooth/lib/fuchsia-bluetooth/src/lib.rs
+++ b/src/connectivity/bluetooth/lib/fuchsia-bluetooth/src/lib.rs
@@ -12,10 +12,10 @@
 pub mod error;
 /// Tools for writing asynchronous expectations in tests
 pub mod expectation;
-/// Fake bt-hci device API
-pub mod fake_hci;
 /// Bluetooth HCI device utilities.
 pub mod hci;
+/// Utility for interacting with the bt-hci-emulator driver
+pub mod hci_emulator;
 /// Bluetooth host API
 pub mod host;
 /// Bluetooth LowEnergy types
diff --git a/src/connectivity/bluetooth/tests/integration/src/harness/control.rs b/src/connectivity/bluetooth/tests/integration/src/harness/control.rs
index 2d60518..4156914 100644
--- a/src/connectivity/bluetooth/tests/integration/src/harness/control.rs
+++ b/src/connectivity/bluetooth/tests/integration/src/harness/control.rs
@@ -11,7 +11,7 @@
     fuchsia_bluetooth::{
         expectation::asynchronous::{ExpectableState, ExpectableStateExt, ExpectationHarness},
         expectation::Predicate,
-        fake_hci::FakeHciDevice,
+        hci_emulator::Emulator,
         util::{clone_host_info, clone_remote_device},
     },
     fuchsia_zircon::{Duration, DurationNum},
@@ -153,7 +153,7 @@
 pub struct ActivatedFakeHost {
     control: ControlHarness,
     host: String,
-    hci: Option<FakeHciDevice>,
+    hci: Option<Emulator>,
 }
 
 // All Fake HCI Devices have this address
@@ -165,7 +165,7 @@
         let initial_hosts: Vec<String> = control.read().hosts.keys().cloned().collect();
         let initial_hosts_ = initial_hosts.clone();
 
-        let hci = FakeHciDevice::new(name)?;
+        let hci = await!(Emulator::new(name))?;
 
         let control_state = await!(control.when_satisfied(
             Predicate::<ControlState>::new(
diff --git a/src/connectivity/bluetooth/tests/integration/src/harness/host_driver.rs b/src/connectivity/bluetooth/tests/integration/src/harness/host_driver.rs
index bab3221..728dac1 100644
--- a/src/connectivity/bluetooth/tests/integration/src/harness/host_driver.rs
+++ b/src/connectivity/bluetooth/tests/integration/src/harness/host_driver.rs
@@ -8,7 +8,7 @@
     fidl_fuchsia_bluetooth_host::{HostEvent, HostProxy},
     fuchsia_async::{self as fasync, TimeoutExt},
     fuchsia_bluetooth::{
-        error::Error as BtError, expectation::Predicate, fake_hci::FakeHciDevice, hci, host,
+        error::Error as BtError, expectation::Predicate, hci, hci_emulator::Emulator, host,
         util::clone_host_state,
     },
     fuchsia_vfs_watcher::{WatchEvent as VfsWatchEvent, Watcher as VfsWatcher},
@@ -33,7 +33,7 @@
 
 struct HostDriverHarnessInner {
     // Fake bt-hci device.
-    fake_hci_dev: Option<FakeHciDevice>,
+    hci_emulator: Option<Emulator>,
 
     // Access to the bt-host device under test.
     host_path: String,
@@ -90,13 +90,13 @@
 
 impl HostDriverHarnessInner {
     fn new(
-        hci: FakeHciDevice,
+        hci: Emulator,
         host_path: String,
         host: HostProxy,
         info: AdapterInfo,
     ) -> HostDriverHarness {
         HostDriverHarness(Arc::new(RwLock::new(HostDriverHarnessInner {
-            fake_hci_dev: Some(hci),
+            hci_emulator: Some(hci),
             host_path: host_path,
             host_proxy: host,
             host_info: info,
@@ -131,7 +131,7 @@
     }
 
     fn close_fake_hci(&mut self) {
-        self.fake_hci_dev = None;
+        self.hci_emulator = None;
     }
 
     fn store_task(&mut self, task: task::Waker) -> usize {
@@ -387,7 +387,7 @@
 
 // Creates a fake bt-hci device and returns the corresponding bt-host device once it gets created.
 async fn setup_emulated_host_test() -> Result<HostDriverHarness, Error> {
-    let fake_hci = FakeHciDevice::new("bt-hci-integration-test-0")?;
+    let fake_hci = await!(Emulator::new("bt-hci-integration-test-0"))?;
     let fake_hci_topo_path = fdio::device_get_topo_path(fake_hci.file())?;
 
     let dir = File::open(&BT_HOST_DIR)?;
diff --git a/src/connectivity/bluetooth/tests/integration/src/tests/control.rs b/src/connectivity/bluetooth/tests/integration/src/tests/control.rs
index 075fb20..87ce019 100644
--- a/src/connectivity/bluetooth/tests/integration/src/tests/control.rs
+++ b/src/connectivity/bluetooth/tests/integration/src/tests/control.rs
@@ -9,7 +9,7 @@
             asynchronous::{ExpectableState, ExpectableStateExt},
             Predicate,
         },
-        fake_hci::FakeHciDevice,
+        hci_emulator::Emulator,
     },
 };
 
@@ -21,8 +21,8 @@
     let initial_hosts: Vec<String> = control.read().hosts.keys().cloned().collect();
     let initial_hosts_ = initial_hosts.clone();
 
-    let fake_hci_0 = FakeHciDevice::new("bt-hci-integration-control-0")?;
-    let fake_hci_1 = FakeHciDevice::new("bt-hci-integration-control-1")?;
+    let fake_hci_0 = await!(Emulator::new("bt-hci-integration-control-0"))?;
+    let fake_hci_1 = await!(Emulator::new("bt-hci-integration-control-1"))?;
 
     let state = await!(control.when_satisfied(
         Predicate::<ControlState>::new(
diff --git a/src/connectivity/bluetooth/tests/integration/src/tests/lifecycle.rs b/src/connectivity/bluetooth/tests/integration/src/tests/lifecycle.rs
index 0c06fcf..b876478 100644
--- a/src/connectivity/bluetooth/tests/integration/src/tests/lifecycle.rs
+++ b/src/connectivity/bluetooth/tests/integration/src/tests/lifecycle.rs
@@ -6,7 +6,7 @@
     failure::{err_msg, Error, ResultExt},
     fidl_fuchsia_bluetooth_host::HostProxy,
     fuchsia_async as fasync,
-    fuchsia_bluetooth::{fake_hci::FakeHciDevice, hci, host},
+    fuchsia_bluetooth::{hci, hci_emulator::Emulator, host},
     fuchsia_vfs_watcher::{self as vfs_watcher, WatchEvent, WatchMessage},
     futures::{Stream, StreamExt, TryStreamExt},
     pin_utils::pin_mut,
@@ -59,7 +59,7 @@
 // Tests that creating and destroying a fake HCI device binds and unbinds the bt-host driver.
 pub async fn lifecycle_test(_: ()) -> Result<(), Error> {
     let original_hosts = host::list_host_devices();
-    let fake_hci = FakeHciDevice::new("bt-hci-integration-lifecycle")?;
+    let fake_hci = await!(Emulator::new("bt-hci-integration-lifecycle"))?;
     let bthost = await!(watch_for_host(original_hosts))?;
 
     // Check a device showed up within an acceptable timeout
diff --git a/src/connectivity/bluetooth/tests/meta/bt-integration-tests.cmx b/src/connectivity/bluetooth/tests/meta/bt-integration-tests.cmx
index 33f8c6f8..0b121ef 100644
--- a/src/connectivity/bluetooth/tests/meta/bt-integration-tests.cmx
+++ b/src/connectivity/bluetooth/tests/meta/bt-integration-tests.cmx
@@ -14,6 +14,7 @@
     },
     "sandbox": {
         "dev": [
+            "class/bt-emulator",
             "class/bt-host",
             "test/test"
         ],