diff --git a/system/bta/BUILD.gn b/system/bta/BUILD.gn
index ab7c327f5861e30294c1ad086447e4e18934ad2b..b8b32eaac57c980f457783274d5f3fc473585b11 100644
--- a/system/bta/BUILD.gn
+++ b/system/bta/BUILD.gn
@@ -117,7 +117,7 @@ static_library("bta") {
   ]
 
   deps = [
-    "//bt/gd/rust/shim:hci_bridge_header",
+    "//bt/gd/rust/shim:shim_bridge_header",
     "//bt/gd/rust/shim:init_flags_bridge_header",
     "//bt/gd/rust/shim:message_loop_thread_bridge_header",
   ]
diff --git a/system/build/Android.bp b/system/build/Android.bp
index 75a4d4f04d35c6cfe313ea3c96059c89997eec4a..2bd18c79a3fbc8346955742639ddb947beca30e0 100644
--- a/system/build/Android.bp
+++ b/system/build/Android.bp
@@ -81,7 +81,7 @@ fluoride_defaults {
     defaults: ["fluoride_types_defaults_fuzzable"],
     header_libs: ["libbluetooth_headers", "libbt_callbacks_cxx_headers"],
     generated_headers: [
-        "libbt_hci_bridge_header",
+        "libbt_shim_bridge_header",
         "libbt_message_loop_thread_bridge_header",
         "cxx-bridge-header"
     ],
diff --git a/system/gd/rust/common/src/sys_prop.rs b/system/gd/rust/common/src/sys_prop.rs
index db0779f4c17b216646b2f863c42dd96a82526cba..301311c9f02e6f8dd21c107a17093711bbda330e 100644
--- a/system/gd/rust/common/src/sys_prop.rs
+++ b/system/gd/rust/common/src/sys_prop.rs
@@ -1,14 +1,19 @@
 //! System properties on Android
 
 #[cfg(target_os = "android")]
-#[cxx::bridge(namespace = bluetooth::common::sys_prop)]
-mod ffi {
-    extern "C" {
-        include!("src/ffi/sys_prop.h");
-        fn get(name: &str) -> String;
+mod wrap {
+    #[cxx::bridge(namespace = bluetooth::common::sys_prop)]
+    pub mod ffi {
+        unsafe extern "C++" {
+            include!("src/ffi/sys_prop.h");
+            fn get(name: &str) -> String;
+        }
     }
 }
 
+#[cfg(target_os = "android")]
+use wrap::ffi;
+
 /// Gets the value of a system property on Android
 #[cfg(target_os = "android")]
 pub fn get(name: &str) -> Option<String> {
@@ -43,7 +48,7 @@ pub fn get_bool(name: &str) -> Option<bool> {
         match value.as_str() {
             "0" | "n" | "no" | "false" | "off" => Some(false),
             "1" | "y" | "yes" | "true" | "on" => Some(true),
-            _ => None
+            _ => None,
         }
     } else {
         None
diff --git a/system/gd/rust/hal/src/ffi/hidl.cc b/system/gd/rust/hal/src/ffi/hidl.cc
index 93bf94d2f1be6b779d385a0401c48b39e269da00..fd46babd757d4461fb4762c83d94a1fdfda15f1d 100644
--- a/system/gd/rust/hal/src/ffi/hidl.cc
+++ b/system/gd/rust/hal/src/ffi/hidl.cc
@@ -99,22 +99,22 @@ void stop_hal() {
   trampoline_ = nullptr;
 }
 
-void send_command(rust::Slice<uint8_t> data) {
+void send_command(rust::Slice<const uint8_t> data) {
   ASSERT(bt_hci_ != nullptr);
   bt_hci_->sendHciCommand(hidl_vec<uint8_t>(data.data(), data.data() + data.length()));
 }
 
-void send_acl(rust::Slice<uint8_t> data) {
+void send_acl(rust::Slice<const uint8_t> data) {
   ASSERT(bt_hci_ != nullptr);
   bt_hci_->sendAclData(hidl_vec<uint8_t>(data.data(), data.data() + data.length()));
 }
 
-void send_sco(rust::Slice<uint8_t> data) {
+void send_sco(rust::Slice<const uint8_t> data) {
   ASSERT(bt_hci_ != nullptr);
   bt_hci_->sendScoData(hidl_vec<uint8_t>(data.data(), data.data() + data.length()));
 }
 
-void send_iso(rust::Slice<uint8_t> data) {
+void send_iso(rust::Slice<const uint8_t> data) {
   if (bt_hci_1_1_ == nullptr) {
     LOG_ERROR("ISO is not supported in HAL v1.0");
     return;
diff --git a/system/gd/rust/hal/src/ffi/hidl.h b/system/gd/rust/hal/src/ffi/hidl.h
index df8a6860980573d9607ab46b5aed171bec1adeb8..20138f64c590c86c3324a24dc01f9286a27d2de6 100644
--- a/system/gd/rust/hal/src/ffi/hidl.h
+++ b/system/gd/rust/hal/src/ffi/hidl.h
@@ -6,10 +6,10 @@ namespace hal {
 
 void start_hal();
 void stop_hal();
-void send_command(rust::Slice<uint8_t> data);
-void send_acl(rust::Slice<uint8_t> data);
-void send_sco(rust::Slice<uint8_t> data);
-void send_iso(rust::Slice<uint8_t> data);
+void send_command(rust::Slice<const uint8_t> data);
+void send_acl(rust::Slice<const uint8_t> data);
+void send_sco(rust::Slice<const uint8_t> data);
+void send_iso(rust::Slice<const uint8_t> data);
 
 }  // namespace hal
 }  // namespace bluetooth
diff --git a/system/gd/rust/hal/src/hidl_hal.rs b/system/gd/rust/hal/src/hidl_hal.rs
index 35b2fded8f308f97cd2a03ba990bcfd079f8500c..ea815e697b7b161780e8daa335d5eadeca6a7904 100644
--- a/system/gd/rust/hal/src/hidl_hal.rs
+++ b/system/gd/rust/hal/src/hidl_hal.rs
@@ -34,8 +34,10 @@ async fn provide_hidl_hal(rt: Arc<Runtime>) -> RawHal {
 }
 
 #[cxx::bridge(namespace = bluetooth::hal)]
+// TODO Either use or remove these functions, this shouldn't be the long term state
+#[allow(dead_code)]
 mod ffi {
-    extern "C" {
+    unsafe extern "C++" {
         include!("src/ffi/hidl.h");
         fn start_hal();
         fn stop_hal();
diff --git a/system/gd/rust/shim/Android.bp b/system/gd/rust/shim/Android.bp
index 9887a6185e153f29501fd31303984f17e80bd21c..5506e1100f7d89ac0fc6a175fd5c3bbab4e10439 100644
--- a/system/gd/rust/shim/Android.bp
+++ b/system/gd/rust/shim/Android.bp
@@ -59,23 +59,17 @@ cc_library_static {
     generated_headers: [
         "libbt_init_flags_bridge_header",
         "libbt_shim_bridge_header",
-        "libbt_hci_bridge_header",
-        "libbt_controller_bridge_header",
         "libbt_message_loop_thread_bridge_header",
         "cxx-bridge-header",
     ],
     generated_sources: [
         "libbt_init_flags_bridge_code",
         "libbt_shim_bridge_code",
-        "libbt_hci_bridge_code",
-        "libbt_controller_bridge_code",
         "libbt_message_loop_thread_bridge_code",
     ],
     export_generated_headers: [
         "libbt_init_flags_bridge_header",
         "libbt_shim_bridge_header",
-        "libbt_hci_bridge_header",
-        "libbt_controller_bridge_header",
         "libbt_message_loop_thread_bridge_header",
         "cxx-bridge-header",
     ],
@@ -102,7 +96,7 @@ cc_library_static {
     defaults: ["gd_ffi_defaults"],
     header_libs: ["libbt_callbacks_cxx_headers"],
     srcs: ["callbacks/callbacks.cc"],
-    generated_headers: ["libbt_hci_bridge_header", "cxx-bridge-header"],
+    generated_headers: ["libbt_shim_bridge_header", "cxx-bridge-header"],
     shared_libs: [
         "libchrome",
     ],
@@ -119,16 +113,16 @@ genrule {
     name: "libbt_shim_bridge_header",
     tools: ["cxxbridge"],
     cmd: "$(location cxxbridge) $(in) --header > $(out)",
-    srcs: ["src/stack.rs"],
-    out: ["src/stack.rs.h"],
+    srcs: ["src/bridge.rs"],
+    out: ["src/bridge.rs.h"],
 }
 
 genrule {
     name: "libbt_shim_bridge_code",
     tools: ["cxxbridge"],
     cmd: "$(location cxxbridge) $(in) >> $(out)",
-    srcs: ["src/stack.rs"],
-    out: ["stack.cc"],
+    srcs: ["src/bridge.rs"],
+    out: ["bridge.cc"],
 }
 
 genrule {
@@ -147,22 +141,6 @@ genrule {
     out: ["init_flags.cc"],
 }
 
-genrule {
-    name: "libbt_hci_bridge_header",
-    tools: ["cxxbridge"],
-    cmd: "$(location cxxbridge) $(in) --header > $(out)",
-    srcs: ["src/hci.rs"],
-    out: ["src/hci.rs.h"],
-}
-
-genrule {
-    name: "libbt_hci_bridge_code",
-    tools: ["cxxbridge"],
-    cmd: "$(location cxxbridge) $(in) >> $(out)",
-    srcs: ["src/hci.rs"],
-    out: ["hci.cc"],
-}
-
 genrule {
     name: "libbt_message_loop_thread_bridge_header",
     tools: ["cxxbridge"],
@@ -178,19 +156,3 @@ genrule {
     srcs: ["src/message_loop_thread.rs"],
     out: ["message_loop_thread.cc"],
 }
-
-genrule {
-    name: "libbt_controller_bridge_header",
-    tools: ["cxxbridge"],
-    cmd: "$(location cxxbridge) $(in) --header > $(out)",
-    srcs: ["src/controller.rs"],
-    out: ["src/controller.rs.h"],
-}
-
-genrule {
-    name: "libbt_controller_bridge_code",
-    tools: ["cxxbridge"],
-    cmd: "$(location cxxbridge) $(in) >> $(out)",
-    srcs: ["src/controller.rs"],
-    out: ["controller.cc"],
-}
diff --git a/system/gd/rust/shim/BUILD.gn b/system/gd/rust/shim/BUILD.gn
index c0912e90e4b819440d0fb5f6eeeb6bb7fb16e16d..9402ac25905897060b7e0b61b583366d534ee58a 100644
--- a/system/gd/rust/shim/BUILD.gn
+++ b/system/gd/rust/shim/BUILD.gn
@@ -18,23 +18,16 @@ import("//common-mk/cxxbridge.gni")
 
 config("rust_shim_config") {
   include_dirs = [ "//bt/gd/rust/shim" ]
-
-  ldflags = [
-    # Opaque types in rust shim cause duplicate definitions of layout. Use the
-    # first defined symbol instead of aborting.
-    # i.e. T::layout::size() and T::layout::align()
-    "-Wl,--allow-multiple-definition",
-  ]
 }
 
 cxxbridge_header("shim_bridge_header") {
-  sources = [ "src/stack.rs" ]
+  sources = [ "src/bridge.rs" ]
   all_dependent_configs = [ ":rust_shim_config" ]
   deps = [ ":cxxlibheader" ]
 }
 
 cxxbridge_cc("shim_bridge_code") {
-  sources = [ "src/stack.rs" ]
+  sources = [ "src/bridge.rs" ]
   deps = [ ":shim_bridge_header" ]
   configs = [ "//bt/gd:gd_defaults" ]
 }
@@ -51,18 +44,6 @@ cxxbridge_cc("init_flags_bridge_code") {
   configs = [ "//bt/gd:gd_defaults" ]
 }
 
-cxxbridge_header("hci_bridge_header") {
-  sources = [ "src/hci.rs" ]
-  all_dependent_configs = [ ":rust_shim_config" ]
-  deps = [ ":cxxlibheader" ]
-}
-
-cxxbridge_cc("hci_bridge_code") {
-  sources = [ "src/hci.rs" ]
-  deps = [ ":hci_bridge_header" ]
-  configs = [ "//bt/gd:gd_defaults" ]
-}
-
 cxxbridge_header("message_loop_thread_bridge_header") {
   sources = [ "src/message_loop_thread.rs" ]
   all_dependent_configs = [ ":rust_shim_config" ]
@@ -75,27 +56,13 @@ cxxbridge_cc("message_loop_thread_bridge_code") {
   configs = [ "//bt/gd:gd_defaults" ]
 }
 
-cxxbridge_header("controller_bridge_header") {
-  sources = [ "src/controller.rs" ]
-  all_dependent_configs = [ ":rust_shim_config" ]
-  deps = [ ":cxxlibheader" ]
-}
-
-cxxbridge_cc("controller_bridge_code") {
-  sources = [ "src/controller.rs" ]
-  deps = [ ":controller_bridge_header" ]
-  configs = [ "//bt/gd:gd_defaults" ]
-}
-
 cxxbridge_libheader("cxxlibheader") {
   deps = []
 }
 
 static_library("libbluetooth_rust_interop") {
   deps = [
-    ":controller_bridge_code",
     ":cxxlibheader",
-    ":hci_bridge_code",
     ":init_flags_bridge_code",
     ":message_loop_thread_bridge_code",
     ":shim_bridge_code",
diff --git a/system/gd/rust/shim/callbacks/callbacks.h b/system/gd/rust/shim/callbacks/callbacks.h
index a8e3dd4f46aa0014d089393e1bec6800a6a961c1..98d9f81b6bc78dbba1245d3a54a050dff3f76760 100644
--- a/system/gd/rust/shim/callbacks/callbacks.h
+++ b/system/gd/rust/shim/callbacks/callbacks.h
@@ -63,11 +63,11 @@ class OnceClosure {
   base::OnceClosure* closure_;
 };
 
-using u8SliceCallback = TrampolineCallback<::rust::Slice<uint8_t>>;
-using u8SliceOnceCallback = TrampolineOnceCallback<::rust::Slice<uint8_t>>;
+using u8SliceCallback = TrampolineCallback<::rust::Slice<const uint8_t>>;
+using u8SliceOnceCallback = TrampolineOnceCallback<::rust::Slice<const uint8_t>>;
 
 }  // namespace rust
 }  // namespace shim
 }  // namespace bluetooth
 
-#include "src/hci.rs.h"
+#include "src/bridge.rs.h"
diff --git a/system/gd/rust/shim/src/bridge.rs b/system/gd/rust/shim/src/bridge.rs
new file mode 100644
index 0000000000000000000000000000000000000000..96b35cce3e8b13e760154e7f946381d50954879a
--- /dev/null
+++ b/system/gd/rust/shim/src/bridge.rs
@@ -0,0 +1,112 @@
+//! Merged bridge
+
+pub use crate::controller::*;
+pub use crate::hci::*;
+pub use crate::stack::*;
+
+#[cxx::bridge(namespace = bluetooth::shim::rust)]
+pub mod ffi {
+    extern "Rust" {
+        type Stack;
+        type Hci;
+        type Controller;
+
+        // Stack
+        fn stack_create() -> Box<Stack>;
+        fn stack_start(stack: &mut Stack);
+        fn stack_stop(stack: &mut Stack);
+
+        fn get_hci(stack: &mut Stack) -> Box<Hci>;
+        fn get_controller(stack: &mut Stack) -> Box<Controller>;
+
+        // HCI
+        fn hci_set_acl_callback(hci: &mut Hci, callback: UniquePtr<u8SliceCallback>);
+        fn hci_set_evt_callback(hci: &mut Hci, callback: UniquePtr<u8SliceCallback>);
+        fn hci_set_le_evt_callback(hci: &mut Hci, callback: UniquePtr<u8SliceCallback>);
+
+        fn hci_send_command(hci: &mut Hci, data: &[u8], callback: UniquePtr<u8SliceOnceCallback>);
+        fn hci_send_acl(hci: &mut Hci, data: &[u8]);
+        fn hci_register_event(hci: &mut Hci, event: u8);
+        fn hci_register_le_event(hci: &mut Hci, subevent: u8);
+
+        // Controller
+        fn controller_supports_simple_pairing(c: &Controller) -> bool;
+        fn controller_supports_secure_connections(c: &Controller) -> bool;
+        fn controller_supports_simultaneous_le_bredr(c: &Controller) -> bool;
+        fn controller_supports_interlaced_inquiry_scan(c: &Controller) -> bool;
+        fn controller_supports_rssi_with_inquiry_results(c: &Controller) -> bool;
+        fn controller_supports_extended_inquiry_response(c: &Controller) -> bool;
+        fn controller_supports_role_switch(c: &Controller) -> bool;
+        fn controller_supports_three_slot_packets(c: &Controller) -> bool;
+        fn controller_supports_five_slot_packets(c: &Controller) -> bool;
+        fn controller_supports_classic_2m_phy(c: &Controller) -> bool;
+        fn controller_supports_classic_3m_phy(c: &Controller) -> bool;
+        fn controller_supports_three_slot_edr_packets(c: &Controller) -> bool;
+        fn controller_supports_five_slot_edr_packets(c: &Controller) -> bool;
+        fn controller_supports_sco(c: &Controller) -> bool;
+        fn controller_supports_hv2_packets(c: &Controller) -> bool;
+        fn controller_supports_hv3_packets(c: &Controller) -> bool;
+        fn controller_supports_ev3_packets(c: &Controller) -> bool;
+        fn controller_supports_ev4_packets(c: &Controller) -> bool;
+        fn controller_supports_ev5_packets(c: &Controller) -> bool;
+        fn controller_supports_esco_2m_phy(c: &Controller) -> bool;
+        fn controller_supports_esco_3m_phy(c: &Controller) -> bool;
+        fn controller_supports_three_slot_esco_edr_packets(c: &Controller) -> bool;
+        fn controller_supports_hold_mode(c: &Controller) -> bool;
+        fn controller_supports_sniff_mode(c: &Controller) -> bool;
+        fn controller_supports_park_mode(c: &Controller) -> bool;
+        fn controller_supports_non_flushable_pb(c: &Controller) -> bool;
+        fn controller_supports_sniff_subrating(c: &Controller) -> bool;
+        fn controller_supports_encryption_pause(c: &Controller) -> bool;
+        fn controller_supports_ble(c: &Controller) -> bool;
+
+        fn controller_supports_privacy(c: &Controller) -> bool;
+        fn controller_supports_packet_extension(c: &Controller) -> bool;
+        fn controller_supports_connection_parameters_request(c: &Controller) -> bool;
+        fn controller_supports_ble_2m_phy(c: &Controller) -> bool;
+        fn controller_supports_ble_coded_phy(c: &Controller) -> bool;
+        fn controller_supports_extended_advertising(c: &Controller) -> bool;
+        fn controller_supports_periodic_advertising(c: &Controller) -> bool;
+        fn controller_supports_peripheral_initiated_feature_exchange(c: &Controller) -> bool;
+        fn controller_supports_connection_parameter_request(c: &Controller) -> bool;
+        fn controller_supports_periodic_advertising_sync_transfer_sender(c: &Controller) -> bool;
+        fn controller_supports_periodic_advertising_sync_transfer_recipient(c: &Controller)
+            -> bool;
+        fn controller_supports_connected_iso_stream_central(c: &Controller) -> bool;
+        fn controller_supports_connected_iso_stream_peripheral(c: &Controller) -> bool;
+        fn controller_supports_iso_broadcaster(c: &Controller) -> bool;
+        fn controller_supports_synchronized_receiver(c: &Controller) -> bool;
+
+        fn controller_supports_reading_remote_extended_features(c: &Controller) -> bool;
+        fn controller_supports_enhanced_setup_synchronous_connection(c: &Controller) -> bool;
+        fn controller_supports_enhanced_accept_synchronous_connection(c: &Controller) -> bool;
+        fn controller_supports_ble_set_privacy_mode(c: &Controller) -> bool;
+
+        fn controller_get_acl_buffer_length(c: &Controller) -> u16;
+        fn controller_get_le_buffer_length(c: &Controller) -> u16;
+        fn controller_get_iso_buffer_length(c: &Controller) -> u16;
+        fn controller_get_le_suggested_default_data_length(c: &Controller) -> u16;
+        fn controller_get_le_maximum_tx_data_length(c: &Controller) -> u16;
+        fn controller_get_le_max_advertising_data_length(c: &Controller) -> u16;
+        fn controller_get_le_supported_advertising_sets(c: &Controller) -> u8;
+        fn controller_get_le_periodic_advertiser_list_size(c: &Controller) -> u8;
+        fn controller_get_acl_buffers(c: &Controller) -> u16;
+        fn controller_get_le_buffers(c: &Controller) -> u8;
+        fn controller_get_iso_buffers(c: &Controller) -> u8;
+        fn controller_get_le_connect_list_size(c: &Controller) -> u8;
+        fn controller_get_le_resolving_list_size(c: &Controller) -> u8;
+        fn controller_get_le_supported_states(c: &Controller) -> u64;
+
+        fn controller_get_address(c: &Controller) -> String;
+    }
+
+    unsafe extern "C++" {
+        include!("callbacks/callbacks.h");
+
+        type u8SliceCallback;
+        fn Run(self: &u8SliceCallback, data: &[u8]);
+
+        type u8SliceOnceCallback;
+        fn Run(self: &u8SliceOnceCallback, data: &[u8]);
+    }
+}
diff --git a/system/gd/rust/shim/src/controller.rs b/system/gd/rust/shim/src/controller.rs
index 70036eba114e0e21180c3fbee26411a08ee1c468..fbb46e14c8aaebb838c4cd6db3a058c3cb38bee2 100644
--- a/system/gd/rust/shim/src/controller.rs
+++ b/system/gd/rust/shim/src/controller.rs
@@ -3,91 +3,23 @@
 use bt_hci::ControllerExports;
 use bt_packets::hci::OpCode;
 use paste::paste;
+use std::ops::Deref;
 use std::sync::Arc;
 
-#[cxx::bridge(namespace = bluetooth::shim::rust)]
-mod ffi {
-    extern "Rust" {
-        type Controller;
-
-        fn controller_supports_simple_pairing(c: &Controller) -> bool;
-        fn controller_supports_secure_connections(c: &Controller) -> bool;
-        fn controller_supports_simultaneous_le_bredr(c: &Controller) -> bool;
-        fn controller_supports_interlaced_inquiry_scan(c: &Controller) -> bool;
-        fn controller_supports_rssi_with_inquiry_results(c: &Controller) -> bool;
-        fn controller_supports_extended_inquiry_response(c: &Controller) -> bool;
-        fn controller_supports_role_switch(c: &Controller) -> bool;
-        fn controller_supports_three_slot_packets(c: &Controller) -> bool;
-        fn controller_supports_five_slot_packets(c: &Controller) -> bool;
-        fn controller_supports_classic_2m_phy(c: &Controller) -> bool;
-        fn controller_supports_classic_3m_phy(c: &Controller) -> bool;
-        fn controller_supports_three_slot_edr_packets(c: &Controller) -> bool;
-        fn controller_supports_five_slot_edr_packets(c: &Controller) -> bool;
-        fn controller_supports_sco(c: &Controller) -> bool;
-        fn controller_supports_hv2_packets(c: &Controller) -> bool;
-        fn controller_supports_hv3_packets(c: &Controller) -> bool;
-        fn controller_supports_ev3_packets(c: &Controller) -> bool;
-        fn controller_supports_ev4_packets(c: &Controller) -> bool;
-        fn controller_supports_ev5_packets(c: &Controller) -> bool;
-        fn controller_supports_esco_2m_phy(c: &Controller) -> bool;
-        fn controller_supports_esco_3m_phy(c: &Controller) -> bool;
-        fn controller_supports_three_slot_esco_edr_packets(c: &Controller) -> bool;
-        fn controller_supports_hold_mode(c: &Controller) -> bool;
-        fn controller_supports_sniff_mode(c: &Controller) -> bool;
-        fn controller_supports_park_mode(c: &Controller) -> bool;
-        fn controller_supports_non_flushable_pb(c: &Controller) -> bool;
-        fn controller_supports_sniff_subrating(c: &Controller) -> bool;
-        fn controller_supports_encryption_pause(c: &Controller) -> bool;
-        fn controller_supports_ble(c: &Controller) -> bool;
-
-        fn controller_supports_privacy(c: &Controller) -> bool;
-        fn controller_supports_packet_extension(c: &Controller) -> bool;
-        fn controller_supports_connection_parameters_request(c: &Controller) -> bool;
-        fn controller_supports_ble_2m_phy(c: &Controller) -> bool;
-        fn controller_supports_ble_coded_phy(c: &Controller) -> bool;
-        fn controller_supports_extended_advertising(c: &Controller) -> bool;
-        fn controller_supports_periodic_advertising(c: &Controller) -> bool;
-        fn controller_supports_peripheral_initiated_feature_exchange(c: &Controller) -> bool;
-        fn controller_supports_connection_parameter_request(c: &Controller) -> bool;
-        fn controller_supports_periodic_advertising_sync_transfer_sender(c: &Controller) -> bool;
-        fn controller_supports_periodic_advertising_sync_transfer_recipient(c: &Controller)
-            -> bool;
-        fn controller_supports_connected_iso_stream_central(c: &Controller) -> bool;
-        fn controller_supports_connected_iso_stream_peripheral(c: &Controller) -> bool;
-        fn controller_supports_iso_broadcaster(c: &Controller) -> bool;
-        fn controller_supports_synchronized_receiver(c: &Controller) -> bool;
-
-        fn controller_supports_reading_remote_extended_features(c: &Controller) -> bool;
-        fn controller_supports_enhanced_setup_synchronous_connection(c: &Controller) -> bool;
-        fn controller_supports_enhanced_accept_synchronous_connection(c: &Controller) -> bool;
-        fn controller_supports_ble_set_privacy_mode(c: &Controller) -> bool;
-
-        fn controller_get_acl_buffer_length(c: &Controller) -> u16;
-        fn controller_get_le_buffer_length(c: &Controller) -> u16;
-        fn controller_get_iso_buffer_length(c: &Controller) -> u16;
-        fn controller_get_le_suggested_default_data_length(c: &Controller) -> u16;
-        fn controller_get_le_maximum_tx_data_length(c: &Controller) -> u16;
-        fn controller_get_le_max_advertising_data_length(c: &Controller) -> u16;
-        fn controller_get_le_supported_advertising_sets(c: &Controller) -> u8;
-        fn controller_get_le_periodic_advertiser_list_size(c: &Controller) -> u8;
-        fn controller_get_acl_buffers(c: &Controller) -> u16;
-        fn controller_get_le_buffers(c: &Controller) -> u8;
-        fn controller_get_iso_buffers(c: &Controller) -> u8;
-        fn controller_get_le_connect_list_size(c: &Controller) -> u8;
-        fn controller_get_le_resolving_list_size(c: &Controller) -> u8;
-        fn controller_get_le_supported_states(c: &Controller) -> u64;
-
-        fn controller_get_address(c: &Controller) -> String;
+#[derive(Clone)]
+pub struct Controller(pub Arc<ControllerExports>);
+impl Deref for Controller {
+    type Target = Arc<ControllerExports>;
+    fn deref(&self) -> &Self::Target {
+        &self.0
     }
 }
 
-pub type Controller = Arc<ControllerExports>;
-
 macro_rules! feature_getters {
     ($($id:ident),*) => {
         paste! {
             $(
-                fn [<controller_supports_ $id>](c: &Controller) -> bool {
+                pub fn [<controller_supports_ $id>](c: &Controller) -> bool {
                     c.features.$id
                 }
             )*
@@ -131,7 +63,7 @@ macro_rules! le_feature_getters {
     ($($id:ident),*) => {
         paste! {
             $(
-                fn [<controller_supports_ $id>](c: &Controller) -> bool {
+                pub fn [<controller_supports_ $id>](c: &Controller) -> bool {
                     c.le_features.$id
                 }
             )*
@@ -161,7 +93,7 @@ macro_rules! opcode_getters {
     ($($id:ident => $opcode:path),*) => {
         paste! {
             $(
-                fn [<controller_supports_ $id>](c: &Controller) -> bool {
+                pub fn [<controller_supports_ $id>](c: &Controller) -> bool {
                     c.commands.is_supported($opcode)
                 }
             )*
@@ -180,7 +112,7 @@ macro_rules! field_getters {
     ($($id:ident : $type:ty),*) => {
         paste! {
             $(
-                fn [<controller_get_ $id>](c: &Controller) -> $type {
+                pub fn [<controller_get_ $id>](c: &Controller) -> $type {
                     c.$id
                 }
             )*
@@ -204,10 +136,10 @@ field_getters! {
     le_supported_states: u64
 }
 
-fn controller_get_le_maximum_tx_data_length(c: &Controller) -> u16 {
+pub fn controller_get_le_maximum_tx_data_length(c: &Controller) -> u16 {
     c.le_max_data_length.supported_max_tx_octets
 }
 
-fn controller_get_address(c: &Controller) -> String {
+pub fn controller_get_address(c: &Controller) -> String {
     c.address.to_string()
 }
diff --git a/system/gd/rust/shim/src/hci.rs b/system/gd/rust/shim/src/hci.rs
index 902a3f933db76f68fca2e1763694af7d12182bef..e0764c8a17b527568a9e6e250367bde5dbfb2ed7 100644
--- a/system/gd/rust/shim/src/hci.rs
+++ b/system/gd/rust/shim/src/hci.rs
@@ -1,37 +1,12 @@
 //! Hci shim
 
+use crate::bridge::ffi;
 use bt_facade_helpers::U8SliceRunnable;
 use bt_hci::facade::HciFacadeService;
 use bt_packets::hci::{AclPacket, CommandPacket, Packet};
 use std::sync::Arc;
 use tokio::runtime::Runtime;
 
-#[cxx::bridge(namespace = bluetooth::shim::rust)]
-mod ffi {
-    extern "C" {
-        include!("callbacks/callbacks.h");
-
-        type u8SliceCallback;
-        fn Run(self: &u8SliceCallback, data: &[u8]);
-
-        type u8SliceOnceCallback;
-        fn Run(self: &u8SliceOnceCallback, data: &[u8]);
-    }
-
-    extern "Rust" {
-        type Hci;
-
-        fn hci_set_acl_callback(hci: &mut Hci, callback: UniquePtr<u8SliceCallback>);
-        fn hci_set_evt_callback(hci: &mut Hci, callback: UniquePtr<u8SliceCallback>);
-        fn hci_set_le_evt_callback(hci: &mut Hci, callback: UniquePtr<u8SliceCallback>);
-
-        fn hci_send_command(hci: &mut Hci, data: &[u8], callback: UniquePtr<u8SliceOnceCallback>);
-        fn hci_send_acl(hci: &mut Hci, data: &[u8]);
-        fn hci_register_event(hci: &mut Hci, event: u8);
-        fn hci_register_le_event(hci: &mut Hci, subevent: u8);
-    }
-}
-
 // we take ownership when we get the callbacks
 unsafe impl Send for ffi::u8SliceCallback {}
 unsafe impl Send for ffi::u8SliceOnceCallback {}
diff --git a/system/gd/rust/shim/src/lib.rs b/system/gd/rust/shim/src/lib.rs
index afe4e45e74a6a5675799e0516cda61fdda3e4f91..a87fb258d796d1c1006babbf0b39c406a9ce8660 100644
--- a/system/gd/rust/shim/src/lib.rs
+++ b/system/gd/rust/shim/src/lib.rs
@@ -2,6 +2,7 @@
 #[macro_use]
 extern crate lazy_static;
 
+mod bridge;
 mod controller;
 mod hci;
 mod init_flags;
diff --git a/system/gd/rust/shim/src/message_loop_thread.rs b/system/gd/rust/shim/src/message_loop_thread.rs
index df32e6d12ce57edbb6835023ff7ba17cd3e9f9cd..107c85510d7b7dce25e37b7751dab4b75f40f0b0 100644
--- a/system/gd/rust/shim/src/message_loop_thread.rs
+++ b/system/gd/rust/shim/src/message_loop_thread.rs
@@ -9,7 +9,7 @@ use tokio::sync::mpsc::{unbounded_channel, UnboundedSender};
 
 #[cxx::bridge(namespace = bluetooth::shim::rust)]
 mod ffi {
-    extern "C" {
+    unsafe extern "C++" {
         include!("callbacks/callbacks.h");
 
         type OnceClosure;
diff --git a/system/gd/rust/shim/src/stack.rs b/system/gd/rust/shim/src/stack.rs
index 2dc77d2ce20eed87f520416720a4dfc526f13fe3..5c19ca4865059d90862abe52ad55464016332c77 100644
--- a/system/gd/rust/shim/src/stack.rs
+++ b/system/gd/rust/shim/src/stack.rs
@@ -3,10 +3,26 @@
 use crate::controller::Controller;
 use crate::hci::Hci;
 use bt_common::init_flags;
-use bt_main::Stack;
+use bt_hci::ControllerExports;
+use std::ops::{Deref, DerefMut};
 use std::sync::Arc;
 use tokio::runtime::{Builder, Runtime};
 
+pub struct Stack(bt_main::Stack);
+
+impl Deref for Stack {
+    type Target = bt_main::Stack;
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl DerefMut for Stack {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+
 lazy_static! {
     pub static ref RUNTIME: Arc<Runtime> = Arc::new(
         Builder::new_multi_thread()
@@ -18,34 +34,15 @@ lazy_static! {
     );
 }
 
-#[cxx::bridge(namespace = bluetooth::shim::rust)]
-mod ffi {
-    extern "Rust" {
-        // TODO(abps) - https://github.com/dtolnay/cxx/issues/496
-        // Externing non-local types results in E0117 errors and that breaks
-        // building with CXX > 1.0.
-        type Stack;
-        type Hci;
-        type Controller;
-
-        fn stack_create() -> Box<Stack>;
-        fn stack_start(stack: &mut Stack);
-        fn stack_stop(stack: &mut Stack);
-
-        fn get_hci(stack: &mut Stack) -> Box<Hci>;
-        fn get_controller(stack: &mut Stack) -> Box<Controller>;
-    }
-}
-
 pub fn stack_create() -> Box<Stack> {
     assert!(init_flags::gd_rust_is_enabled());
 
     let local_rt = RUNTIME.clone();
     RUNTIME.block_on(async move {
-        let stack = Stack::new(local_rt).await;
+        let stack = bt_main::Stack::new(local_rt).await;
         stack.use_default_snoop().await;
 
-        Box::new(stack)
+        Box::new(Stack(stack))
     })
 }
 
@@ -73,5 +70,5 @@ pub fn get_controller(stack: &mut Stack) -> Box<Controller> {
     assert!(init_flags::gd_rust_is_enabled());
     assert!(init_flags::gd_controller_is_enabled());
 
-    Box::new(stack.get_blocking::<Controller>())
+    Box::new(Controller(stack.get_blocking::<Arc<ControllerExports>>()))
 }
diff --git a/system/main/shim/controller.cc b/system/main/shim/controller.cc
index 487f06c6b676836156002b4a464b5772ddffa649..91dcee4e313de0ced1af23d7d786b10295df2757 100644
--- a/system/main/shim/controller.cc
+++ b/system/main/shim/controller.cc
@@ -26,7 +26,7 @@
 #include "osi/include/log.h"
 
 #include "hci/controller.h"
-#include "src/controller.rs.h"
+#include "src/bridge.rs.h"
 
 using ::bluetooth::common::init_flags::gd_rust_is_enabled;
 using ::bluetooth::shim::GetController;
diff --git a/system/main/shim/hci_layer.cc b/system/main/shim/hci_layer.cc
index bca7e3b7ae5dfe29276e3cf46e92ca9a1833004b..a45551fa29f6e83441d373da0823067777aa6de0 100644
--- a/system/main/shim/hci_layer.cc
+++ b/system/main/shim/hci_layer.cc
@@ -34,7 +34,7 @@
 #include "osi/include/allocator.h"
 #include "osi/include/future.h"
 #include "packet/raw_builder.h"
-#include "src/hci.rs.h"
+#include "src/bridge.rs.h"
 #include "stack/include/bt_types.h"
 
 /**
@@ -394,7 +394,7 @@ static void transmit_command(BT_HDR* command,
   }
 }
 
-static void transmit_fragment(uint8_t* stream, size_t length) {
+static void transmit_fragment(const uint8_t* stream, size_t length) {
   uint16_t handle_with_flags;
   STREAM_TO_UINT16(handle_with_flags, stream);
   auto pb_flag = static_cast<bluetooth::hci::PacketBoundaryFlag>(
@@ -501,7 +501,7 @@ using bluetooth::shim::rust::u8SliceCallback;
 using bluetooth::shim::rust::u8SliceOnceCallback;
 
 static BT_HDR* WrapRustPacketAndCopy(uint16_t event,
-                                     ::rust::Slice<uint8_t>* data) {
+                                     ::rust::Slice<const uint8_t>* data) {
   size_t packet_size = data->length() + kBtHdrSize;
   BT_HDR* packet = reinterpret_cast<BT_HDR*>(osi_malloc(packet_size));
   packet->offset = 0;
@@ -512,7 +512,7 @@ static BT_HDR* WrapRustPacketAndCopy(uint16_t event,
   return packet;
 }
 
-static void on_acl(::rust::Slice<uint8_t> data) {
+static void on_acl(::rust::Slice<const uint8_t> data) {
   if (!send_data_upwards) {
     return;
   }
@@ -520,7 +520,7 @@ static void on_acl(::rust::Slice<uint8_t> data) {
   packet_fragmenter->reassemble_and_dispatch(legacy_data);
 }
 
-static void on_event(::rust::Slice<uint8_t> data) {
+static void on_event(::rust::Slice<const uint8_t> data) {
   if (!send_data_upwards) {
     return;
   }
@@ -530,7 +530,7 @@ static void on_event(::rust::Slice<uint8_t> data) {
 
 void OnRustTransmitPacketCommandComplete(command_complete_cb complete_callback,
                                          void* context,
-                                         ::rust::Slice<uint8_t> data) {
+                                         ::rust::Slice<const uint8_t> data) {
   BT_HDR* response = WrapRustPacketAndCopy(MSG_HC_TO_STACK_HCI_EVT, &data);
   complete_callback(response, context);
 }
@@ -538,7 +538,7 @@ void OnRustTransmitPacketCommandComplete(command_complete_cb complete_callback,
 void OnRustTransmitPacketStatus(command_status_cb status_callback,
                                 void* context,
                                 std::unique_ptr<OsiObject> command,
-                                ::rust::Slice<uint8_t> data) {
+                                ::rust::Slice<const uint8_t> data) {
   ASSERT(data.length() >= 3);
   uint8_t status = data.data()[2];
   status_callback(status, static_cast<BT_HDR*>(command->Release()), context);
@@ -548,7 +548,7 @@ static void transmit_command(BT_HDR* command,
                              command_complete_cb complete_callback,
                              command_status_cb status_callback, void* context) {
   CHECK(command != nullptr);
-  uint8_t* data = command->data + command->offset;
+  const uint8_t* data = command->data + command->offset;
   size_t len = command->len;
   CHECK(len >= (kCommandOpcodeSize + kCommandLengthSize));
 
@@ -576,7 +576,7 @@ static void transmit_command(BT_HDR* command,
   }
 }
 
-static void transmit_fragment(uint8_t* stream, size_t length) {
+static void transmit_fragment(const uint8_t* stream, size_t length) {
   bluetooth::shim::rust::hci_send_acl(
       **bluetooth::shim::Stack::Stack::GetInstance()->GetRustHci(),
       ::rust::Slice(stream, length));
@@ -654,7 +654,7 @@ static void transmit_fragment(BT_HDR* packet, bool send_transmit_finished) {
       (packet->event & MSG_EVT_MASK) != MSG_STACK_TO_HC_HCI_CMD &&
       send_transmit_finished;
 
-  uint8_t* stream = packet->data + packet->offset;
+  const uint8_t* stream = packet->data + packet->offset;
   size_t length = packet->len;
   if (bluetooth::common::init_flags::gd_rust_is_enabled()) {
     rust::transmit_fragment(stream, length);
diff --git a/system/main/shim/stack.h b/system/main/shim/stack.h
index a981e632c97b85c790132c57b422cc90fffd684a..948d13bc891bb444093a213663d4742031d449a7 100644
--- a/system/main/shim/stack.h
+++ b/system/main/shim/stack.h
@@ -27,7 +27,7 @@
 #include "gd/os/thread.h"
 #include "gd/os/utils.h"
 #include "gd/stack_manager.h"
-#include "src/stack.rs.h"
+#include "src/bridge.rs.h"
 
 // The shim layer implementation on the Gd stack side.
 namespace bluetooth {