[bt][host][fidl] Use fuchsia.bluetooth.PeerId and error syntax

* Changed all host.fidl methods to use fuchsia.bluetooth.PeerId rather
than string as the peer ID type.
* Changed all host.fidl methods that have a reply to use the FIDL error
syntax and the fuchsia.bluetooth.sys.Error type instead of
fuchsia.bluetooth.Status.
* Converted some of the multi-line awaits in integration tests to inline
where the former was unnecessary.
* Removed the GATT proxy setup in bt-gap's connection handling.

Bug: 35008
Test: bt-integration-tests; bt-host-unittests.
Change-Id: Icc1c38206b032d714628d86bbe5e5d8c89e28a97
diff --git a/sdk/fidl/fuchsia.bluetooth.sys/access.fidl b/sdk/fidl/fuchsia.bluetooth.sys/access.fidl
index 10bdfaa..e2f12cb 100644
--- a/sdk/fidl/fuchsia.bluetooth.sys/access.fidl
+++ b/sdk/fidl/fuchsia.bluetooth.sys/access.fidl
@@ -12,6 +12,21 @@
 
     /// The peer designated for the operation was not found.
     PEER_NOT_FOUND = 2;
+
+    /// The time limit for the operation has expired.
+    TIMED_OUT = 3;
+
+    /// The operation was canceled.
+    CANCELED = 4;
+
+    /// Operation already in progress.
+    IN_PROGRESS = 5;
+
+    /// Operation not supported.
+    NOT_SUPPORTED = 6;
+
+    /// The operation was given an invalid set of arguments.
+    INVALID_ARGUMENTS = 7;
 };
 
 /// Represents an active procedure. The validity of a handle that supports this protocol is tied to
@@ -19,7 +34,8 @@
 ///
 ///   1. Closing a token handle ends the procedure that it is attached to.
 ///   2. The system closes a token handle to communicate that a procedure was internally terminated.
-protocol ProcedureToken {};
+protocol ProcedureToken {
+};
 
 /// Protocol that abstracts the operational modes and procedures defined in the Bluetooth Generic
 /// Access Profile (see Core Specification v5.1, Vol 3, Part C).
