diff --git a/system/test/rootcanal/bluetooth_hci.cc b/system/test/rootcanal/bluetooth_hci.cc
index 7ceff6720c1bc331c3ef237a6cb0fd973720cfdd..0df9bd830fe490ee168b88340ff7648c493167cd 100644
--- a/system/test/rootcanal/bluetooth_hci.cc
+++ b/system/test/rootcanal/bluetooth_hci.cc
@@ -25,8 +25,9 @@
 
 #include "hci_internals.h"
 #include "log/log.h"
-#include "model/devices/hci_socket_device.h"
+#include "model/devices/hci_device.h"
 #include "model/devices/link_layer_socket_device.h"
+#include "model/hci/hci_socket_transport.h"
 
 namespace android {
 namespace hardware {
@@ -37,7 +38,8 @@ namespace sim {
 using android::hardware::hidl_vec;
 using rootcanal::AsyncTaskId;
 using rootcanal::DualModeController;
-using rootcanal::HciSocketDevice;
+using rootcanal::HciDevice;
+using rootcanal::HciSocketTransport;
 using rootcanal::LinkLayerSocketDevice;
 using rootcanal::TaskCallback;
 
@@ -205,7 +207,8 @@ Return<void> BluetoothHci::initialize_impl(
     SetUpTestChannel();
     SetUpHciServer([this](std::shared_ptr<AsyncDataChannel> socket,
                           AsyncDataChannelServer* srv) {
-      test_model_.AddHciConnection(HciSocketDevice::Create(socket, ""));
+      auto transport = HciSocketTransport::Create(socket);
+      test_model_.AddHciConnection(HciDevice::Create(transport, ""));
       srv->StartListening();
     });
     SetUpLinkLayerServer([this](std::shared_ptr<AsyncDataChannel> socket,
diff --git a/tools/rootcanal/Android.bp b/tools/rootcanal/Android.bp
index d2d893a7c072c5efec911b162e6846d0beaf8475..66e846f1aa2fec9cae425b31e236153ef17310b7 100644
--- a/tools/rootcanal/Android.bp
+++ b/tools/rootcanal/Android.bp
@@ -80,13 +80,14 @@ cc_library_static {
         "model/controller/security_manager.cc",
         "model/devices/device.cc",
         "model/devices/device_properties.cc",
-        "model/devices/h4_data_channel_packetizer.cc",
-        "model/devices/h4_packetizer.cc",
-        "model/devices/h4_parser.cc",
-        "model/devices/hci_protocol.cc",
-        "model/devices/hci_socket_device.cc",
+        "model/devices/hci_device.cc",
         "model/devices/link_layer_socket_device.cc",
         "model/devices/remote_loopback_device.cc",
+        "model/hci/h4_data_channel_packetizer.cc",
+        "model/hci/h4_packetizer.cc",
+        "model/hci/h4_parser.cc",
+        "model/hci/hci_protocol.cc",
+        "model/hci/hci_socket_transport.cc",
         "model/setup/async_manager.cc",
         "model/setup/phy_layer_factory.cc",
         "model/setup/test_channel_transport.cc",
@@ -230,9 +231,9 @@ cc_library_static {
         "gd_defaults",
     ],
     srcs: [
-        "model/devices/h4_packetizer.cc",
-        "model/devices/h4_parser.cc",
-        "model/devices/hci_protocol.cc",
+        "model/hci/h4_packetizer.cc",
+        "model/hci/h4_parser.cc",
+        "model/hci/hci_protocol.cc",
     ],
 
     local_include_dirs: [
diff --git a/tools/rootcanal/desktop/test_environment.cc b/tools/rootcanal/desktop/test_environment.cc
index 530abab5a58bd0874196eb0b42351b49deb04bec..261dc43e33cb331924452e430019e63360d02999 100644
--- a/tools/rootcanal/desktop/test_environment.cc
+++ b/tools/rootcanal/desktop/test_environment.cc
@@ -20,8 +20,8 @@
 #include <utility>      // for move
 #include <vector>       // for vector
 
-#include "model/devices/hci_socket_device.h"         // for HciSocketDevice
 #include "model/devices/link_layer_socket_device.h"  // for LinkLayerSocketDevice
+#include "model/hci/hci_socket_transport.h"          // for HciSocketTransport
 #include "net/async_data_channel.h"                  // for AsyncDataChannel
 #include "net/async_data_channel_connector.h"  // for AsyncDataChannelConnector
 #include "os/log.h"  // for LOG_INFO, LOG_ERROR, LOG_WARN
@@ -31,7 +31,8 @@ namespace bluetooth {
 namespace root_canal {
 
 using rootcanal::AsyncTaskId;
-using rootcanal::HciSocketDevice;
+using rootcanal::HciDevice;
+using rootcanal::HciSocketTransport;
 using rootcanal::LinkLayerSocketDevice;
 using rootcanal::TaskCallback;
 
@@ -57,8 +58,9 @@ void TestEnvironment::initialize(std::promise<void> barrier) {
   SetUpTestChannel();
   SetUpHciServer([this](std::shared_ptr<AsyncDataChannel> socket,
                         AsyncDataChannelServer* srv) {
+    auto transport = HciSocketTransport::Create(socket);
     test_model_.AddHciConnection(
-        HciSocketDevice::Create(socket, controller_properties_file_));
+        HciDevice::Create(transport, controller_properties_file_));
     srv->StartListening();
   });
   SetUpLinkLayerServer();
diff --git a/tools/rootcanal/model/devices/hci_device.cc b/tools/rootcanal/model/devices/hci_device.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a6e48c31c8a69fadedbe28031277f42f49939049
--- /dev/null
+++ b/tools/rootcanal/model/devices/hci_device.cc
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci_device.h"
+
+#include "os/log.h"
+
+namespace rootcanal {
+
+HciDevice::HciDevice(std::shared_ptr<HciTransport> transport,
+                     const std::string& properties_filename)
+    : DualModeController(properties_filename), transport_(transport) {
+  advertising_interval_ms_ = std::chrono::milliseconds(1000);
+
+  page_scan_delay_ms_ = std::chrono::milliseconds(600);
+
+  properties_.SetPageScanRepetitionMode(0);
+  properties_.SetClassOfDevice(0x600420);
+  properties_.SetExtendedInquiryData({
+      12,  // length
+      9,   // Type: Device Name
+      'g',
+      'D',
+      'e',
+      'v',
+      'i',
+      'c',
+      'e',
+      '-',
+      'h',
+      'c',
+      'i',
+
+  });
+  properties_.SetName({
+      'g',
+      'D',
+      'e',
+      'v',
+      'i',
+      'c',
+      'e',
+      '-',
+      'H',
+      'C',
+      'I',
+  });
+
+  RegisterEventChannel([this](std::shared_ptr<std::vector<uint8_t>> packet) {
+    transport_->SendEvent(*packet);
+  });
+  RegisterAclChannel([this](std::shared_ptr<std::vector<uint8_t>> packet) {
+    transport_->SendAcl(*packet);
+  });
+  RegisterScoChannel([this](std::shared_ptr<std::vector<uint8_t>> packet) {
+    transport_->SendSco(*packet);
+  });
+  RegisterIsoChannel([this](std::shared_ptr<std::vector<uint8_t>> packet) {
+    transport_->SendIso(*packet);
+  });
+
+  transport_->RegisterCallbacks(
+      [this](const std::shared_ptr<std::vector<uint8_t>> command) {
+        HandleCommand(command);
+      },
+      [this](const std::shared_ptr<std::vector<uint8_t>> acl) {
+        HandleAcl(acl);
+      },
+      [this](const std::shared_ptr<std::vector<uint8_t>> sco) {
+        HandleSco(sco);
+      },
+      [this](const std::shared_ptr<std::vector<uint8_t>> iso) {
+        HandleIso(iso);
+      },
+      [this]() {
+        LOG_INFO("HCI transport closed");
+        Close();
+      });
+}
+
+void HciDevice::TimerTick() {
+  transport_->TimerTick();
+  DualModeController::TimerTick();
+}
+
+void HciDevice::Close() {
+  transport_->Close();
+  DualModeController::Close();
+}
+
+}  // namespace rootcanal
diff --git a/tools/rootcanal/model/devices/hci_device.h b/tools/rootcanal/model/devices/hci_device.h
new file mode 100644
index 0000000000000000000000000000000000000000..eb79cd96075c6395b88ee756e1c45e6c4194919f
--- /dev/null
+++ b/tools/rootcanal/model/devices/hci_device.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <memory>  // for shared_ptr, make_...
+#include <string>  // for string
+
+#include "model/controller/dual_mode_controller.h"  // for DualModeController
+#include "model/hci/hci_transport.h"                // for HciTransport
+
+namespace rootcanal {
+
+class HciDevice : public DualModeController {
+ public:
+  HciDevice(std::shared_ptr<HciTransport> transport,
+            const std::string& properties_filename);
+  ~HciDevice() = default;
+
+  static std::shared_ptr<HciDevice> Create(
+      std::shared_ptr<HciTransport> transport,
+      const std::string& properties_filename) {
+    return std::make_shared<HciDevice>(transport, properties_filename);
+  }
+
+  std::string GetTypeString() const override { return "hci_device"; }
+
+  void TimerTick() override;
+
+  void Close() override;
+
+ private:
+  std::shared_ptr<HciTransport> transport_;
+};
+
+}  // namespace rootcanal
diff --git a/tools/rootcanal/model/devices/hci_socket_device.cc b/tools/rootcanal/model/devices/hci_socket_device.cc
deleted file mode 100644
index 83bf5e2cae1e53a4a826edfe870e0f84f12e5691..0000000000000000000000000000000000000000
--- a/tools/rootcanal/model/devices/hci_socket_device.cc
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "hci_socket_device.h"
-
-#include <chrono>       // for milliseconds
-#include <type_traits>  // for remove_extent_t
-
-#include "model/devices/device_properties.h"  // for DeviceProperties
-#include "os/log.h"                           // for LOG_INFO, LOG_ALWAYS_FATAL
-
-using std::vector;
-
-namespace rootcanal {
-
-HciSocketDevice::HciSocketDevice(std::shared_ptr<AsyncDataChannel> socket,
-                                 const std::string& properties_filename)
-    : DualModeController(properties_filename), socket_(socket) {
-  advertising_interval_ms_ = std::chrono::milliseconds(1000);
-
-  page_scan_delay_ms_ = std::chrono::milliseconds(600);
-
-  properties_.SetPageScanRepetitionMode(0);
-  properties_.SetClassOfDevice(0x600420);
-  properties_.SetExtendedInquiryData({
-      16,  // length
-      9,   // Type: Device Name
-      'g',
-      'D',
-      'e',
-      'v',
-      'i',
-      'c',
-      'e',
-      '-',
-      'h',
-      'c',
-      'i',
-      '_',
-      'n',
-      'e',
-      't',
-  });
-  properties_.SetName({
-      'g',
-      'D',
-      'e',
-      'v',
-      'i',
-      'c',
-      'e',
-      '-',
-      'H',
-      'C',
-      'I',
-      '_',
-      'N',
-      'e',
-      't',
-  });
-
-  h4_ = H4DataChannelPacketizer(
-      socket,
-      [this](const std::vector<uint8_t>& raw_command) {
-        std::shared_ptr<std::vector<uint8_t>> packet_copy =
-            std::make_shared<std::vector<uint8_t>>(raw_command);
-        HandleCommand(packet_copy);
-      },
-      [](const std::vector<uint8_t>&) {
-        LOG_ALWAYS_FATAL("Unexpected Event in HciSocketDevice!");
-      },
-      [this](const std::vector<uint8_t>& raw_acl) {
-        std::shared_ptr<std::vector<uint8_t>> packet_copy =
-            std::make_shared<std::vector<uint8_t>>(raw_acl);
-        HandleAcl(packet_copy);
-      },
-      [this](const std::vector<uint8_t>& raw_sco) {
-        std::shared_ptr<std::vector<uint8_t>> packet_copy =
-            std::make_shared<std::vector<uint8_t>>(raw_sco);
-        HandleSco(packet_copy);
-      },
-      [this](const std::vector<uint8_t>& raw_iso) {
-        std::shared_ptr<std::vector<uint8_t>> packet_copy =
-            std::make_shared<std::vector<uint8_t>>(raw_iso);
-        HandleIso(packet_copy);
-      },
-      [this]() {
-        LOG_INFO("HCI socket device disconnected");
-        Close();
-      });
-
-  RegisterEventChannel([this](std::shared_ptr<std::vector<uint8_t>> packet) {
-    SendHci(PacketType::EVENT, packet);
-  });
-  RegisterAclChannel([this](std::shared_ptr<std::vector<uint8_t>> packet) {
-    SendHci(PacketType::ACL, packet);
-  });
-  RegisterScoChannel([this](std::shared_ptr<std::vector<uint8_t>> packet) {
-    SendHci(PacketType::SCO, packet);
-  });
-  RegisterIsoChannel([this](std::shared_ptr<std::vector<uint8_t>> packet) {
-    SendHci(PacketType::ISO, packet);
-  });
-}
-
-void HciSocketDevice::TimerTick() {
-  h4_.OnDataReady(socket_);
-  DualModeController::TimerTick();
-}
-
-void HciSocketDevice::SendHci(
-    PacketType packet_type,
-    const std::shared_ptr<std::vector<uint8_t>> packet) {
-  if (!socket_ || !socket_->Connected()) {
-    LOG_INFO("Closed socket. Dropping packet of type %d",
-             static_cast<int>(packet_type));
-    return;
-  }
-  uint8_t type = static_cast<uint8_t>(packet_type);
-  h4_.Send(type, packet->data(), packet->size());
-}
-
-void HciSocketDevice::Close() {
-  if (socket_) {
-    socket_->Close();
-  }
-  DualModeController::Close();
-}
-
-}  // namespace rootcanal
diff --git a/tools/rootcanal/model/devices/h4_data_channel_packetizer.cc b/tools/rootcanal/model/hci/h4_data_channel_packetizer.cc
similarity index 90%
rename from tools/rootcanal/model/devices/h4_data_channel_packetizer.cc
rename to tools/rootcanal/model/hci/h4_data_channel_packetizer.cc
index f089b5d7d532030fe18d7887adfb0bebac40e429..99f887547e679614b39b0f50e4a67b68730ef37a 100644
--- a/tools/rootcanal/model/devices/h4_data_channel_packetizer.cc
+++ b/tools/rootcanal/model/hci/h4_data_channel_packetizer.cc
@@ -26,10 +26,10 @@
 #include <utility>      // for move
 #include <vector>       // for vector
 
-#include "model/devices/h4_parser.h"  // for H4Parser, ClientDisconnectCa...
-#include "model/devices/hci_protocol.h"  // for PacketReadCallback, AsyncDataChannel
-#include "net/async_data_channel.h"      // for AsyncDataChannel
-#include "os/log.h"                      // for LOG_ERROR, LOG_ALWAYS_FATAL
+#include "model/hci/h4_parser.h"     // for H4Parser, ClientDisconnectCa...
+#include "model/hci/hci_protocol.h"  // for PacketReadCallback, AsyncDataChannel
+#include "net/async_data_channel.h"  // for AsyncDataChannel
+#include "os/log.h"                  // for LOG_ERROR, LOG_ALWAYS_FATAL
 
 namespace rootcanal {
 
diff --git a/tools/rootcanal/model/devices/h4_data_channel_packetizer.h b/tools/rootcanal/model/hci/h4_data_channel_packetizer.h
similarity index 100%
rename from tools/rootcanal/model/devices/h4_data_channel_packetizer.h
rename to tools/rootcanal/model/hci/h4_data_channel_packetizer.h
diff --git a/tools/rootcanal/model/devices/h4_packetizer.cc b/tools/rootcanal/model/hci/h4_packetizer.cc
similarity index 100%
rename from tools/rootcanal/model/devices/h4_packetizer.cc
rename to tools/rootcanal/model/hci/h4_packetizer.cc
diff --git a/tools/rootcanal/model/devices/h4_packetizer.h b/tools/rootcanal/model/hci/h4_packetizer.h
similarity index 100%
rename from tools/rootcanal/model/devices/h4_packetizer.h
rename to tools/rootcanal/model/hci/h4_packetizer.h
diff --git a/tools/rootcanal/model/devices/h4_parser.cc b/tools/rootcanal/model/hci/h4_parser.cc
similarity index 95%
rename from tools/rootcanal/model/devices/h4_parser.cc
rename to tools/rootcanal/model/hci/h4_parser.cc
index fa9fecf04b38180cbbce096bc0e649f75c427780..f55be5cff4f0ec3d440149c4721a3656ad75b0ba 100644
--- a/tools/rootcanal/model/devices/h4_parser.cc
+++ b/tools/rootcanal/model/hci/h4_parser.cc
@@ -14,7 +14,7 @@
 // limitations under the License.
 //
 
-#include "model/devices/h4_parser.h"  // for H4Parser, PacketType, H4Pars...
+#include "model/hci/h4_parser.h"  // for H4Parser, PacketType, H4Pars...
 
 #include <stddef.h>  // for size_t
 
@@ -23,8 +23,8 @@
 #include <utility>     // for move
 #include <vector>      // for vector
 
-#include "model/devices/hci_protocol.h"  // for PacketReadCallback
-#include "os/log.h"                      // for LOG_ALWAYS_FATAL, LOG_INFO
+#include "model/hci/hci_protocol.h"  // for PacketReadCallback
+#include "os/log.h"                  // for LOG_ALWAYS_FATAL, LOG_INFO
 
 namespace rootcanal {
 
diff --git a/tools/rootcanal/model/devices/h4_parser.h b/tools/rootcanal/model/hci/h4_parser.h
similarity index 98%
rename from tools/rootcanal/model/devices/h4_parser.h
rename to tools/rootcanal/model/hci/h4_parser.h
index 688692409fa736f963b8fc9af5586e5ed89d5854..8a8802c878bd475fb0f49d71860d665a7c65aebd 100644
--- a/tools/rootcanal/model/devices/h4_parser.h
+++ b/tools/rootcanal/model/hci/h4_parser.h
@@ -23,7 +23,7 @@
 #include <ostream>     // for operator<<, ostream
 #include <vector>      // for vector
 
-#include "model/devices/hci_protocol.h"  // for PacketReadCallback
+#include "model/hci/hci_protocol.h"  // for PacketReadCallback
 
 namespace rootcanal {
 
diff --git a/tools/rootcanal/model/devices/hci_protocol.cc b/tools/rootcanal/model/hci/hci_protocol.cc
similarity index 100%
rename from tools/rootcanal/model/devices/hci_protocol.cc
rename to tools/rootcanal/model/hci/hci_protocol.cc
diff --git a/tools/rootcanal/model/devices/hci_protocol.h b/tools/rootcanal/model/hci/hci_protocol.h
similarity index 100%
rename from tools/rootcanal/model/devices/hci_protocol.h
rename to tools/rootcanal/model/hci/hci_protocol.h
diff --git a/tools/rootcanal/model/hci/hci_socket_transport.cc b/tools/rootcanal/model/hci/hci_socket_transport.cc
new file mode 100644
index 0000000000000000000000000000000000000000..0d6f0c8b842b9fc19c080321dbc9128d5abee41a
--- /dev/null
+++ b/tools/rootcanal/model/hci/hci_socket_transport.cc
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "hci_socket_transport.h"
+
+#include "os/log.h"  // for LOG_INFO, LOG_ALWAYS_FATAL
+
+namespace rootcanal {
+
+HciSocketTransport::HciSocketTransport(std::shared_ptr<AsyncDataChannel> socket)
+    : socket_(socket) {}
+
+void HciSocketTransport::RegisterCallbacks(PacketCallback command_callback,
+                                           PacketCallback acl_callback,
+                                           PacketCallback sco_callback,
+                                           PacketCallback iso_callback,
+                                           CloseCallback close_callback) {
+  // TODO: Avoid the copy here by using new buffer in H4DataChannel
+  h4_ = H4DataChannelPacketizer(
+      socket_,
+      [command_callback](const std::vector<uint8_t>& raw_command) {
+        std::shared_ptr<std::vector<uint8_t>> packet_copy =
+            std::make_shared<std::vector<uint8_t>>(raw_command);
+        command_callback(packet_copy);
+      },
+      [](const std::vector<uint8_t>&) {
+        LOG_ALWAYS_FATAL("Unexpected Event in HciSocketTransport!");
+      },
+      [acl_callback](const std::vector<uint8_t>& raw_acl) {
+        std::shared_ptr<std::vector<uint8_t>> packet_copy =
+            std::make_shared<std::vector<uint8_t>>(raw_acl);
+        acl_callback(packet_copy);
+      },
+      [sco_callback](const std::vector<uint8_t>& raw_sco) {
+        std::shared_ptr<std::vector<uint8_t>> packet_copy =
+            std::make_shared<std::vector<uint8_t>>(raw_sco);
+        sco_callback(packet_copy);
+      },
+      [iso_callback](const std::vector<uint8_t>& raw_iso) {
+        std::shared_ptr<std::vector<uint8_t>> packet_copy =
+            std::make_shared<std::vector<uint8_t>>(raw_iso);
+        iso_callback(packet_copy);
+      },
+      close_callback);
+}
+
+void HciSocketTransport::TimerTick() { h4_.OnDataReady(socket_); }
+
+void HciSocketTransport::SendHci(PacketType packet_type,
+                                 const std::vector<uint8_t>& packet) {
+  if (!socket_ || !socket_->Connected()) {
+    LOG_INFO("Closed socket. Dropping packet of type %d",
+             static_cast<int>(packet_type));
+    return;
+  }
+  uint8_t type = static_cast<uint8_t>(packet_type);
+  h4_.Send(type, packet.data(), packet.size());
+}
+
+void HciSocketTransport::SendEvent(const std::vector<uint8_t>& packet) {
+  SendHci(PacketType::EVENT, packet);
+}
+
+void HciSocketTransport::SendAcl(const std::vector<uint8_t>& packet) {
+  SendHci(PacketType::ACL, packet);
+}
+
+void HciSocketTransport::SendSco(const std::vector<uint8_t>& packet) {
+  SendHci(PacketType::SCO, packet);
+}
+
+void HciSocketTransport::SendIso(const std::vector<uint8_t>& packet) {
+  SendHci(PacketType::ISO, packet);
+}
+
+void HciSocketTransport::Close() { socket_->Close(); }
+
+}  // namespace rootcanal
diff --git a/tools/rootcanal/model/devices/hci_socket_device.h b/tools/rootcanal/model/hci/hci_socket_transport.h
similarity index 51%
rename from tools/rootcanal/model/devices/hci_socket_device.h
rename to tools/rootcanal/model/hci/hci_socket_transport.h
index 19324e8382e4c97358f29410321c8a98999d08db..f3f42f40c55a64fde18c10287ac1a06d7e466dd8 100644
--- a/tools/rootcanal/model/devices/hci_socket_device.h
+++ b/tools/rootcanal/model/hci/hci_socket_transport.h
@@ -16,43 +16,47 @@
 
 #pragma once
 
-#include <cstdint>     // for uint8_t
-#include <functional>  // for __base, function
-#include <memory>      // for shared_ptr, make_...
-#include <string>      // for string
-#include <vector>      // for vector
+#include <memory>  // for shared_ptr, make_...
 
-#include "model/controller/dual_mode_controller.h"     // for DualModeController
-#include "model/devices/h4_data_channel_packetizer.h"  // for ClientDisconnectC...
-#include "model/devices/hci_protocol.h"                // for PacketReadCallback
-#include "net/async_data_channel.h"                    // for AsyncDataChannel
+#include "model/hci/h4_data_channel_packetizer.h"  // for H4DataChannelP...
+#include "model/hci/hci_transport.h"               // for HciTransport
+#include "net/async_data_channel.h"                // for AsyncDataChannel
 
 namespace rootcanal {
 
 using android::net::AsyncDataChannel;
 
-class HciSocketDevice : public DualModeController {
+class HciSocketTransport : public HciTransport {
  public:
-  HciSocketDevice(std::shared_ptr<AsyncDataChannel> socket,
-                  const std::string& properties_filename);
-  ~HciSocketDevice() = default;
-
-  static std::shared_ptr<HciSocketDevice> Create(
-      std::shared_ptr<AsyncDataChannel> socket,
-      const std::string& properties_filename) {
-    return std::make_shared<HciSocketDevice>(socket, properties_filename);
+  HciSocketTransport(std::shared_ptr<AsyncDataChannel> socket);
+  ~HciSocketTransport() = default;
+
+  static std::shared_ptr<HciSocketTransport> Create(
+      std::shared_ptr<AsyncDataChannel> socket) {
+    return std::make_shared<HciSocketTransport>(socket);
   }
 
-  std::string GetTypeString() const override { return "hci_socket_device"; }
+  void SendEvent(const std::vector<uint8_t>& packet) override;
 
-  void TimerTick() override;
+  void SendAcl(const std::vector<uint8_t>& packet) override;
+
+  void SendSco(const std::vector<uint8_t>& packet) override;
+
+  void SendIso(const std::vector<uint8_t>& packet) override;
 
-  void SendHci(PacketType packet_type,
-               const std::shared_ptr<std::vector<uint8_t>> packet);
+  void RegisterCallbacks(PacketCallback command_callback,
+                         PacketCallback acl_callback,
+                         PacketCallback sco_callback,
+                         PacketCallback iso_callback,
+                         CloseCallback close_callback) override;
+
+  void TimerTick() override;
 
   void Close() override;
 
  private:
+  void SendHci(PacketType packet_type, const std::vector<uint8_t>& packet);
+
   std::shared_ptr<AsyncDataChannel> socket_;
   H4DataChannelPacketizer h4_{socket_,
                               [](const std::vector<uint8_t>&) {},
diff --git a/tools/rootcanal/model/hci/hci_transport.h b/tools/rootcanal/model/hci/hci_transport.h
new file mode 100644
index 0000000000000000000000000000000000000000..8ffb349abdcae7a215bc1fe7a2308e18e1007c02
--- /dev/null
+++ b/tools/rootcanal/model/hci/hci_transport.h
@@ -0,0 +1,51 @@
+//
+// Copyright 2022 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+
+#pragma once
+
+#include <functional>
+#include <vector>
+
+namespace rootcanal {
+
+using PacketCallback =
+    std::function<void(const std::shared_ptr<std::vector<uint8_t>>)>;
+using CloseCallback = std::function<void()>;
+
+class HciTransport {
+ public:
+  virtual ~HciTransport() = default;
+
+  virtual void SendEvent(const std::vector<uint8_t>& packet) = 0;
+
+  virtual void SendAcl(const std::vector<uint8_t>& packet) = 0;
+
+  virtual void SendSco(const std::vector<uint8_t>& packet) = 0;
+
+  virtual void SendIso(const std::vector<uint8_t>& packet) = 0;
+
+  virtual void RegisterCallbacks(PacketCallback command_callback,
+                                 PacketCallback acl_callback,
+                                 PacketCallback sco_callback,
+                                 PacketCallback iso_callback,
+                                 CloseCallback close_callback) = 0;
+
+  virtual void TimerTick() = 0;
+
+  virtual void Close() = 0;
+};
+
+}  // namespace rootcanal
diff --git a/tools/rootcanal/model/setup/test_model.cc b/tools/rootcanal/model/setup/test_model.cc
index 3b993b436063a094b3228f559bba8bdd434aa6de..c2ad28189ccfdd072bc0de4e9435cfa93e832fde 100644
--- a/tools/rootcanal/model/setup/test_model.cc
+++ b/tools/rootcanal/model/setup/test_model.cc
@@ -171,7 +171,7 @@ void TestModel::AddRemote(const std::string& server, int port,
   AddLinkLayerConnection(dev, phy_type);
 }
 
-void TestModel::AddHciConnection(std::shared_ptr<HciSocketDevice> dev) {
+void TestModel::AddHciConnection(std::shared_ptr<HciDevice> dev) {
   size_t index = Add(std::static_pointer_cast<Device>(dev));
   std::string addr = "da:4c:10:de:17:";  // Da HCI dev
   std::stringstream stream;
diff --git a/tools/rootcanal/model/setup/test_model.h b/tools/rootcanal/model/setup/test_model.h
index 99138a845de8724406c049b45430a5ccb9de06a1..263346c9f8ef6a47705245adb5dbb479ce2566a1 100644
--- a/tools/rootcanal/model/setup/test_model.h
+++ b/tools/rootcanal/model/setup/test_model.h
@@ -25,7 +25,7 @@
 #include <vector>      // for vector
 
 #include "hci/address.h"                       // for Address
-#include "model/devices/hci_socket_device.h"   // for HciSocketDevice
+#include "model/devices/hci_device.h"          // for HciDevice
 #include "model/setup/async_manager.h"         // for AsyncUserId, AsyncTaskId
 #include "phy.h"                               // for Phy, Phy::Type
 #include "phy_layer_factory.h"                 // for PhyLayerFactory
@@ -76,7 +76,7 @@ class TestModel {
 
   // Handle incoming remote connections
   void AddLinkLayerConnection(std::shared_ptr<Device> dev, Phy::Type phy_type);
-  void AddHciConnection(std::shared_ptr<HciSocketDevice> dev);
+  void AddHciConnection(std::shared_ptr<HciDevice> dev);
 
   // Handle closed remote connections (both hci & link layer)
   void OnConnectionClosed(size_t index, AsyncUserId user_id);
diff --git a/tools/rootcanal/test/h4_parser_unittest.cc b/tools/rootcanal/test/h4_parser_unittest.cc
index 020911765363b8472bbb42309ba050c89124895b..9b74c1c8ca9522b232289155bd27730089183ce8 100644
--- a/tools/rootcanal/test/h4_parser_unittest.cc
+++ b/tools/rootcanal/test/h4_parser_unittest.cc
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-#include "model/devices/h4_parser.h"
+#include "model/hci/h4_parser.h"
 
 #include <gtest/gtest.h>