diff --git a/src/connectivity/bluetooth/core/bt-gap/src/host_device.rs b/src/connectivity/bluetooth/core/bt-gap/src/host_device.rs
index 9e3c81b..30d6f87 100644
--- a/src/connectivity/bluetooth/core/bt-gap/src/host_device.rs
+++ b/src/connectivity/bluetooth/core/bt-gap/src/host_device.rs
@@ -5,15 +5,12 @@
 use {
     anyhow::format_err,
     fidl::endpoints::ClientEnd,
-    fidl_fuchsia_bluetooth::DeviceClass,
-    fidl_fuchsia_bluetooth::PeerId as FidlPeerId,
+    fidl_fuchsia_bluetooth::{self as fbt, DeviceClass},
     fidl_fuchsia_bluetooth_control::{
         self as control, HostData, InputCapabilityType, OutputCapabilityType,
         PairingDelegateMarker, PairingOptions,
     },
-    fidl_fuchsia_bluetooth_gatt::ClientProxy,
     fidl_fuchsia_bluetooth_host::{HostEvent, HostProxy},
-    fidl_fuchsia_bluetooth_le::CentralProxy,
     fuchsia_bluetooth::{
         inspect::Inspectable,
         types::{BondingData, HostInfo, Peer, PeerId},
@@ -22,16 +19,15 @@
     futures::{Future, FutureExt, StreamExt},
     parking_lot::RwLock,
     pin_utils::pin_mut,
-    std::{collections::HashMap, convert::TryInto, path::PathBuf, sync::Arc},
+    std::{convert::TryInto, path::PathBuf, sync::Arc},
 };
 
-use crate::types::{self, from_fidl_status, Error};
+use crate::types::{self, from_fidl_result, from_fidl_status, Error};
 
 pub struct HostDevice {
     pub path: PathBuf,
     host: HostProxy,
     info: Inspectable<HostInfo>,
-    gatt: HashMap<String, (CentralProxy, ClientProxy)>,
 }
 
 // Many HostDevice methods return impl Future rather than being implemented as `async`. This has an
@@ -41,7 +37,7 @@
 // the future was polled.
 impl HostDevice {
     pub fn new(path: PathBuf, host: HostProxy, info: Inspectable<HostInfo>) -> Self {
-        HostDevice { path, host, info, gatt: HashMap::new() }
+        HostDevice { path, host, info }
     }
 
     pub fn get_host(&self) -> &HostProxy {
@@ -61,50 +57,43 @@
         &self.info
     }
 
-    pub fn rm_gatt(&mut self, id: String) -> impl Future<Output = types::Result<()>> {
-        let gatt_entry = self.gatt.remove(&id);
-        async move {
-            if let Some((central, _)) = gatt_entry {
-                from_fidl_status(central.disconnect_peripheral(id.as_str()).await)
-            } else {
-                Err(Error::not_found("Unknown Peripheral"))
-            }
-        }
-    }
-
     pub fn set_name(&self, mut name: String) -> impl Future<Output = types::Result<()>> {
-        self.host.set_local_name(&mut name).map(from_fidl_status)
+        self.host.set_local_name(&mut name).map(from_fidl_result)
     }
 
     pub fn set_device_class(
         &self,
         mut cod: DeviceClass,
     ) -> impl Future<Output = types::Result<()>> {
-        self.host.set_device_class(&mut cod).map(from_fidl_status)
+        self.host.set_device_class(&mut cod).map(from_fidl_result)
     }
 
     pub fn start_discovery(&mut self) -> impl Future<Output = types::Result<()>> {
-        self.host.start_discovery().map(from_fidl_status)
+        self.host.start_discovery().map(from_fidl_result)
     }
 
-    pub fn connect(&mut self, device_id: String) -> impl Future<Output = types::Result<()>> {
-        self.host.connect(&device_id).map(from_fidl_status)
+    pub fn connect(&mut self, id: PeerId) -> impl Future<Output = types::Result<()>> {
+        let mut id: fbt::PeerId = id.into();
+        self.host.connect(&mut id).map(from_fidl_result)
     }
 
-    pub fn disconnect(&mut self, device_id: String) -> impl Future<Output = types::Result<()>> {
-        self.host.disconnect(&device_id).map(from_fidl_status)
+    pub fn disconnect(&mut self, id: PeerId) -> impl Future<Output = types::Result<()>> {
+        let mut id: fbt::PeerId = id.into();
+        self.host.disconnect(&mut id).map(from_fidl_result)
     }
 
     pub fn pair(
         &mut self,
-        mut id: FidlPeerId,
+        id: PeerId,
         options: PairingOptions,
     ) -> impl Future<Output = types::Result<()>> {
-        self.host.pair(&mut id, options).map(from_fidl_status)
+        let mut id: fbt::PeerId = id.into();
+        self.host.pair(&mut id, options).map(from_fidl_result)
     }
 
-    pub fn forget(&mut self, peer_id: String) -> impl Future<Output = types::Result<()>> {
-        self.host.forget(&peer_id).map(from_fidl_status)
+    pub fn forget(&mut self, id: PeerId) -> impl Future<Output = types::Result<()>> {
+        let mut id: fbt::PeerId = id.into();
+        self.host.forget(&mut id).map(from_fidl_result)
     }
 
     pub fn close(&self) -> types::Result<()> {
@@ -120,20 +109,19 @@
     }
 
     pub fn set_connectable(&self, value: bool) -> impl Future<Output = types::Result<()>> {
-        self.host.set_connectable(value).map(from_fidl_status)
+        self.host.set_connectable(value).map(from_fidl_result)
     }
 
-    pub fn stop_discovery(&self) -> impl Future<Output = types::Result<()>> {
-        self.host.stop_discovery().map(from_fidl_status)
+    pub fn stop_discovery(&self) -> types::Result<()> {
+        self.host.stop_discovery().map_err(|e| e.into())
     }
 
     pub fn set_discoverable(&self, discoverable: bool) -> impl Future<Output = types::Result<()>> {
-        self.host.set_discoverable(discoverable).map(from_fidl_status)
+        self.host.set_discoverable(discoverable).map(from_fidl_result)
     }
 
     pub fn set_local_data(&self, mut data: HostData) -> types::Result<()> {
-        self.host.set_local_data(&mut data)?;
-        Ok(())
+        self.host.set_local_data(&mut data).map_err(|e| e.into())
     }
 
     pub fn enable_privacy(&self, enable: bool) -> types::Result<()> {
diff --git a/src/connectivity/bluetooth/core/bt-gap/src/host_dispatcher.rs b/src/connectivity/bluetooth/core/bt-gap/src/host_dispatcher.rs
index ac5055f..fef7c6c 100644
--- a/src/connectivity/bluetooth/core/bt-gap/src/host_dispatcher.rs
+++ b/src/connectivity/bluetooth/core/bt-gap/src/host_dispatcher.rs
@@ -5,9 +5,7 @@
 use {
     anyhow::{format_err, Context as _, Error},
     fidl::endpoints::{self, ServerEnd},
-    fidl_fuchsia_bluetooth::{
-        Appearance, DeviceClass, Error as FidlError, ErrorCode, PeerId as FidlPeerId,
-    },
+    fidl_fuchsia_bluetooth::{Appearance, DeviceClass, Error as FidlError, ErrorCode},
     fidl_fuchsia_bluetooth_bredr::ProfileMarker,
     fidl_fuchsia_bluetooth_control::{
         self as control, ControlControlHandle, HostData, InputCapabilityType, LocalKey,
@@ -70,13 +68,10 @@
     fn drop(&mut self) {
         fx_vlog!(1, "DiscoveryRequestToken dropped");
         if let Some(host) = self.adap.upgrade() {
-            let await_response = host.write().stop_discovery();
-            fasync::spawn(async move {
-                if let Err(err) = await_response.await {
-                    // TODO(45325) - we should close the host channel if an error is returned
-                    fx_log_warn!("Unexpected error response when stopping discovery: {:?}", err);
-                }
-            });
+            if let Err(err) = host.write().stop_discovery() {
+                // TODO(45325) - we should close the host channel if an error is returned
+                fx_log_warn!("Unexpected error response when stopping discovery: {:?}", err);
+            }
         }
     }
 }
@@ -461,7 +456,7 @@
         for adapter in adapters {
             let adapter_path = adapter.read().path.clone();
 
-            let fut = adapter.write().forget(peer_id.to_string());
+            let fut = adapter.write().forget(peer_id);
             match fut.await {
                 Ok(()) => adapters_removed += 1,
                 Err(types::Error::HostError(FidlError {
@@ -484,7 +479,7 @@
         Ok(())
     }
 
-    pub async fn connect(&self, peer_id: String) -> types::Result<()> {
+    pub async fn connect(&self, peer_id: PeerId) -> types::Result<()> {
         let host = self.get_active_adapter().await;
         match host {
             Some(host) => {
@@ -495,7 +490,7 @@
         }
     }
 
-    pub async fn pair(&self, id: FidlPeerId, pairing_options: PairingOptions) -> types::Result<()> {
+    pub async fn pair(&self, id: PeerId, pairing_options: PairingOptions) -> types::Result<()> {
         let host = self.get_active_adapter().await;
         match host {
             Some(host) => {
@@ -507,14 +502,10 @@
     }
 
     // Attempt to disconnect peer with id `peer_id` from all transports
-    pub async fn disconnect(&self, peer_id: String) -> types::Result<()> {
+    pub async fn disconnect(&self, peer_id: PeerId) -> types::Result<()> {
         let host = self.get_active_adapter().await;
         match host {
             Some(host) => {
-                // Suppress the error from `rm_gatt`, as the peer not having a GATT entry
-                // (i.e. not using LE) is not a failure condition
-                let fut = host.write().rm_gatt(peer_id.clone());
-                let _ = fut.await;
                 let fut = host.write().disconnect(peer_id);
                 fut.await
             }
diff --git a/src/connectivity/bluetooth/core/bt-gap/src/services/control.rs b/src/connectivity/bluetooth/core/bt-gap/src/services/control.rs
index 1aa0d50..773e1ce 100644
--- a/src/connectivity/bluetooth/core/bt-gap/src/services/control.rs
+++ b/src/connectivity/bluetooth/core/bt-gap/src/services/control.rs
@@ -12,7 +12,10 @@
     std::sync::Arc,
 };
 
-use crate::{host_dispatcher::*, types::status_response};
+use crate::{
+    host_dispatcher::*,
+    types::{self, status_response},
+};
 
 struct ControlSession {
     discovery_token: Option<Arc<DiscoveryRequestToken>>,
@@ -42,6 +45,23 @@
     Ok(())
 }
 
+fn parse_peer_id(id: &str) -> Result<PeerId, types::Error> {
+    id.parse::<PeerId>()
+        .map_err(|_| types::Error::InternalError(format_err!("invalid peer ID: {}", id)))
+}
+
+async fn handle_connect(hd: HostDispatcher, device_id: &str) -> types::Result<()> {
+    hd.connect(parse_peer_id(device_id)?).await
+}
+
+async fn handle_forget(hd: HostDispatcher, device_id: &str) -> types::Result<()> {
+    hd.forget(parse_peer_id(device_id)?).await
+}
+
+async fn handle_disconnect(hd: HostDispatcher, device_id: &str) -> types::Result<()> {
+    hd.disconnect(parse_peer_id(device_id)?).await
+}
+
 async fn handler(
     hd: HostDispatcher,
     session: &mut ControlSession,
@@ -49,11 +69,11 @@
 ) -> fidl::Result<()> {
     match event {
         ControlRequest::Connect { device_id, responder } => {
-            let result = hd.connect(device_id).await;
+            let result = handle_connect(hd, &device_id).await;
             responder.send(&mut status_response(result))
         }
         ControlRequest::Pair { id, options, responder } => {
-            let result = hd.pair(id, options).await;
+            let result = hd.pair(id.into(), options).await;
             responder.send(&mut status_response(result))
         }
         ControlRequest::SetDiscoverable { discoverable, responder } => {
@@ -76,17 +96,11 @@
             Ok(())
         }
         ControlRequest::Forget { device_id, responder } => {
-            let peer_id = device_id
-                .parse::<PeerId>()
-                .map_err(|_| format_err!(format!("Invalid peer identifier: {}", device_id)));
-            let result = match peer_id {
-                Ok(peer_id) => hd.forget(peer_id).await,
-                Err(e) => Err(e.into()),
-            };
+            let result = handle_forget(hd, &device_id).await;
             responder.send(&mut status_response(result))
         }
         ControlRequest::Disconnect { device_id, responder } => {
-            let result = hd.disconnect(device_id).await;
+            let result = handle_disconnect(hd, &device_id).await;
             responder.send(&mut status_response(result))
         }
         ControlRequest::GetKnownRemoteDevices { responder } => {
diff --git a/src/connectivity/bluetooth/core/bt-gap/src/test/host_device.rs b/src/connectivity/bluetooth/core/bt-gap/src/test/host_device.rs
index 075c8be..18c10af 100644
--- a/src/connectivity/bluetooth/core/bt-gap/src/test/host_device.rs
+++ b/src/connectivity/bluetooth/core/bt-gap/src/test/host_device.rs
@@ -8,7 +8,6 @@
     fidl_fuchsia_bluetooth_host::{HostControlHandle, HostMarker, HostRequest, HostRequestStream},
     fidl_fuchsia_bluetooth_sys::{HostInfo as FidlHostInfo, TechnologyType},
     fuchsia_bluetooth::{
-        bt_fidl_status,
         inspect::{placeholder_node, Inspectable},
         types::{Address, BondingData, HostInfo, Peer, PeerId},
     },
@@ -62,7 +61,7 @@
     let expect_fidl = expect_call(server.clone(), |_, e| match e {
         HostRequest::SetLocalName { local_name, responder } => {
             info.write().local_name = Some(local_name);
-            responder.send(&mut bt_fidl_status!())?;
+            responder.send(&mut Ok(()))?;
             Ok(())
         }
         _ => Err(format_err!("Unexpected!")),
diff --git a/src/connectivity/bluetooth/core/bt-gap/src/types.rs b/src/connectivity/bluetooth/core/bt-gap/src/types.rs
index 5f6bb9c..2aa8d3b 100644
--- a/src/connectivity/bluetooth/core/bt-gap/src/types.rs
+++ b/src/connectivity/bluetooth/core/bt-gap/src/types.rs
@@ -2,15 +2,23 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-use {anyhow::format_err, fidl_fuchsia_bluetooth as bt, fuchsia_bluetooth::bt_fidl_status};
+use {
+    anyhow::format_err, fidl_fuchsia_bluetooth as bt, fidl_fuchsia_bluetooth_sys as sys,
+    fuchsia_bluetooth::bt_fidl_status,
+};
 
 /// Type representing Possible errors raised in the operation of BT-GAP
 #[derive(Debug)]
 pub enum Error {
-    // Internal bt-gap Error
+    /// Internal bt-gap Error
     InternalError(anyhow::Error),
-    // Host Error
+
+    /// Host Error
     HostError(bt::Error),
+
+    /// fuchsia.bluetooth.sys API errors. Used to encapsulate errors that are reported by bt-host
+    /// and for the fuchsia.bluetooth.sys.Access API.
+    SysError(sys::Error),
 }
 
 pub type Result<T> = std::result::Result<T, Error>;
@@ -20,6 +28,9 @@
         match self {
             Error::HostError(err) => bt::Status { error: Some(Box::new(err)) },
             Error::InternalError(err) => bt_fidl_status!(Failed, format!("{}", err)),
+            Error::SysError(err) => {
+                bt::Status { error: Some(Box::new(sys_error_to_deprecated(err))) }
+            }
         }
     }
 
@@ -31,14 +42,6 @@
         })
     }
 
-    pub fn not_found(desc: &str) -> Error {
-        Error::HostError(bt::Error {
-            error_code: bt::ErrorCode::NotFound,
-            protocol_error_code: 0,
-            description: Some(desc.to_string()),
-        })
-    }
-
     pub fn as_failure(self) -> anyhow::Error {
         match self {
             Error::InternalError(err) => err,
@@ -46,6 +49,7 @@
                 "Host Error: {}",
                 err.description.unwrap_or("Unknown Host Error".to_string())
             ),
+            Error::SysError(err) => format_err!("Host Error: {:?}", err),
         }
     }
 }
@@ -62,11 +66,18 @@
     }
 }
 
+impl From<sys::Error> for Error {
+    fn from(err: sys::Error) -> Error {
+        Error::SysError(err)
+    }
+}
+
 impl From<anyhow::Error> for Error {
     fn from(err: anyhow::Error) -> Error {
         Error::InternalError(err)
     }
 }
+
 impl From<fidl::Error> for Error {
     fn from(err: fidl::Error) -> Error {
         Error::InternalError(format_err!(format!("Internal FIDL error: {}", err)))
@@ -80,6 +91,31 @@
     }
 }
 
+pub fn from_fidl_result<T>(r: fidl::Result<std::result::Result<T, sys::Error>>) -> Result<T> {
+    match r {
+        Ok(r) => r.map_err(Error::from),
+        Err(e) => Err(Error::from(e)),
+    }
+}
+
+// Maps a fuchsia.bluetooth.sys.Error value to a fuchsia.bluetooth.Error. This is maintained for
+// compatibility until fuchsia.bluetooth.control and fuchsia.bluetooth.Status are removed.
+fn sys_error_to_deprecated(e: sys::Error) -> bt::Error {
+    bt::Error {
+        error_code: match e {
+            sys::Error::Failed => bt::ErrorCode::Failed,
+            sys::Error::PeerNotFound => bt::ErrorCode::NotFound,
+            sys::Error::TimedOut => bt::ErrorCode::TimedOut,
+            sys::Error::Canceled => bt::ErrorCode::Canceled,
+            sys::Error::InProgress => bt::ErrorCode::InProgress,
+            sys::Error::NotSupported => bt::ErrorCode::NotSupported,
+            sys::Error::InvalidArguments => bt::ErrorCode::InvalidArguments,
+        },
+        protocol_error_code: 0,
+        description: None,
+    }
+}
+
 pub trait StatusExt {
     fn as_result(self) -> Result<()>;
 }
diff --git a/src/connectivity/bluetooth/core/bt-host/fidl/gatt_client_server.cc b/src/connectivity/bluetooth/core/bt-host/fidl/gatt_client_server.cc
index 1edd551..d8ede06 100644
--- a/src/connectivity/bluetooth/core/bt-host/fidl/gatt_client_server.cc
+++ b/src/connectivity/bluetooth/core/bt-host/fidl/gatt_client_server.cc
@@ -45,7 +45,8 @@
   auto cb = [callback = std::move(callback)](bt::att::Status status, auto services) {
     std::vector<ServiceInfo> out;
     if (!status) {
-      auto fidl_status = fidl_helpers::StatusToFidl(status, "Failed to discover services");
+      auto fidl_status =
+          fidl_helpers::StatusToFidlDeprecated(status, "Failed to discover services");
       callback(std::move(fidl_status), std::move(out));
       return;
     }
diff --git a/src/connectivity/bluetooth/core/bt-host/fidl/gatt_remote_service_server.cc b/src/connectivity/bluetooth/core/bt-host/fidl/gatt_remote_service_server.cc
index d57e091..9f752f1 100644
--- a/src/connectivity/bluetooth/core/bt-host/fidl/gatt_remote_service_server.cc
+++ b/src/connectivity/bluetooth/core/bt-host/fidl/gatt_remote_service_server.cc
@@ -77,7 +77,7 @@
       }
     }
 
-    callback(fidl_helpers::StatusToFidl(status, ""), std::move(fidl_chrcs));
+    callback(fidl_helpers::StatusToFidlDeprecated(status, ""), std::move(fidl_chrcs));
   };
 
   service_->DiscoverCharacteristics(std::move(res_cb));
@@ -95,7 +95,7 @@
       value.Copy(&vec_view);
     }
 
-    callback(fidl_helpers::StatusToFidl(status), std::move(vec));
+    callback(fidl_helpers::StatusToFidlDeprecated(status), std::move(vec));
   };
 
   service_->ReadCharacteristic(CharacteristicHandle(id), std::move(cb));
@@ -115,7 +115,7 @@
       value.Copy(&vec_view);
     }
 
-    callback(fidl_helpers::StatusToFidl(status), std::move(vec));
+    callback(fidl_helpers::StatusToFidlDeprecated(status), std::move(vec));
   };
 
   service_->ReadLongCharacteristic(CharacteristicHandle(id), offset, max_bytes, std::move(cb));
@@ -124,7 +124,7 @@
 void GattRemoteServiceServer::WriteCharacteristic(uint64_t id, ::std::vector<uint8_t> value,
                                                   WriteCharacteristicCallback callback) {
   auto cb = [callback = std::move(callback)](bt::att::Status status) {
-    callback(fidl_helpers::StatusToFidl(status, ""));
+    callback(fidl_helpers::StatusToFidlDeprecated(status, ""));
   };
 
   service_->WriteCharacteristic(CharacteristicHandle(id), std::move(value), std::move(cb));
@@ -134,7 +134,7 @@
                                                       ::std::vector<uint8_t> value,
                                                       WriteLongCharacteristicCallback callback) {
   auto cb = [callback = std::move(callback)](bt::att::Status status) {
-    callback(fidl_helpers::StatusToFidl(status, ""));
+    callback(fidl_helpers::StatusToFidlDeprecated(status, ""));
   };
 
   service_->WriteLongCharacteristic(CharacteristicHandle(id), offset, std::move(value),
@@ -158,7 +158,7 @@
       value.Copy(&vec_view);
     }
 
-    callback(fidl_helpers::StatusToFidl(status), std::move(vec));
+    callback(fidl_helpers::StatusToFidlDeprecated(status), std::move(vec));
   };
 
   service_->ReadDescriptor(DescriptorHandle(id), std::move(cb));
@@ -177,7 +177,7 @@
       value.Copy(&vec_view);
     }
 
-    callback(fidl_helpers::StatusToFidl(status), std::move(vec));
+    callback(fidl_helpers::StatusToFidlDeprecated(status), std::move(vec));
   };
 
   service_->ReadLongDescriptor(DescriptorHandle(id), offset, max_bytes, std::move(cb));
@@ -187,7 +187,7 @@
                                               WriteDescriptorCallback callback) {
   service_->WriteDescriptor(DescriptorHandle(id), std::move(value),
                             [callback = std::move(callback)](bt::att::Status status) {
-                              callback(fidl_helpers::StatusToFidl(status, ""));
+                              callback(fidl_helpers::StatusToFidlDeprecated(status, ""));
                             });
 }
 
@@ -196,7 +196,7 @@
                                                   WriteLongDescriptorCallback callback) {
   service_->WriteLongDescriptor(DescriptorHandle(id), offset, std::move(value),
                                 [callback = std::move(callback)](bt::att::Status status) {
-                                  callback(fidl_helpers::StatusToFidl(status, ""));
+                                  callback(fidl_helpers::StatusToFidlDeprecated(status, ""));
                                 });
 }
 
@@ -218,7 +218,7 @@
 
     service_->DisableNotifications(handle, iter->second,
                                    [callback = std::move(callback)](bt::att::Status status) {
-                                     callback(fidl_helpers::StatusToFidl(status, ""));
+                                     callback(fidl_helpers::StatusToFidlDeprecated(status, ""));
                                    });
     notify_handlers_.erase(iter);
 
@@ -263,7 +263,7 @@
       self->notify_handlers_.erase(handle);
     }
 
-    callback(fidl_helpers::StatusToFidl(status, ""));
+    callback(fidl_helpers::StatusToFidlDeprecated(status, ""));
   };
 
   service_->EnableNotifications(handle, std::move(value_cb), std::move(status_cb));
diff --git a/src/connectivity/bluetooth/core/bt-host/fidl/helpers.cc b/src/connectivity/bluetooth/core/bt-host/fidl/helpers.cc
index ecfc8a39..0ba3abd 100644
--- a/src/connectivity/bluetooth/core/bt-host/fidl/helpers.cc
+++ b/src/connectivity/bluetooth/core/bt-host/fidl/helpers.cc
@@ -189,7 +189,7 @@
   return bt::DeviceAddressBytes(bytes);
 }
 
-ErrorCode HostErrorToFidl(bt::HostError host_error) {
+ErrorCode HostErrorToFidlDeprecated(bt::HostError host_error) {
   switch (host_error) {
     case bt::HostError::kFailed:
       return ErrorCode::FAILED;
@@ -222,6 +222,29 @@
   return status;
 }
 
+fsys::Error HostErrorToFidl(bt::HostError error) {
+  ZX_DEBUG_ASSERT(error != bt::HostError::kNoError);
+  switch (error) {
+    case bt::HostError::kFailed:
+      return fsys::Error::FAILED;
+    case bt::HostError::kTimedOut:
+      return fsys::Error::TIMED_OUT;
+    case bt::HostError::kInvalidParameters:
+      return fsys::Error::INVALID_ARGUMENTS;
+    case bt::HostError::kCanceled:
+      return fsys::Error::CANCELED;
+    case bt::HostError::kInProgress:
+      return fsys::Error::IN_PROGRESS;
+    case bt::HostError::kNotSupported:
+      return fsys::Error::NOT_SUPPORTED;
+    case bt::HostError::kNotFound:
+      return fsys::Error::PEER_NOT_FOUND;
+    default:
+      break;
+  }
+  return fsys::Error::FAILED;
+}
+
 bt::UUID UuidFromFidl(const fuchsia::bluetooth::Uuid& input) {
   bt::UUID output;
   // Conversion must always succeed given the defined size of |input|.
diff --git a/src/connectivity/bluetooth/core/bt-host/fidl/helpers.h b/src/connectivity/bluetooth/core/bt-host/fidl/helpers.h
index a2cb298..e7a99b5 100644
--- a/src/connectivity/bluetooth/core/bt-host/fidl/helpers.h
+++ b/src/connectivity/bluetooth/core/bt-host/fidl/helpers.h
@@ -48,21 +48,21 @@
 
 // Functions for generating a FIDL bluetooth::Status
 
-fuchsia::bluetooth::ErrorCode HostErrorToFidl(bt::HostError host_error);
+fuchsia::bluetooth::ErrorCode HostErrorToFidlDeprecated(bt::HostError host_error);
 
 fuchsia::bluetooth::Status NewFidlError(fuchsia::bluetooth::ErrorCode error_code,
                                         std::string description);
 
 template <typename ProtocolErrorCode>
-fuchsia::bluetooth::Status StatusToFidl(const bt::Status<ProtocolErrorCode>& status,
-                                        std::string msg = "") {
+fuchsia::bluetooth::Status StatusToFidlDeprecated(const bt::Status<ProtocolErrorCode>& status,
+                                                  std::string msg = "") {
   fuchsia::bluetooth::Status fidl_status;
   if (status.is_success()) {
     return fidl_status;
   }
 
   auto error = fuchsia::bluetooth::Error::New();
-  error->error_code = HostErrorToFidl(status.error());
+  error->error_code = HostErrorToFidlDeprecated(status.error());
   error->description = msg.empty() ? status.ToString() : std::move(msg);
   if (status.is_protocol_error()) {
     error->protocol_error_code = static_cast<uint32_t>(status.protocol_error());
@@ -72,6 +72,23 @@
   return fidl_status;
 }
 
+// Convert a bt::HostError to fuchsia.bluetooth.sys.Error. This function does only
+// deals with bt::HostError types and does not support Bluetooth protocol-specific errors; to
+// represent such errors use protocol-specific FIDL error types. An |error| value of
+// HostError::kNoError is not allowed.
+fuchsia::bluetooth::sys::Error HostErrorToFidl(bt::HostError error);
+
+// Convert any bt::Status to a fit::result that uses the fuchsia.bluetooth.sys library error codes.
+template <typename ProtocolErrorCode>
+fit::result<void, fuchsia::bluetooth::sys::Error> StatusToFidl(
+    bt::Status<ProtocolErrorCode> status) {
+  if (status) {
+    return fit::ok();
+  } else {
+    return fit::error(HostErrorToFidl(status.error()));
+  }
+}
+
 bt::UUID UuidFromFidl(const fuchsia::bluetooth::Uuid& input);
 
 // Functions that convert FIDL types to library objects.
diff --git a/src/connectivity/bluetooth/core/bt-host/fidl/helpers_unittest.cc b/src/connectivity/bluetooth/core/bt-host/fidl/helpers_unittest.cc
index 3deb1e9..64f0be5 100644
--- a/src/connectivity/bluetooth/core/bt-host/fidl/helpers_unittest.cc
+++ b/src/connectivity/bluetooth/core/bt-host/fidl/helpers_unittest.cc
@@ -19,6 +19,19 @@
 namespace fidl_helpers {
 namespace {
 
+TEST(FidlHelpersTest, HostErrorToFidl) {
+  EXPECT_EQ(fsys::Error::FAILED, HostErrorToFidl(bt::HostError::kFailed));
+  EXPECT_EQ(fsys::Error::TIMED_OUT, HostErrorToFidl(bt::HostError::kTimedOut));
+  EXPECT_EQ(fsys::Error::INVALID_ARGUMENTS, HostErrorToFidl(bt::HostError::kInvalidParameters));
+  EXPECT_EQ(fsys::Error::CANCELED, HostErrorToFidl(bt::HostError::kCanceled));
+  EXPECT_EQ(fsys::Error::IN_PROGRESS, HostErrorToFidl(bt::HostError::kInProgress));
+  EXPECT_EQ(fsys::Error::NOT_SUPPORTED, HostErrorToFidl(bt::HostError::kNotSupported));
+  EXPECT_EQ(fsys::Error::PEER_NOT_FOUND, HostErrorToFidl(bt::HostError::kNotFound));
+
+  // All other errors currently map to FAILED.
+  EXPECT_EQ(fsys::Error::FAILED, HostErrorToFidl(bt::HostError::kProtocolError));
+}
+
 TEST(FIDL_HelpersTest, AddressBytesFrommString) {
   EXPECT_FALSE(AddressBytesFromString(""));
   EXPECT_FALSE(AddressBytesFromString("FF"));
diff --git a/src/connectivity/bluetooth/core/bt-host/fidl/host_server.cc b/src/connectivity/bluetooth/core/bt-host/fidl/host_server.cc
index 811c87d..b9d4e8a 100644
--- a/src/connectivity/bluetooth/core/bt-host/fidl/host_server.cc
+++ b/src/connectivity/bluetooth/core/bt-host/fidl/host_server.cc
@@ -4,6 +4,7 @@
 
 #include "host_server.h"
 
+#include <lib/fit/result.h>
 #include <zircon/assert.h>
 
 #include "fuchsia/bluetooth/control/cpp/fidl.h"
@@ -31,14 +32,17 @@
 namespace bthost {
 
 namespace fbt = fuchsia::bluetooth;
+namespace fsys = fuchsia::bluetooth::sys;
 
 using bt::PeerId;
 using bt::sm::IOCapability;
 using fidl_helpers::AddressBytesFromString;
+using fidl_helpers::HostErrorToFidl;
 using fidl_helpers::NewFidlError;
 using fidl_helpers::PeerIdFromString;
 using fidl_helpers::SecurityLevelFromFidl;
 using fidl_helpers::StatusToFidl;
+using fidl_helpers::StatusToFidlDeprecated;
 using fuchsia::bluetooth::Bool;
 using fuchsia::bluetooth::ErrorCode;
 using fuchsia::bluetooth::Status;
@@ -165,7 +169,7 @@
                             if (status && self) {
                               self->NotifyInfoChange();
                             }
-                            callback(StatusToFidl(status, "Can't Set Local Name"));
+                            callback(StatusToFidl(status));
                           });
 }
 
@@ -173,19 +177,18 @@
 void HostServer::SetDeviceClass(fbt::DeviceClass device_class, SetDeviceClassCallback callback) {
   // Device Class values must only contain data in the lower 3 bytes.
   if (device_class.value >= 1 << 24) {
-    callback(NewFidlError(ErrorCode::INVALID_ARGUMENTS, "Can't Set Device Class"));
+    callback(fit::error(fsys::Error::INVALID_ARGUMENTS));
     return;
   }
   bt::DeviceClass dev_class(device_class.value);
-  adapter()->SetDeviceClass(dev_class, [callback = std::move(callback)](auto status) {
-    callback(fidl_helpers::StatusToFidl(status, "Can't Set Device Class"));
-  });
+  adapter()->SetDeviceClass(
+      dev_class, [callback = std::move(callback)](auto status) { callback(StatusToFidl(status)); });
 }
 
 void HostServer::StartLEDiscovery(StartDiscoveryCallback callback) {
   auto le_manager = adapter()->le_discovery_manager();
   if (!le_manager) {
-    callback(NewFidlError(ErrorCode::BAD_STATE, "Adapter is not initialized yet."));
+    callback(fit::error(fsys::Error::FAILED));
     return;
   }
   le_manager->StartDiscovery(
@@ -193,18 +196,18 @@
         // End the new session if this AdapterServer got destroyed in the
         // mean time (e.g. because the client disconnected).
         if (!self) {
-          callback(NewFidlError(ErrorCode::FAILED, "Adapter Shutdown"));
+          callback(fit::error(fsys::Error::FAILED));
           return;
         }
 
         if (!self->requesting_discovery_) {
-          callback(NewFidlError(ErrorCode::CANCELED, "Request canceled"));
+          callback(fit::error(fsys::Error::CANCELED));
           return;
         }
 
         if (!session) {
           bt_log(TRACE, "bt-host", "failed to start LE discovery session");
-          callback(NewFidlError(ErrorCode::FAILED, "Failed to start LE discovery session"));
+          callback(fit::error(fsys::Error::FAILED));
           self->bredr_discovery_session_ = nullptr;
           self->requesting_discovery_ = false;
           return;
@@ -222,7 +225,7 @@
         // Send the adapter state update.
         self->NotifyInfoChange();
 
-        callback(Status());
+        callback(fit::ok());
       });
 }
 
@@ -232,7 +235,7 @@
 
   if (le_discovery_session_ || requesting_discovery_) {
     bt_log(TRACE, "bt-host", "discovery already in progress");
-    callback(NewFidlError(ErrorCode::IN_PROGRESS, "Discovery already in progress"));
+    callback(fit::error(fsys::Error::IN_PROGRESS));
     return;
   }
 
@@ -247,19 +250,26 @@
       [self = weak_ptr_factory_.GetWeakPtr(), callback = std::move(callback)](
           bt::hci::Status status, auto session) mutable {
         if (!self) {
-          callback(NewFidlError(ErrorCode::FAILED, "Adapter Shutdown"));
+          callback(fit::error(fsys::Error::FAILED));
           return;
         }
 
         if (!self->requesting_discovery_) {
-          callback(NewFidlError(ErrorCode::CANCELED, "Request Canceled"));
+          callback(fit::error(fsys::Error::CANCELED));
           return;
         }
 
         if (!status || !session) {
           bt_log(TRACE, "bt-host", "failed to start BR/EDR discovery session");
-          callback(StatusToFidl(status, "Failed to start BR/EDR discovery session"));
+
+          fit::result<void, fsys::Error> result;
+          if (!status) {
+            result = StatusToFidl(status);
+          } else {
+            result = fit::error(fsys::Error::FAILED);
+          }
           self->requesting_discovery_ = false;
+          callback(std::move(result));
           return;
         }
 
@@ -268,19 +278,18 @@
       });
 }
 
-void HostServer::StopDiscovery(StopDiscoveryCallback callback) {
+void HostServer::StopDiscovery() {
   bt_log(TRACE, "bt-host", "StopDiscovery()");
-  if (!le_discovery_session_) {
-    bt_log(TRACE, "bt-host", "no active discovery session");
-    callback(NewFidlError(ErrorCode::BAD_STATE, "No discovery session in progress"));
-    return;
-  }
 
+  bool discovering = le_discovery_session_ || bredr_discovery_session_;
   bredr_discovery_session_ = nullptr;
   le_discovery_session_ = nullptr;
-  NotifyInfoChange();
 
-  callback(Status());
+  if (discovering) {
+    NotifyInfoChange();
+  } else {
+    bt_log(TRACE, "bt-host", "no active discovery session");
+  }
 }
 
 void HostServer::SetConnectable(bool connectable, SetConnectableCallback callback) {
@@ -288,7 +297,7 @@
 
   auto bredr_conn_manager = adapter()->bredr_connection_manager();
   if (!bredr_conn_manager) {
-    callback(NewFidlError(ErrorCode::NOT_SUPPORTED, "Connectable mode not available"));
+    callback(fit::error(fsys::Error::NOT_SUPPORTED));
     return;
   }
   bredr_conn_manager->SetConnectable(
@@ -401,44 +410,50 @@
   if (!discoverable) {
     bredr_discoverable_session_ = nullptr;
     NotifyInfoChange();
-    callback(Status());
+    callback(fit::ok());
     return;
   }
   if (discoverable && requesting_discoverable_) {
     bt_log(TRACE, "bt-host", "SetDiscoverable already in progress");
-    callback(NewFidlError(ErrorCode::IN_PROGRESS, "SetDiscoverable already in progress"));
+    callback(fit::error(fsys::Error::IN_PROGRESS));
     return;
   }
   requesting_discoverable_ = true;
   auto bredr_manager = adapter()->bredr_discovery_manager();
   if (!bredr_manager) {
-    callback(NewFidlError(ErrorCode::FAILED, "Discoverable mode not available"));
+    callback(fit::error(fsys::Error::FAILED));
     return;
   }
   bredr_manager->RequestDiscoverable(
       [self = weak_ptr_factory_.GetWeakPtr(), callback = std::move(callback)](
           bt::hci::Status status, auto session) {
         if (!self) {
-          callback(NewFidlError(ErrorCode::FAILED, "Adapter Shutdown"));
+          callback(fit::error(fsys::Error::FAILED));
           return;
         }
 
         if (!self->requesting_discoverable_) {
-          callback(NewFidlError(ErrorCode::CANCELED, "Request canceled"));
+          callback(fit::error(fsys::Error::CANCELED));
           return;
         }
 
         if (!status || !session) {
           bt_log(TRACE, "bt-host", "failed to set discoverable");
-          callback(StatusToFidl(status, "Failed to set discoverable"));
+          fit::result<void, fsys::Error> result;
+          if (!status) {
+            result = StatusToFidl(status);
+          } else {
+            result = fit::error(fsys::Error::FAILED);
+          }
           self->requesting_discoverable_ = false;
+          callback(std::move(result));
           return;
         }
 
         self->bredr_discoverable_session_ = std::move(session);
         self->requesting_discoverable_ = false;
         self->NotifyInfoChange();
-        callback(Status());
+        callback(fit::ok());
       });
 }
 
@@ -488,16 +503,12 @@
 // Attempt to connect to peer identified by |peer_id|. The peer must be
 // in our peer cache. We will attempt to connect technologies (LowEnergy,
 // Classic or Dual-Mode) as the peer claims to support when discovered
-void HostServer::Connect(::std::string peer_id, ConnectCallback callback) {
-  auto id = PeerIdFromString(peer_id);
-  if (!id.has_value()) {
-    callback(NewFidlError(ErrorCode::INVALID_ARGUMENTS, "invalid peer ID"));
-    return;
-  }
-  auto peer = adapter()->peer_cache()->FindById(*id);
+void HostServer::Connect(fbt::PeerId peer_id, ConnectCallback callback) {
+  bt::PeerId id{peer_id.value};
+  auto peer = adapter()->peer_cache()->FindById(id);
   if (!peer) {
     // We don't support connecting to peers that are not in our cache
-    callback(NewFidlError(ErrorCode::NOT_FOUND, "Cannot find peer with the given ID"));
+    callback(fit::error(fsys::Error::PEER_NOT_FOUND));
     return;
   }
 
@@ -505,35 +516,24 @@
   // LowEnergy we assume LE. If a dual-mode peer, we should attempt to connect
   // both protocols.
   if (!peer->le()) {
-    ConnectBrEdr(*id, std::move(callback));
+    ConnectBrEdr(id, std::move(callback));
     return;
   }
 
-  ConnectLowEnergy(*id, std::move(callback));
+  ConnectLowEnergy(id, std::move(callback));
 }
 
 // Attempt to disconnect the peer identified by |peer_id| from all transports.
 // If the peer is already not connected, return success. If the peer is
 // disconnected succesfully, return success.
-void HostServer::Disconnect(::std::string peer_id, DisconnectCallback callback) {
-  auto id = PeerIdFromString(peer_id);
-  if (!id.has_value()) {
-    callback(NewFidlError(ErrorCode::INVALID_ARGUMENTS, "invalid peer ID"));
-    return;
-  }
-
-  auto le_disc = adapter()->le_connection_manager()->Disconnect(*id);
-  auto bredr_disc = adapter()->bredr_connection_manager()->Disconnect(*id);
-
+void HostServer::Disconnect(fbt::PeerId peer_id, DisconnectCallback callback) {
+  bt::PeerId id{peer_id.value};
+  auto le_disc = adapter()->le_connection_manager()->Disconnect(id);
+  auto bredr_disc = adapter()->bredr_connection_manager()->Disconnect(id);
   if (le_disc && bredr_disc) {
-    callback(Status());
+    callback(fit::ok());
   } else {
-    if (le_disc)
-      callback(NewFidlError(ErrorCode::UNKNOWN, "Failed to disconnect from Br/Edr"));
-    else if (bredr_disc)
-      callback(NewFidlError(ErrorCode::UNKNOWN, "Failed to disconnect from LE"));
-    else
-      callback(NewFidlError(ErrorCode::UNKNOWN, "Failed to disconnect from both LE and Br/Edr"));
+    callback(fit::error(fsys::Error::FAILED));
   }
 }
 
@@ -542,8 +542,8 @@
   auto on_complete = [self, callback = std::move(callback), peer_id](auto status, auto connection) {
     if (!status) {
       ZX_ASSERT(!connection);
-      bt_log(TRACE, "bt-host", "failed to connect to peer (id %s)", bt_str(peer_id));
-      callback(StatusToFidl(status, "failed to connect"));
+      bt_log(TRACE, "bt-host", "failed to connect LE transport to peer (id %s)", bt_str(peer_id));
+      callback(fit::error(HostErrorToFidl(status.error())));
       return;
     }
 
@@ -551,13 +551,13 @@
     ZX_ASSERT(connection);
     ZX_ASSERT(peer_id == connection->peer_identifier());
 
-    callback(Status());
+    callback(fit::ok());
 
     if (self)
       self->RegisterLowEnergyConnection(std::move(connection), false);
   };
   if (!adapter()->le_connection_manager()->Connect(peer_id, std::move(on_complete))) {
-    callback(NewFidlError(ErrorCode::FAILED, "failed to connect"));
+    callback(fit::error(fsys::Error::FAILED));
   }
 }
 
@@ -567,8 +567,9 @@
   auto on_complete = [callback = std::move(callback), peer_id](auto status, auto connection) {
     if (!status) {
       ZX_ASSERT(!connection);
-      bt_log(TRACE, "bt-host", "failed to connect to peer (id %s)", bt_str(peer_id));
-      callback(StatusToFidl(status, "failed to connect"));
+      bt_log(TRACE, "bt-host", "failed to connect BR/EDR transport to peer (id %s)",
+             bt_str(peer_id));
+      callback(fit::error(HostErrorToFidl(status.error())));
       return;
     }
 
@@ -576,39 +577,35 @@
     ZX_ASSERT(connection);
     ZX_ASSERT(peer_id == connection->peer_id());
 
-    callback(Status());
+    callback(fit::ok());
   };
 
   if (!adapter()->bredr_connection_manager()->Connect(peer_id, std::move(on_complete))) {
-    callback(NewFidlError(ErrorCode::FAILED, "failed to connect"));
+    callback(fit::error(fsys::Error::FAILED));
   }
 }
 
-void HostServer::Forget(::std::string peer_id, ForgetCallback callback) {
-  auto id = PeerIdFromString(peer_id);
-  if (!id.has_value()) {
-    callback(NewFidlError(ErrorCode::INVALID_ARGUMENTS, "Invalid peer ID"));
-    return;
-  }
-  auto peer = adapter()->peer_cache()->FindById(*id);
+void HostServer::Forget(fbt::PeerId peer_id, ForgetCallback callback) {
+  bt::PeerId id{peer_id.value};
+  auto peer = adapter()->peer_cache()->FindById(id);
   if (!peer) {
-    bt_log(TRACE, "bt-host", "peer %s to forget wasn't found", peer_id.c_str());
-    callback(Status());
+    bt_log(TRACE, "bt-host", "peer %s to forget wasn't found", bt_str(id));
+    callback(fit::ok());
     return;
   }
 
-  const bool le_disconnected = adapter()->le_connection_manager()->Disconnect(*id);
-  const bool bredr_disconnected = adapter()->bredr_connection_manager()->Disconnect(*id);
-  const bool peer_removed = adapter()->peer_cache()->RemoveDisconnectedPeer(*id);
+  const bool le_disconnected = adapter()->le_connection_manager()->Disconnect(id);
+  const bool bredr_disconnected = adapter()->bredr_connection_manager()->Disconnect(id);
+  const bool peer_removed = adapter()->peer_cache()->RemoveDisconnectedPeer(id);
 
   if (!le_disconnected || !bredr_disconnected) {
     const auto message =
-        fxl::StringPrintf("Link(s) failed to close:%s%s", le_disconnected ? "" : " LE",
+        fxl::StringPrintf("link(s) failed to close:%s%s", le_disconnected ? "" : " LE",
                           bredr_disconnected ? "" : " BR/EDR");
-    callback(NewFidlError(ErrorCode::FAILED, message));
+    callback(fit::error(fsys::Error::FAILED));
   } else {
     ZX_ASSERT(peer_removed);
-    callback(Status());
+    callback(fit::ok());
   }
 }
 
@@ -618,7 +615,7 @@
   auto peer = adapter()->peer_cache()->FindById(peer_id);
   if (!peer) {
     // We don't support pairing to peers that are not in our cache
-    callback(NewFidlError(ErrorCode::NOT_FOUND, "Cannot find peer with the given ID"));
+    callback(fit::error(fsys::Error::PEER_NOT_FOUND));
     return;
   }
   // If options specifies a transport preference for LE or BR/EDR, we use that. Otherwise, we use
@@ -639,7 +636,7 @@
   if (options.has_le_security_level()) {
     security_level = SecurityLevelFromFidl(options.le_security_level());
     if (!security_level.has_value()) {
-      callback(NewFidlError(ErrorCode::INVALID_ARGUMENTS, "invalid security level"));
+      callback(fit::error(fsys::Error::INVALID_ARGUMENTS));
       return;
     }
   } else {
@@ -652,10 +649,10 @@
   auto on_complete = [peer_id, callback = std::move(callback)](bt::sm::Status status) {
     if (!status) {
       bt_log(WARN, "bt-host", "failed to pair to peer (id %s)", bt_str(peer_id));
-      callback(NewFidlError(fidl_helpers::HostErrorToFidl(status.error()), "Failed to pair"));
-      return;
+      callback(fit::error(HostErrorToFidl(status.error())));
+    } else {
+      callback(fit::ok());
     }
-    callback(Status());
   };
   adapter()->le_connection_manager()->Pair(peer_id, *security_level, bondable_mode,
                                            std::move(on_complete));
@@ -665,10 +662,10 @@
   auto on_complete = [peer_id, callback = std::move(callback)](bt::hci::Status status) {
     if (!status) {
       bt_log(WARN, "bt-host", "failed to pair to peer (id %s)", bt_str(peer_id));
-      callback(NewFidlError(fidl_helpers::HostErrorToFidl(status.error()), "Failed to pair"));
-      return;
+      callback(fit::error(HostErrorToFidl(status.error())));
+    } else {
+      callback(fit::ok());
     }
-    callback(Status());
   };
   adapter()->bredr_connection_manager()->Pair(peer_id, std::move(on_complete));
 }
@@ -746,7 +743,7 @@
 void HostServer::CompletePairing(PeerId id, bt::sm::Status status) {
   bt_log(TRACE, "bt-host", "pairing complete for peer: %s, status: %s", bt_str(id), bt_str(status));
   ZX_DEBUG_ASSERT(pairing_delegate_);
-  pairing_delegate_->OnPairingComplete(id.ToString(), StatusToFidl(status));
+  pairing_delegate_->OnPairingComplete(id.ToString(), StatusToFidlDeprecated(status));
 }
 
 void HostServer::ConfirmPairing(PeerId id, ConfirmCallback confirm) {
diff --git a/src/connectivity/bluetooth/core/bt-host/fidl/host_server.h b/src/connectivity/bluetooth/core/bt-host/fidl/host_server.h
index 77097b1..d4e5be9 100644
--- a/src/connectivity/bluetooth/core/bt-host/fidl/host_server.h
+++ b/src/connectivity/bluetooth/core/bt-host/fidl/host_server.h
@@ -89,7 +89,7 @@
                       SetDeviceClassCallback callback) override;
 
   void StartDiscovery(StartDiscoveryCallback callback) override;
-  void StopDiscovery(StopDiscoveryCallback callback) override;
+  void StopDiscovery() override;
   void SetConnectable(bool connectable, SetConnectableCallback callback) override;
   void SetDiscoverable(bool discoverable, SetDiscoverableCallback callback) override;
   void EnableBackgroundScan(bool enabled) override;
@@ -98,11 +98,11 @@
       ::fuchsia::bluetooth::control::InputCapabilityType input,
       ::fuchsia::bluetooth::control::OutputCapabilityType output,
       ::fidl::InterfaceHandle<::fuchsia::bluetooth::control::PairingDelegate> delegate) override;
-  void Connect(::std::string device_id, ConnectCallback callback) override;
-  void Disconnect(::std::string device_id, DisconnectCallback callback) override;
+  void Connect(::fuchsia::bluetooth::PeerId id, ConnectCallback callback) override;
+  void Disconnect(::fuchsia::bluetooth::PeerId id, DisconnectCallback callback) override;
   void Pair(::fuchsia::bluetooth::PeerId id, ::fuchsia::bluetooth::control::PairingOptions options,
             PairCallback callback) override;
-  void Forget(::std::string peer_id, ForgetCallback callback) override;
+  void Forget(::fuchsia::bluetooth::PeerId id, ForgetCallback callback) override;
 
   void RequestLowEnergyCentral(
       ::fidl::InterfaceRequest<fuchsia::bluetooth::le::Central> central) override;
diff --git a/src/connectivity/bluetooth/core/bt-host/fidl/host_server_unittest.cc b/src/connectivity/bluetooth/core/bt-host/fidl/host_server_unittest.cc
index 1456f59..a2d25dd 100644
--- a/src/connectivity/bluetooth/core/bt-host/fidl/host_server_unittest.cc
+++ b/src/connectivity/bluetooth/core/bt-host/fidl/host_server_unittest.cc
@@ -36,24 +36,15 @@
 using bt::UpperBits;
 using bt::l2cap::testing::FakeChannel;
 using bt::testing::FakePeer;
-using HostPairingDelegate = bt::gap::PairingDelegate;
-using fuchsia::bluetooth::control::InputCapabilityType;
-using fuchsia::bluetooth::control::OutputCapabilityType;
-using FidlErrorCode = fuchsia::bluetooth::ErrorCode;
-using FidlStatus = fuchsia::bluetooth::Status;
-using fuchsia::bluetooth::control::PairingOptions;
-using FidlPairingDelegate = fuchsia::bluetooth::control::PairingDelegate;
-using fuchsia::bluetooth::control::PairingSecurityLevel;
-
-using FidlRemoteDevice = fuchsia::bluetooth::control::RemoteDevice;
 
 namespace fbt = fuchsia::bluetooth;
+namespace fctrl = fuchsia::bluetooth::control;
 namespace fsys = fuchsia::bluetooth::sys;
 
 const bt::DeviceAddress kLeTestAddr(bt::DeviceAddress::Type::kLEPublic, {0x01, 0, 0, 0, 0, 0});
 const bt::DeviceAddress kBredrTestAddr(bt::DeviceAddress::Type::kBREDR, {0x01, 0, 0, 0, 0, 0});
 
-class MockPairingDelegate : public fuchsia::bluetooth::control::testing::PairingDelegate_TestBase {
+class MockPairingDelegate : public fctrl::testing::PairingDelegate_TestBase {
  public:
   MockPairingDelegate(fidl::InterfaceRequest<PairingDelegate> request,
                       async_dispatcher_t* dispatcher)
@@ -62,14 +53,12 @@
   ~MockPairingDelegate() override = default;
 
   MOCK_METHOD(void, OnPairingRequest,
-              (fuchsia::bluetooth::control::RemoteDevice device,
-               fuchsia::bluetooth::control::PairingMethod method, fidl::StringPtr displayed_passkey,
-               OnPairingRequestCallback callback),
+              (fctrl::RemoteDevice device, fctrl::PairingMethod method,
+               fidl::StringPtr displayed_passkey, OnPairingRequestCallback callback),
               (override));
   MOCK_METHOD(void, OnPairingComplete, (std::string device_id, fuchsia::bluetooth::Status status),
               (override));
-  MOCK_METHOD(void, OnRemoteKeypress,
-              (std::string device_id, fuchsia::bluetooth::control::PairingKeypressType keypress),
+  MOCK_METHOD(void, OnRemoteKeypress, (std::string device_id, fctrl::PairingKeypressType keypress),
               (override));
 
  private:
@@ -119,9 +108,9 @@
   // Create and bind a MockPairingDelegate and attach it to the HostServer under test. It is
   // heap-allocated to permit its explicit destruction.
   [[nodiscard]] std::unique_ptr<MockPairingDelegate> SetMockPairingDelegate(
-      InputCapabilityType input_capability, OutputCapabilityType output_capability) {
+      fctrl::InputCapabilityType input_capability, fctrl::OutputCapabilityType output_capability) {
     using ::testing::StrictMock;
-    fidl::InterfaceHandle<FidlPairingDelegate> pairing_delegate_handle;
+    fidl::InterfaceHandle<fctrl::PairingDelegate> pairing_delegate_handle;
     auto pairing_delegate = std::make_unique<StrictMock<MockPairingDelegate>>(
         pairing_delegate_handle.NewRequest(), dispatcher());
     host_client()->SetPairingDelegate(input_capability, output_capability,
@@ -143,15 +132,15 @@
 
     auto fake_peer = std::make_unique<FakePeer>(device_addr);
     test_device()->AddPeer(std::move(fake_peer));
-    // Initialize with error to ensure callback is called
-    FidlStatus connect_status =
-        fidl_helpers::NewFidlError(FidlErrorCode::BAD_STATE, "This should disappear");
-    host_client()->Connect(peer->identifier().ToString(), [&connect_status](FidlStatus cb_status) {
-      ASSERT_FALSE(cb_status.error);
-      connect_status = std::move(cb_status);
+
+    std::optional<fit::result<void, fsys::Error>> connect_result;
+    host_client()->Connect(fbt::PeerId{peer->identifier().value()}, [&](auto result) {
+      ASSERT_FALSE(result.is_err());
+      connect_result = std::move(result);
     });
     RunLoopUntilIdle();
-    if (connect_status.error != nullptr) {
+
+    if (!connect_result || connect_result->is_error()) {
       peer = nullptr;
       fake_chan = nullptr;
     }
@@ -168,13 +157,13 @@
 
 TEST_F(FIDL_HostServerTest, FidlIoCapabilitiesMapToHostIoCapability) {
   // Isolate HostServer's private bt::gap::PairingDelegate implementation.
-  auto host_pairing_delegate = static_cast<HostPairingDelegate*>(host_server());
+  auto host_pairing_delegate = static_cast<bt::gap::PairingDelegate*>(host_server());
 
   // Getter should be safe to call when no PairingDelegate assigned.
   EXPECT_EQ(bt::sm::IOCapability::kNoInputNoOutput, host_pairing_delegate->io_capability());
 
-  auto fidl_pairing_delegate =
-      SetMockPairingDelegate(InputCapabilityType::KEYBOARD, OutputCapabilityType::DISPLAY);
+  auto fidl_pairing_delegate = SetMockPairingDelegate(fctrl::InputCapabilityType::KEYBOARD,
+                                                      fctrl::OutputCapabilityType::DISPLAY);
   EXPECT_EQ(bt::sm::IOCapability::kKeyboardDisplay, host_pairing_delegate->io_capability());
 }
 
@@ -182,9 +171,9 @@
   using namespace ::testing;
 
   // Isolate HostServer's private bt::gap::PairingDelegate implementation.
-  auto host_pairing_delegate = static_cast<HostPairingDelegate*>(host_server());
-  auto fidl_pairing_delegate =
-      SetMockPairingDelegate(InputCapabilityType::KEYBOARD, OutputCapabilityType::DISPLAY);
+  auto host_pairing_delegate = static_cast<bt::gap::PairingDelegate*>(host_server());
+  auto fidl_pairing_delegate = SetMockPairingDelegate(fctrl::InputCapabilityType::KEYBOARD,
+                                                      fctrl::OutputCapabilityType::DISPLAY);
 
   // fuchsia::bluetooth::Status is move-only so check its value in a lambda.
   EXPECT_CALL(*fidl_pairing_delegate, OnPairingComplete(EndsWith("c0decafe"), _))
@@ -203,24 +192,24 @@
 
 TEST_F(FIDL_HostServerTest, HostConfirmPairingRequestsConsentPairingOverFidl) {
   using namespace ::testing;
-  auto host_pairing_delegate = static_cast<HostPairingDelegate*>(host_server());
-  auto fidl_pairing_delegate =
-      SetMockPairingDelegate(InputCapabilityType::KEYBOARD, OutputCapabilityType::DISPLAY);
+  auto host_pairing_delegate = static_cast<bt::gap::PairingDelegate*>(host_server());
+  auto fidl_pairing_delegate = SetMockPairingDelegate(fctrl::InputCapabilityType::KEYBOARD,
+                                                      fctrl::OutputCapabilityType::DISPLAY);
 
   auto* const peer = adapter()->peer_cache()->NewPeer(kLeTestAddr, /*connectable=*/true);
   ASSERT_TRUE(peer);
 
   EXPECT_CALL(*fidl_pairing_delegate,
-              OnPairingRequest(_, fuchsia::bluetooth::control::PairingMethod::CONSENT,
-                               fidl::StringPtr(nullptr), _))
-      .WillOnce([id = peer->identifier()](FidlRemoteDevice device, Unused, Unused,
-                                          FidlPairingDelegate::OnPairingRequestCallback callback) {
-        EXPECT_THAT(device.identifier, EndsWith(id.ToString()));
-        callback(/*accept=*/true, /*entered_passkey=*/nullptr);
-      });
+              OnPairingRequest(_, fctrl::PairingMethod::CONSENT, fidl::StringPtr(nullptr), _))
+      .WillOnce(
+          [id = peer->identifier()](fctrl::RemoteDevice device, Unused, Unused,
+                                    fctrl::PairingDelegate::OnPairingRequestCallback callback) {
+            EXPECT_THAT(device.identifier, EndsWith(id.ToString()));
+            callback(/*accept=*/true, /*entered_passkey=*/nullptr);
+          });
 
   bool confirm_cb_value = false;
-  HostPairingDelegate::ConfirmCallback confirm_cb = [&confirm_cb_value](bool confirmed) {
+  bt::gap::PairingDelegate::ConfirmCallback confirm_cb = [&confirm_cb_value](bool confirmed) {
     confirm_cb_value = confirmed;
   };
   host_pairing_delegate->ConfirmPairing(peer->identifier(), std::move(confirm_cb));
@@ -233,19 +222,19 @@
 TEST_F(FIDL_HostServerTest,
        HostDisplayPasskeyRequestsPasskeyDisplayOrNumericComparisonPairingOverFidl) {
   using namespace ::testing;
-  auto host_pairing_delegate = static_cast<HostPairingDelegate*>(host_server());
-  auto fidl_pairing_delegate =
-      SetMockPairingDelegate(InputCapabilityType::KEYBOARD, OutputCapabilityType::DISPLAY);
+  auto host_pairing_delegate = static_cast<bt::gap::PairingDelegate*>(host_server());
+  auto fidl_pairing_delegate = SetMockPairingDelegate(fctrl::InputCapabilityType::KEYBOARD,
+                                                      fctrl::OutputCapabilityType::DISPLAY);
 
   auto* const peer = adapter()->peer_cache()->NewPeer(kLeTestAddr, /*connectable=*/true);
   ASSERT_TRUE(peer);
 
   // This call should use PASSKEY_DISPLAY to request that the user perform peer passkey entry.
-  using fuchsia::bluetooth::control::PairingMethod;
-  using OnPairingRequestCallback = FidlPairingDelegate::OnPairingRequestCallback;
+  using fctrl::PairingMethod;
+  using OnPairingRequestCallback = fctrl::PairingDelegate::OnPairingRequestCallback;
   EXPECT_CALL(*fidl_pairing_delegate,
               OnPairingRequest(_, PairingMethod::PASSKEY_DISPLAY, fidl::StringPtr("012345"), _))
-      .WillOnce([id = peer->identifier()](FidlRemoteDevice device, Unused, Unused,
+      .WillOnce([id = peer->identifier()](fctrl::RemoteDevice device, Unused, Unused,
                                           OnPairingRequestCallback callback) {
         EXPECT_THAT(device.identifier, EndsWith(id.ToString()));
         callback(/*accept=*/false, /*entered_passkey=*/nullptr);
@@ -256,7 +245,7 @@
     EXPECT_FALSE(confirmed);
     confirm_cb_called = true;
   };
-  using DisplayMethod = HostPairingDelegate::DisplayMethod;
+  using DisplayMethod = bt::gap::PairingDelegate::DisplayMethod;
   host_pairing_delegate->DisplayPasskey(peer->identifier(), 12345, DisplayMethod::kPeerEntry,
                                         confirm_cb);
 
@@ -268,7 +257,7 @@
   // the local and peer devices.
   EXPECT_CALL(*fidl_pairing_delegate,
               OnPairingRequest(_, PairingMethod::PASSKEY_COMPARISON, fidl::StringPtr("012345"), _))
-      .WillOnce([id = peer->identifier()](FidlRemoteDevice device, Unused, Unused,
+      .WillOnce([id = peer->identifier()](fctrl::RemoteDevice device, Unused, Unused,
                                           OnPairingRequestCallback callback) {
         EXPECT_THAT(device.identifier, EndsWith(id.ToString()));
         callback(/*accept=*/false, /*entered_passkey=*/nullptr);
@@ -285,33 +274,32 @@
 
 TEST_F(FIDL_HostServerTest, HostRequestPasskeyRequestsPasskeyEntryPairingOverFidl) {
   using namespace ::testing;
-  auto host_pairing_delegate = static_cast<HostPairingDelegate*>(host_server());
-  auto fidl_pairing_delegate =
-      SetMockPairingDelegate(InputCapabilityType::KEYBOARD, OutputCapabilityType::DISPLAY);
+  auto host_pairing_delegate = static_cast<bt::gap::PairingDelegate*>(host_server());
+  auto fidl_pairing_delegate = SetMockPairingDelegate(fctrl::InputCapabilityType::KEYBOARD,
+                                                      fctrl::OutputCapabilityType::DISPLAY);
 
   auto* const peer = adapter()->peer_cache()->NewPeer(kLeTestAddr, /*connectable=*/true);
   ASSERT_TRUE(peer);
 
-  using OnPairingRequestCallback = FidlPairingDelegate::OnPairingRequestCallback;
+  using OnPairingRequestCallback = fctrl::PairingDelegate::OnPairingRequestCallback;
   EXPECT_CALL(*fidl_pairing_delegate,
-              OnPairingRequest(_, fuchsia::bluetooth::control::PairingMethod::PASSKEY_ENTRY,
-                               fidl::StringPtr(nullptr), _))
-      .WillOnce([id = peer->identifier()](FidlRemoteDevice device, Unused, Unused,
+              OnPairingRequest(_, fctrl::PairingMethod::PASSKEY_ENTRY, fidl::StringPtr(nullptr), _))
+      .WillOnce([id = peer->identifier()](fctrl::RemoteDevice device, Unused, Unused,
                                           OnPairingRequestCallback callback) {
         EXPECT_THAT(device.identifier, EndsWith(id.ToString()));
         callback(/*accept=*/false, "012345");
       })
-      .WillOnce([id = peer->identifier()](FidlRemoteDevice device, Unused, Unused,
+      .WillOnce([id = peer->identifier()](fctrl::RemoteDevice device, Unused, Unused,
                                           OnPairingRequestCallback callback) {
         EXPECT_THAT(device.identifier, EndsWith(id.ToString()));
         callback(/*accept=*/true, nullptr);
       })
-      .WillOnce([id = peer->identifier()](FidlRemoteDevice device, Unused, Unused,
+      .WillOnce([id = peer->identifier()](fctrl::RemoteDevice device, Unused, Unused,
                                           OnPairingRequestCallback callback) {
         EXPECT_THAT(device.identifier, EndsWith(id.ToString()));
         callback(/*accept=*/true, u8"🍂");
       })
-      .WillOnce([id = peer->identifier()](FidlRemoteDevice device, Unused, Unused,
+      .WillOnce([id = peer->identifier()](fctrl::RemoteDevice device, Unused, Unused,
                                           OnPairingRequestCallback callback) {
         EXPECT_THAT(device.identifier, EndsWith(id.ToString()));
         callback(/*accept=*/true, "012345");
@@ -387,7 +375,7 @@
   info.reset();
   host_server()->WatchState([&](auto value) { info = std::move(value); });
   EXPECT_FALSE(info.has_value());
-  host_server()->StopDiscovery([](auto) {});
+  host_server()->StopDiscovery();
   RunLoopUntilIdle();
   ASSERT_TRUE(info.has_value());
   ASSERT_TRUE(info->has_discovering());
@@ -449,13 +437,17 @@
     pairing_request_sent = true;
   };
   fake_chan->SetSendCallback(expect_default_bytebuffer, dispatcher());
-  FidlStatus pair_status;
-  PairingOptions opts;
-  host_client()->Pair(fuchsia::bluetooth::PeerId{.value = peer->identifier().value()},
-                      std::move(opts),
-                      [&pair_status](FidlStatus status) { pair_status = std::move(status); });
+
+  std::optional<fit::result<void, fsys::Error>> pair_result;
+  fctrl::PairingOptions opts;
+  host_client()->Pair(fbt::PeerId{peer->identifier().value()}, std::move(opts),
+                      [&](auto result) { pair_result = std::move(result); });
   RunLoopUntilIdle();
-  ASSERT_EQ(pair_status.error, nullptr);
+
+  // TODO(fxb/886): We don't have a good mechanism for driving pairing to completion without faking
+  // the entire SMP exchange. We should add SMP mocks that allows us to propagate a result up to the
+  // FIDL layer. For now we assert that pairing has started and remains pending.
+  ASSERT_FALSE(pair_result);  // Pairing request is pending
   ASSERT_TRUE(pairing_request_sent);
 }
 
@@ -487,14 +479,17 @@
   };
   fake_chan->SetSendCallback(expect_default_bytebuffer, dispatcher());
 
-  FidlStatus pair_status;
-  PairingOptions opts;
-  opts.set_le_security_level(PairingSecurityLevel::ENCRYPTED);
-  host_client()->Pair(fuchsia::bluetooth::PeerId{.value = peer->identifier().value()},
-                      std::move(opts),
-                      [&pair_status](FidlStatus status) { pair_status = std::move(status); });
+  std::optional<fit::result<void, fsys::Error>> pair_result;
+  fctrl::PairingOptions opts;
+  opts.set_le_security_level(fctrl::PairingSecurityLevel::ENCRYPTED);
+  host_client()->Pair(fbt::PeerId{peer->identifier().value()}, std::move(opts),
+                      [&](auto result) { pair_result = std::move(result); });
   RunLoopUntilIdle();
-  ASSERT_EQ(pair_status.error, nullptr);
+
+  // TODO(fxb/886): We don't have a good mechanism for driving pairing to completion without faking
+  // the entire SMP exchange. We should add SMP mocks that allows us to propagate a result up to the
+  // FIDL layer. For now we assert that pairing has started and remains pending.
+  ASSERT_FALSE(pair_result);  // Pairing request is pending
   ASSERT_TRUE(pairing_request_sent);
 }
 
@@ -526,14 +521,17 @@
   };
   fake_chan->SetSendCallback(expect_default_bytebuffer, dispatcher());
 
-  FidlStatus pair_status;
-  PairingOptions opts;
+  std::optional<fit::result<void, fsys::Error>> pair_result;
+  fctrl::PairingOptions opts;
   opts.set_non_bondable(true);
-  host_client()->Pair(fuchsia::bluetooth::PeerId{.value = peer->identifier().value()},
-                      std::move(opts),
-                      [&pair_status](FidlStatus status) { pair_status = std::move(status); });
+  host_client()->Pair(fbt::PeerId{peer->identifier().value()}, std::move(opts),
+                      [&](auto result) { pair_result = std::move(result); });
   RunLoopUntilIdle();
-  ASSERT_EQ(pair_status.error, nullptr);
+
+  // TODO(fxb/886): We don't have a good mechanism for driving pairing to completion without faking
+  // the entire SMP exchange. We should add SMP mocks that allows us to propagate a result up to the
+  // FIDL layer. For now we assert that pairing has started and remains pending.
+  ASSERT_FALSE(pair_result);  // Pairing request is pending
   ASSERT_TRUE(pairing_request_sent);
 }
 
@@ -543,18 +541,18 @@
   ASSERT_TRUE(fake_chan);
   ASSERT_EQ(bt::gap::Peer::ConnectionState::kConnected, peer->le()->connection_state());
 
-  FidlStatus pair_status;
-  PairingOptions opts;
+  std::optional<fit::result<void, fsys::Error>> pair_result;
+  fctrl::PairingOptions opts;
   // Set pairing option with classic
-  opts.set_transport(fuchsia::bluetooth::control::TechnologyType::CLASSIC);
-  auto pair_cb = [&pair_status](FidlStatus cb_status) {
-    ASSERT_TRUE(cb_status.error);
-    pair_status = std::move(cb_status);
+  opts.set_transport(fctrl::TechnologyType::CLASSIC);
+  auto pair_cb = [&](auto result) {
+    ASSERT_TRUE(result.is_err());
+    pair_result = std::move(result);
   };
-  host_client()->Pair(fuchsia::bluetooth::PeerId{.value = peer->identifier().value()},
-                      std::move(opts), pair_cb);
+  host_client()->Pair(fbt::PeerId{peer->identifier().value()}, std::move(opts), std::move(pair_cb));
   RunLoopUntilIdle();
-  ASSERT_EQ(pair_status.error->error_code, FidlErrorCode::NOT_FOUND);
+  ASSERT_TRUE(pair_result);
+  ASSERT_EQ(pair_result->error(), fsys::Error::PEER_NOT_FOUND);
 }
 
 TEST_F(FIDL_HostServerTest, WatchPeersHangsOnFirstCallWithNoExistingPeers) {
diff --git a/src/connectivity/bluetooth/core/bt-host/fidl/low_energy_central_server.cc b/src/connectivity/bluetooth/core/bt-host/fidl/low_energy_central_server.cc
index 83f69d8..ded63ba 100644
--- a/src/connectivity/bluetooth/core/bt-host/fidl/low_energy_central_server.cc
+++ b/src/connectivity/bluetooth/core/bt-host/fidl/low_energy_central_server.cc
@@ -167,7 +167,7 @@
     if (!status) {
       ZX_DEBUG_ASSERT(!conn_ref);
       bt_log(TRACE, "bt-host", "failed to connect to connect to peer (id %s)", bt_str(peer_id));
-      callback(fidl_helpers::StatusToFidl(status, "failed to connect"));
+      callback(fidl_helpers::StatusToFidlDeprecated(status, "failed to connect"));
       return;
     }
 
diff --git a/src/connectivity/bluetooth/core/bt-host/fidl/profile_server.cc b/src/connectivity/bluetooth/core/bt-host/fidl/profile_server.cc
index 7b82b81..04f2321 100644
--- a/src/connectivity/bluetooth/core/bt-host/fidl/profile_server.cc
+++ b/src/connectivity/bluetooth/core/bt-host/fidl/profile_server.cc
@@ -417,7 +417,7 @@
 
   registered_.emplace(next, handle);
   last_service_id_ = next;
-  callback(fidl_helpers::StatusToFidl(bt::sdp::Status()), next);
+  callback(fidl_helpers::StatusToFidlDeprecated(bt::sdp::Status()), next);
 }
 
 void ProfileServer::RemoveService(uint64_t service_id) {
@@ -465,7 +465,7 @@
   }
 
   auto connected_cb = [cb = callback.share()](auto chan_sock) {
-    cb(fidl_helpers::StatusToFidl(bt::sdp::Status()),
+    cb(fidl_helpers::StatusToFidlDeprecated(bt::sdp::Status()),
        ChannelSocketToFidlChannel(std::move(chan_sock)));
   };
   ZX_DEBUG_ASSERT(adapter());
diff --git a/src/connectivity/bluetooth/fidl/host.fidl b/src/connectivity/bluetooth/fidl/host.fidl
index e462d17..3d8b121 100644
--- a/src/connectivity/bluetooth/fidl/host.fidl
+++ b/src/connectivity/bluetooth/fidl/host.fidl
@@ -65,10 +65,10 @@
     WatchPeers() -> (vector<sys.Peer> updated, vector<bt.PeerId> removed);
 
     /// Sets the local name for this host device.
-    SetLocalName(string local_name) -> (fuchsia.bluetooth.Status status);
+    SetLocalName(string local_name) -> () error sys.Error;
 
     /// Sets the device class for this host device.
-    SetDeviceClass(bt.DeviceClass device_class) -> (fuchsia.bluetooth.Status status);
+    SetDeviceClass(bt.DeviceClass device_class) -> () error sys.Error;
 
     /// Initiates a general discovery procedure for BR/EDR and LE devices. On success, discovered
     /// peers can be monitored using the [`fuchsia.bluetooth.host/Host.WatchPeers`] method.
@@ -78,30 +78,30 @@
     /// Discovery will continue until it is terminated via StopDiscovery() or if the Host protocol
     /// channel gets closed. If the device does not support BR/EDR, only LE discovery will be
     /// performed.
-    StartDiscovery() -> (fuchsia.bluetooth.Status status);
+    StartDiscovery() -> () error sys.Error;
 
     /// Terminates discovery if one was started via StartDiscovery().
     ///
     /// NOTE: If another client is performing discovery (e.g. via its own le.Central interface handle),
     /// then the system will continue performing device discovery even if this method results in
     /// success.
-    StopDiscovery() -> (fuchsia.bluetooth.Status status);
+    StopDiscovery();
 
     /// Sets whether this host should be connectable.
-    SetConnectable(bool enabled) -> (fuchsia.bluetooth.Status status);
+    SetConnectable(bool enabled) -> () error sys.Error;
 
     /// Sets whether this host should be discoverable.
-    SetDiscoverable(bool enabled) -> (fuchsia.bluetooth.Status status);
+    SetDiscoverable(bool enabled) -> () error sys.Error;
 
-    /// Establish a BR/EDR and/or LE connection to the remote device with identifier `device_id`:
+    /// Establish a BR/EDR and/or LE connection to the peer with identifier `id`:
     ///
-    ///   - If the device is known to support the BR/EDR transport then a logical link over that
+    ///   - If the peer is known to support the BR/EDR transport then a logical link over that
     ///     transport will be established to the device. If the connection attempt is successful,
     ///     local services registered using "RequestProfile()" will be available to the peer.
     ///     Traditional services discovered on the peer will be notified to local services
     ///     asynchronously.
     ///
-    ///   - If the device is known to support the LE transport then a logical link over that
+    ///   - If the peer is known to support the LE transport then a logical link over that
     ///     transport will be established to the device. If the connection attempt is successful,
     ///     GATT services in the local database (populated via RequestGattServer()) will become
     ///     available to the peer. Similarly, remote GATT services that are discovered on the
@@ -111,15 +111,15 @@
     /// The result of the procedure will be communicated via `status`. If the remote device
     /// supports both BR/EDR and LE transports and a link cannot be established over both, then an
     /// error Status will be returned and neither transport will be connected.
-    Connect(string device_id) -> (fuchsia.bluetooth.Status status);
+    Connect(bt.PeerId id) -> () error sys.Error;
 
-    /// Terminate all connections (BR/EDR or LE) to the remote peer with identifier `peer_id`.
+    /// Terminate all connections (BR/EDR or LE) to the remote peer with identifier `id`.
     ///
-    /// + request `peer_id` The identifier of the peer to disconnect.
+    /// + request `id` The identifier of the peer to disconnect.
     /// - response `status` Contains an error if either LE or BR/EDR transport fails to disconnect. Contains
     ///                     success when both transports are successfully disconnected or if the peer is already
     ///                     disconnected.
-    Disconnect(string peer_id) -> (fuchsia.bluetooth.Status status);
+    Disconnect(bt.PeerId id) -> () error sys.Error;
 
     /// Initiates pairing to the peer with the supplied `id` and `options`. Returns an error
     /// variant of fuchsia.bluetooth.Status if no connected peer with `id` is found or the pairing
@@ -129,7 +129,7 @@
     /// NOTE: This is intended to satisfy test scenarios that require pairing procedures to be
     /// initiated without relying on service access. In normal operation, Bluetooth security is
     /// enforced during service access.
-    Pair(fuchsia.bluetooth.PeerId id, fuchsia.bluetooth.control.PairingOptions options) -> (fuchsia.bluetooth.Status status);
+    Pair(fuchsia.bluetooth.PeerId id, fuchsia.bluetooth.control.PairingOptions options) -> () error sys.Error;
 
     /// Deletes a peer from the Bluetooth host. If the peer is connected, it will be disconnected.
     /// `device_id` will no longer refer to any peer, even if a device with the same address is
@@ -138,7 +138,7 @@
     /// Returns success after no peer exists that's identified by `device_id` (even if it didn't
     /// exist before Forget), failure if the peer specified by `device_id` could not be
     /// disconnected or deleted and still exists.
-    Forget(string device_id) -> (fuchsia.bluetooth.Status status);
+    Forget(bt.PeerId id) -> () error sys.Error;
 
     /// Enable or disable a passive LE background scan. When enabled, the bt-host
     /// device will continuously perform a passive LE scan in the background when
diff --git a/src/connectivity/bluetooth/tests/integration/src/tests/host_driver.rs b/src/connectivity/bluetooth/tests/integration/src/tests/host_driver.rs
index 78798d6..dc3cfe8 100644
--- a/src/connectivity/bluetooth/tests/integration/src/tests/host_driver.rs
+++ b/src/connectivity/bluetooth/tests/integration/src/tests/host_driver.rs
@@ -4,9 +4,9 @@
 
 use {
     anyhow::{format_err, Context as _, Error},
-    fidl_fuchsia_bluetooth::{DeviceClass, MAJOR_DEVICE_CLASS_TOY},
+    fidl_fuchsia_bluetooth::{self as fbt, DeviceClass, MAJOR_DEVICE_CLASS_TOY},
     fidl_fuchsia_bluetooth_host::HostProxy,
-    fidl_fuchsia_bluetooth_sys::TechnologyType,
+    fidl_fuchsia_bluetooth_sys::{self as fsys, TechnologyType},
     fidl_fuchsia_bluetooth_test::{EmulatorSettings, HciError, PeerProxy},
     fuchsia_async as fasync,
     fuchsia_bluetooth::{
@@ -74,8 +74,7 @@
     let _ = harness
         .when_satisfied(emulator::expectation::local_name_is(NAME), timeout_duration())
         .await?;
-    let fut = expect_host_state(&harness, expectation::host_driver::name(NAME));
-    fut.await?;
+    expect_host_state(&harness, expectation::host_driver::name(NAME)).await?;
     Ok(())
 }
 
@@ -83,13 +82,14 @@
 // down to the controller.
 async fn test_set_local_name(harness: HostDriverHarness) -> Result<(), Error> {
     const NAME: &str = "test1234";
-    let fut = harness.aux().proxy().set_local_name(NAME);
-    fut.await?;
+    let proxy = harness.aux().proxy().clone();
+    let result = proxy.set_local_name(NAME).await?;
+    expect_eq!(Ok(()), result)?;
+
     let _ = harness
         .when_satisfied(emulator::expectation::local_name_is(NAME), timeout_duration())
         .await?;
-    let fut = expect_host_state(&harness, expectation::host_driver::name(NAME));
-    fut.await?;
+    expect_host_state(&harness, expectation::host_driver::name(NAME)).await?;
 
     Ok(())
 }
@@ -97,8 +97,10 @@
 // Tests that the device class assigned to a bt-host gets propagated down to the controller.
 async fn test_set_device_class(harness: HostDriverHarness) -> Result<(), Error> {
     let mut device_class = DeviceClass { value: MAJOR_DEVICE_CLASS_TOY + 4 };
-    let fut = harness.aux().proxy().set_device_class(&mut device_class);
-    fut.await?;
+    let proxy = harness.aux().proxy().clone();
+    let result = proxy.set_device_class(&mut device_class).await?;
+    expect_eq!(Ok(()), result)?;
+
     let _ = harness
         .when_satisfied(emulator::expectation::device_class_is(device_class), timeout_duration())
         .await?;
@@ -108,25 +110,37 @@
 // Tests that host state updates when discoverable mode is turned on.
 // TODO(armansito): Test for FakeHciDevice state changes.
 async fn test_discoverable(harness: HostDriverHarness) -> Result<(), Error> {
+    let proxy = harness.aux().proxy().clone();
+
+    // Disabling discoverable mode when not discoverable should succeed.
+    let result = proxy.set_discoverable(false).await?;
+    expect_eq!(Ok(()), result)?;
+
     // Enable discoverable mode.
-    let fut = harness.aux().proxy().set_discoverable(true);
-    fut.await?;
+    let result = proxy.set_discoverable(true).await?;
+    expect_eq!(Ok(()), result)?;
     expect_host_state(&harness, expectation::host_driver::discoverable(true)).await?;
 
     // Disable discoverable mode
-    let fut = harness.aux().proxy().set_discoverable(false);
-    fut.await?;
+    let result = proxy.set_discoverable(false).await?;
+    expect_eq!(Ok(()), result)?;
     expect_host_state(&harness, expectation::host_driver::discoverable(false)).await?;
 
+    // Disabling discoverable mode when not discoverable should succeed.
+    let result = proxy.set_discoverable(false).await?;
+    expect_eq!(Ok(()), result)?;
+
     Ok(())
 }
 
 // Tests that host state updates when discovery is started and stopped.
 // TODO(armansito): Test for FakeHciDevice state changes.
 async fn test_discovery(harness: HostDriverHarness) -> Result<(), Error> {
+    let proxy = harness.aux().proxy().clone();
+
     // Start discovery. "discovering" should get set to true.
-    let fut = harness.aux().proxy().start_discovery();
-    fut.await?;
+    let result = proxy.start_discovery().await?;
+    expect_eq!(Ok(()), result)?;
     expect_host_state(&harness, expectation::host_driver::discovering(true)).await?;
 
     let address = Address::Random([1, 0, 0, 0, 0, 0]);
@@ -137,8 +151,7 @@
     expect_peer(&harness, peer::name("Fake").and(peer::address(address))).await?;
 
     // Stop discovery. "discovering" should get set to false.
-    let fut = harness.aux().proxy().stop_discovery();
-    fut.await?;
+    let _ = proxy.stop_discovery()?;
     expect_host_state(&harness, expectation::host_driver::discovering(false)).await?;
 
     Ok(())
@@ -148,16 +161,18 @@
 // TODO(armansito): Test for FakeHciDevice state changes.
 async fn test_close(harness: HostDriverHarness) -> Result<(), Error> {
     // Enable all procedures.
-    let fut = harness.aux().proxy().start_discovery();
-    fut.await?;
-    let fut = harness.aux().proxy().set_discoverable(true);
-    fut.await?;
+    let proxy = harness.aux().proxy().clone();
+    let result = proxy.start_discovery().await?;
+    expect_eq!(Ok(()), result)?;
+    let result = proxy.set_discoverable(true).await?;
+    expect_eq!(Ok(()), result)?;
+
     let active_state = expectation::host_driver::discoverable(true)
         .and(expectation::host_driver::discovering(true));
     expect_host_state(&harness, active_state).await?;
 
     // Close should cancel these procedures.
-    harness.aux().proxy().close()?;
+    proxy.close()?;
 
     let closed_state_update = expectation::host_driver::discoverable(false)
         .and(expectation::host_driver::discovering(false));
@@ -191,7 +206,8 @@
 
     // Wait for all fake devices to be discovered.
     let proxy = harness.aux().proxy().clone();
-    let _ = proxy.start_discovery().await?;
+    let result = proxy.start_discovery().await?;
+    expect_eq!(Ok(()), result)?;
     let expected_le =
         peer::address(le_peer_address).and(peer::technology(TechnologyType::LowEnergy));
     let expected_bredr =
@@ -215,9 +231,11 @@
     // Configure `peer2` to return an error for the connection attempt.
     let _ = peer2.assign_connection_status(HciError::ConnectionTimeout).await?;
 
+    let proxy = harness.aux().proxy().clone();
+
     // Start discovery and let bt-host process the fake devices.
-    let fut = harness.aux().proxy().start_discovery();
-    fut.await?;
+    let result = proxy.start_discovery().await?;
+    expect_eq!(Ok(()), result)?;
 
     expect_peer(&harness, peer::address(address1)).await?;
     expect_peer(&harness, peer::address(address2)).await?;
@@ -230,24 +248,26 @@
         .iter()
         .find(|x| x.1.address == address1)
         .ok_or(format_err!("success peer not found"))?
-        .0;
+        .0
+        .clone();
     let failure_id = peers
         .iter()
         .find(|x| x.1.address == address2)
         .ok_or(format_err!("error peer not found"))?
-        .0;
+        .0
+        .clone();
+    let mut success_id: fbt::PeerId = success_id.into();
+    let mut failure_id: fbt::PeerId = failure_id.into();
 
     // Connecting to the failure peer should result in an error.
-    let fut = harness.aux().proxy().connect(&failure_id.to_string());
-    let status = fut.await?;
-    expect_true!(status.error.is_some())?;
+    let status = proxy.connect(&mut failure_id).await?;
+    expect_eq!(Err(fsys::Error::Failed), status)?;
 
     // Connecting to the success peer should return success and the peer should become connected.
-    let fut = harness.aux().proxy().connect(&success_id.to_string());
-    let status = fut.await?;
-    expect_true!(status.error.is_none())?;
+    let status = proxy.connect(&mut success_id).await?;
+    expect_eq!(Ok(()), status)?;
 
-    let connected = peer::identifier(*success_id).and(peer::connected(true));
+    let connected = peer::identifier(success_id.into()).and(peer::connected(true));
     expect_peer(&harness, connected).await?;
     Ok(())
 }
@@ -260,8 +280,10 @@
     let proxy = fut.await?;
 
     // Start discovery and let bt-host process the fake LE peer.
-    let fut = harness.aux().proxy().start_discovery();
-    fut.await?;
+    let host = harness.aux().proxy().clone();
+    let result = host.start_discovery().await?;
+    expect_eq!(Ok(()), result)?;
+
     let le_dev = expectation::peer::address(address.clone());
     expect_peer(&harness, le_dev).await?;
 
@@ -281,66 +303,62 @@
 
 /// Disconnecting from an unknown device should succeed
 async fn test_disconnect_unknown_device(harness: HostDriverHarness) -> Result<(), Error> {
-    let unknown_id = "0123401234";
-    let fut = harness.aux().proxy().disconnect(unknown_id);
+    let mut unknown_id = PeerId(0).into();
+    let fut = harness.aux().proxy().disconnect(&mut unknown_id);
     let status = fut.await?;
-    expect_eq!(status.error, None)
+    expect_eq!(Ok(()), status)
 }
 
 /// Disconnecting from a known, unconnected device should succeed
 async fn test_disconnect_unconnected_device(harness: HostDriverHarness) -> Result<(), Error> {
     let address = Address::Random([1, 0, 0, 0, 0, 0]);
-    let (success_dev, _proxy) = wait_for_test_peer(harness.clone(), &address).await?;
-    let fut = harness.aux().proxy().disconnect(&success_dev.to_string());
+    let (id, _proxy) = wait_for_test_peer(harness.clone(), &address).await?;
+    let mut id = id.into();
+    let fut = harness.aux().proxy().disconnect(&mut id);
     let status = fut.await?;
-    expect_eq!(status.error, None)
+    expect_eq!(Ok(()), status)
 }
 
 /// Disconnecting from a connected device should succeed and result in the device being disconnected
 async fn test_disconnect_connected_device(harness: HostDriverHarness) -> Result<(), Error> {
     let address = Address::Random([1, 0, 0, 0, 0, 0]);
-    let (success_dev, _proxy) = wait_for_test_peer(harness.clone(), &address).await?;
-    let success_dev = success_dev.to_string();
+    let (id, _proxy) = wait_for_test_peer(harness.clone(), &address).await?;
+    let mut id = id.into();
+    let proxy = harness.aux().proxy().clone();
 
-    let fut = harness.aux().proxy().connect(&success_dev);
-    let status = fut.await?;
-    expect_eq!(status.error, None)?;
+    let status = proxy.connect(&mut id).await?;
+    expect_eq!(Ok(()), status)?;
 
     let connected = peer::address(address).and(peer::connected(true));
     let disconnected = peer::address(address).and(peer::connected(false));
 
     let _ = expect_peer(&harness, connected).await?;
-    let fut = harness.aux().proxy().disconnect(&success_dev);
-    let status = fut.await?;
-    expect_eq!(status.error, None)?;
+    let status = proxy.disconnect(&mut id).await?;
+    expect_eq!(Ok(()), status)?;
+
     let _ = expect_peer(&harness, disconnected).await?;
     Ok(())
 }
 
 async fn test_forget(harness: HostDriverHarness) -> Result<(), Error> {
     let address = Address::Random([1, 0, 0, 0, 0, 0]);
-    let (le_peer, _proxy) = wait_for_test_peer(harness.clone(), &address).await?;
+    let (id, _proxy) = wait_for_test_peer(harness.clone(), &address).await?;
+    let mut id = id.into();
+    let proxy = harness.aux().proxy().clone();
 
-    // Start discovery and let bt-host process the fake peers.
-    let fut = harness.aux().proxy().start_discovery();
-    fut.await?;
-
-    // Wait for fake peer to be discovered.
+    // Wait for fake peer to be discovered (`wait_for_test_peer` starts discovery).
     let expected_peer = expectation::peer::address(address);
     expect_peer(&harness, expected_peer.clone()).await?;
 
     // Connecting to the peer should return success and the peer should become connected.
-    let fut = harness.aux().proxy().connect(&le_peer.to_string());
-    let mut status = fut.await?;
-    expect_true!(status.error.is_none())?;
-
+    let status = proxy.connect(&mut id).await?;
+    expect_eq!(Ok(()), status)?;
     expect_peer(&harness, expected_peer.and(expectation::peer::connected(true))).await?;
 
     // Forgetting the peer should result in its removal.
-    let fut = harness.aux().proxy().forget(&le_peer.to_string());
-    status = fut.await?;
-    expect_true!(status.error.is_none())?;
-    expect_no_peer(&harness, le_peer).await?;
+    let status = proxy.forget(&mut id).await?;
+    expect_eq!(Ok(()), status)?;
+    expect_no_peer(&harness, id.into()).await?;
 
     // TODO(BT-879): Test that the link closes by querying fake HCI.