[bt][common] Remove bt::LinkedList

Remove bt::LinkedList (aka fbl::DoublyLinkedList). Replace
remaining uses of LinkedList with std::list or std::queue.
While this does introduce allocations compared to intrusively
linked lists, it is necessary to move off of fbl.

Bug: 100658, 652
Test: fx test //src/connectivity/bluetooth/core/bt-host
Change-Id: I0a12c4ec12aa83fb8b37f7da54003407acfb50d0
Reviewed-on: https://fuchsia-review.googlesource.com/c/fuchsia/+/700073
Reviewed-by: Ali Saeed <saeedali@google.com>
Commit-Queue: Ben Lawson <benlawson@google.com>
diff --git a/pw_bluetooth_sapphire/host/att/bearer.h b/pw_bluetooth_sapphire/host/att/bearer.h
index 6969862..4847769 100644
--- a/pw_bluetooth_sapphire/host/att/bearer.h
+++ b/pw_bluetooth_sapphire/host/att/bearer.h
@@ -19,7 +19,6 @@
 #include "src/connectivity/bluetooth/core/bt-host/att/error.h"
 #include "src/connectivity/bluetooth/core/bt-host/att/packet.h"
 #include "src/connectivity/bluetooth/core/bt-host/common/byte_buffer.h"
-#include "src/connectivity/bluetooth/core/bt-host/common/linked_list.h"
 #include "src/connectivity/bluetooth/core/bt-host/common/packet_view.h"
 #include "src/connectivity/bluetooth/core/bt-host/l2cap/channel.h"
 #include "src/connectivity/bluetooth/core/bt-host/l2cap/scoped_channel.h"
diff --git a/pw_bluetooth_sapphire/host/att/database.h b/pw_bluetooth_sapphire/host/att/database.h
index b36c364..9e4bb2f 100644
--- a/pw_bluetooth_sapphire/host/att/database.h
+++ b/pw_bluetooth_sapphire/host/att/database.h
@@ -154,10 +154,6 @@
   // The list of groupings is sorted by handle where each grouping maps to a
   // non-overlapping handle range. Successive groupings don't necessarily
   // represent contiguous handle ranges as any grouping can be removed.
-  //
-  // Note: This uses a std::list because std::lower_bound doesn't work with a
-  // LinkedList (aka fbl::DoublyLinkedList). This is only marginally
-  // less space efficient.
   GroupingList groupings_;
 
   fxl::WeakPtrFactory<Database> weak_ptr_factory_;
diff --git a/pw_bluetooth_sapphire/host/common/BUILD.gn b/pw_bluetooth_sapphire/host/common/BUILD.gn
index 5a7b953..742761c 100644
--- a/pw_bluetooth_sapphire/host/common/BUILD.gn
+++ b/pw_bluetooth_sapphire/host/common/BUILD.gn
@@ -24,7 +24,6 @@
     "identifier.cc",
     "identifier.h",
     "inspectable.h",
-    "linked_list.h",
     "log.cc",
     "log.h",
     "manufacturer_names.cc",
diff --git a/pw_bluetooth_sapphire/host/common/linked_list.h b/pw_bluetooth_sapphire/host/common/linked_list.h
deleted file mode 100644
index 575a76f..0000000
--- a/pw_bluetooth_sapphire/host/common/linked_list.h
+++ /dev/null
@@ -1,33 +0,0 @@
-// Copyright 2017 The Fuchsia Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file.
-
-#ifndef SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_COMMON_LINKED_LIST_H_
-#define SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_COMMON_LINKED_LIST_H_
-
-#include <memory>
-
-#include <fbl/intrusive_double_list.h>
-
-namespace bt {
-
-// TODO(armansito): Use this in more places where it makes sense (see fxbug.dev/652).
-
-// Convenience template aliases for an fbl intrusive container backed
-// LinkedList.
-//
-//   * Elements need to be dynamically allocated and entries MUST be managed
-//     pointers.
-//
-//   * This is currently implemented as a doubly linked-list. This adds extra
-//     storage overhead but makes it suitable for FIFO queues.
-
-template <typename T>
-using LinkedList = fbl::DoublyLinkedList<std::unique_ptr<T>>;
-
-template <typename T>
-using LinkedListable = fbl::DoublyLinkedListable<std::unique_ptr<T>>;
-
-}  // namespace bt
-
-#endif  // SRC_CONNECTIVITY_BLUETOOTH_CORE_BT_HOST_COMMON_LINKED_LIST_H_
diff --git a/pw_bluetooth_sapphire/host/l2cap/channel_manager.cc b/pw_bluetooth_sapphire/host/l2cap/channel_manager.cc
index 52bedc2..5a2ac73 100644
--- a/pw_bluetooth_sapphire/host/l2cap/channel_manager.cc
+++ b/pw_bluetooth_sapphire/host/l2cap/channel_manager.cc
@@ -98,7 +98,7 @@
   // Stores packets received on a connection handle before a link for it has
   // been created.
   using PendingPacketMap =
-      std::unordered_map<hci_spec::ConnectionHandle, LinkedList<hci::ACLDataPacket>>;
+      std::unordered_map<hci_spec::ConnectionHandle, std::queue<hci::ACLDataPacketPtr>>;
   PendingPacketMap pending_packets_;
 
   // Store information required to create and forward channels for locally-
@@ -305,7 +305,7 @@
   // If a LogicalLink does not exist, we set up a queue for its packets to be
   // delivered when the LogicalLink gets created.
   if (iter == ll_map_.end()) {
-    pp_iter = pending_packets_.emplace(handle, LinkedList<hci::ACLDataPacket>()).first;
+    pp_iter = pending_packets_.emplace(handle, std::queue<hci::ACLDataPacketPtr>()).first;
   } else {
     // A logical link exists. |pp_iter| will be valid only if the drain task has
     // not run yet (see ChannelManagerImpl::RegisterInternal()).
@@ -315,7 +315,7 @@
   if (pp_iter != pending_packets_.end()) {
     packet->set_trace_id(TRACE_NONCE());
     TRACE_FLOW_BEGIN("bluetooth", "ChannelMaager::OnDataReceived queued", packet->trace_id());
-    pp_iter->second.push_back(std::move(packet));
+    pp_iter->second.push(std::move(packet));
     bt_log(TRACE, "l2cap", "queued rx packet on handle: %#.4x", handle);
     return;
   }
@@ -346,8 +346,9 @@
   auto pp_iter = pending_packets_.find(handle);
   if (pp_iter != pending_packets_.end()) {
     auto& packets = pp_iter->second;
-    while (!packets.is_empty()) {
-      auto packet = packets.pop_front();
+    while (!packets.empty()) {
+      auto packet = std::move(packets.front());
+      packets.pop();
       TRACE_FLOW_END("bluetooth", "ChannelManagerImpl::OnDataReceived queued", packet->trace_id());
       ll->HandleRxPacket(std::move(packet));
     }
diff --git a/pw_bluetooth_sapphire/host/l2cap/channel_manager_unittest.cc b/pw_bluetooth_sapphire/host/l2cap/channel_manager_unittest.cc
index a4e4fa8..f445191 100644
--- a/pw_bluetooth_sapphire/host/l2cap/channel_manager_unittest.cc
+++ b/pw_bluetooth_sapphire/host/l2cap/channel_manager_unittest.cc
@@ -451,10 +451,10 @@
   CommandId NextCommandId() { return next_command_id_++; }
 
  private:
-  bool SendPackets(LinkedList<hci::ACLDataPacket> packets, ChannelId channel_id,
+  bool SendPackets(std::list<hci::ACLDataPacketPtr> packets, ChannelId channel_id,
                    hci::AclDataChannel::PacketPriority priority) {
     for (const auto& packet : packets) {
-      const ByteBuffer& data = packet.view().data();
+      const ByteBuffer& data = packet->view().data();
       if (expected_packets_.empty()) {
         ADD_FAILURE() << "Unexpected outbound ACL data";
         std::cout << "{ ";
@@ -484,7 +484,7 @@
         expected_packets_.pop();
       }
     }
-    return !packets.is_empty();
+    return !packets.empty();
   }
 
   std::unique_ptr<ChannelManager> chanmgr_;
diff --git a/pw_bluetooth_sapphire/host/l2cap/fragmenter_unittest.cc b/pw_bluetooth_sapphire/host/l2cap/fragmenter_unittest.cc
index 2ac19fc..b1faf8a 100644
--- a/pw_bluetooth_sapphire/host/l2cap/fragmenter_unittest.cc
+++ b/pw_bluetooth_sapphire/host/l2cap/fragmenter_unittest.cc
@@ -187,7 +187,7 @@
 
   auto fragments = pdu.ReleaseFragments();
 
-  EXPECT_TRUE(ContainersEqual(expected_fragment, fragments.begin()->view().data()));
+  EXPECT_TRUE(ContainersEqual(expected_fragment, (*fragments.begin())->view().data()));
 }
 
 TEST(FragmenterTest, SingleFragment) {
@@ -208,7 +208,7 @@
 
   auto fragments = pdu.ReleaseFragments();
 
-  EXPECT_TRUE(ContainersEqual(expected_fragment, fragments.begin()->view().data()));
+  EXPECT_TRUE(ContainersEqual(expected_fragment, (*fragments.begin())->view().data()));
 }
 
 TEST(FragmenterTest, SingleFragmentExactFit) {
@@ -230,7 +230,7 @@
 
   auto fragments = pdu.ReleaseFragments();
 
-  EXPECT_TRUE(ContainersEqual(expected_fragment, fragments.begin()->view().data()));
+  EXPECT_TRUE(ContainersEqual(expected_fragment, (*fragments.begin())->view().data()));
 }
 
 TEST(FragmenterTest, TwoFragmentsOffByOne) {
@@ -260,8 +260,8 @@
 
   auto fragments = pdu.ReleaseFragments();
 
-  EXPECT_TRUE(ContainersEqual(expected_fragment0, fragments.begin()->view().data()));
-  EXPECT_TRUE(ContainersEqual(expected_fragment1, (++fragments.begin())->view().data()));
+  EXPECT_TRUE(ContainersEqual(expected_fragment0, (*fragments.begin())->view().data()));
+  EXPECT_TRUE(ContainersEqual(expected_fragment1, (*++fragments.begin())->view().data()));
 }
 
 TEST(FragmenterTest, TwoFragmentsExact) {
@@ -292,8 +292,8 @@
 
   auto fragments = pdu.ReleaseFragments();
 
-  EXPECT_TRUE(ContainersEqual(expected_fragment0, fragments.begin()->view().data()));
-  EXPECT_TRUE(ContainersEqual(expected_fragment1, (++fragments.begin())->view().data()));
+  EXPECT_TRUE(ContainersEqual(expected_fragment0, (*fragments.begin())->view().data()));
+  EXPECT_TRUE(ContainersEqual(expected_fragment1, (*++fragments.begin())->view().data()));
 }
 
 TEST(FragmenterTest, ManyFragmentsOffByOne) {
@@ -339,10 +339,10 @@
 
   auto fragments = pdu.ReleaseFragments();
   auto iter = fragments.begin();
-  EXPECT_TRUE(ContainersEqual(expected_fragment0, (iter++)->view().data()));
-  EXPECT_TRUE(ContainersEqual(expected_fragment1, (iter++)->view().data()));
-  EXPECT_TRUE(ContainersEqual(expected_fragment2, (iter++)->view().data()));
-  EXPECT_TRUE(ContainersEqual(expected_fragment3, iter->view().data()));
+  EXPECT_TRUE(ContainersEqual(expected_fragment0, (*iter++)->view().data()));
+  EXPECT_TRUE(ContainersEqual(expected_fragment1, (*iter++)->view().data()));
+  EXPECT_TRUE(ContainersEqual(expected_fragment2, (*iter++)->view().data()));
+  EXPECT_TRUE(ContainersEqual(expected_fragment3, (*iter)->view().data()));
 }
 
 TEST(FragmenterTest, MaximalSizedPayload) {
@@ -389,9 +389,9 @@
 
   auto fragments = pdu.ReleaseFragments();
   auto iter = fragments.begin();
-  EXPECT_TRUE(ContainersEqual(expected_fragment0, (iter++)->view().data()));
-  EXPECT_TRUE(ContainersEqual(expected_fragment1, (iter++)->view().data()));
-  EXPECT_TRUE(ContainersEqual(expected_fragment2, (iter++)->view().data()));
+  EXPECT_TRUE(ContainersEqual(expected_fragment0, (*iter++)->view().data()));
+  EXPECT_TRUE(ContainersEqual(expected_fragment1, (*iter++)->view().data()));
+  EXPECT_TRUE(ContainersEqual(expected_fragment2, (*iter++)->view().data()));
 }
 
 }  // namespace
diff --git a/pw_bluetooth_sapphire/host/l2cap/logical_link.cc b/pw_bluetooth_sapphire/host/l2cap/logical_link.cc
index e5ef687..ad73daa 100644
--- a/pw_bluetooth_sapphire/host/l2cap/logical_link.cc
+++ b/pw_bluetooth_sapphire/host/l2cap/logical_link.cc
@@ -254,7 +254,7 @@
   PDU pdu = fragmenter_.BuildFrame(id, payload, fcs_option, flushable);
   auto fragments = pdu.ReleaseFragments();
 
-  ZX_ASSERT(!fragments.is_empty());
+  ZX_ASSERT(!fragments.empty());
   acl_data_channel_->SendPackets(std::move(fragments), id, ChannelPriority(id));
 }
 
diff --git a/pw_bluetooth_sapphire/host/l2cap/pdu.cc b/pw_bluetooth_sapphire/host/l2cap/pdu.cc
index 15f40c1..f4174c7 100644
--- a/pw_bluetooth_sapphire/host/l2cap/pdu.cc
+++ b/pw_bluetooth_sapphire/host/l2cap/pdu.cc
@@ -9,17 +9,13 @@
 
 namespace bt::l2cap {
 
-PDU::PDU() : fragment_count_(0u) {}
-
 // NOTE: The order in which these are initialized matters, as
 // other.ReleaseFragments() resets |other.fragment_count_|.
-PDU::PDU(PDU&& other)
-    : fragment_count_(other.fragment_count_), fragments_(other.ReleaseFragments()) {}
+PDU::PDU(PDU&& other) : fragments_(other.ReleaseFragments()) {}
 
 PDU& PDU::operator=(PDU&& other) {
   // NOTE: The order in which these are initialized matters, as
   // other.ReleaseFragments() resets |other.fragment_count_|.
-  fragment_count_ = other.fragment_count_;
   fragments_ = other.ReleaseFragments();
   return *this;
 }
@@ -38,7 +34,7 @@
   bool found = false;
   size_t offset = 0u;
   for (auto iter = fragments_.begin(); iter != fragments_.end() && remaining; ++iter) {
-    auto payload = iter->view().payload_data();
+    auto payload = (*iter)->view().payload_data();
 
     // Skip the Basic L2CAP header for the first fragment.
     if (iter == fragments_.begin()) {
@@ -77,27 +73,25 @@
 
 PDU::FragmentList PDU::ReleaseFragments() {
   auto out_list = std::move(fragments_);
-  fragment_count_ = 0u;
 
   ZX_DEBUG_ASSERT(!is_valid());
   return out_list;
 }
 
 const BasicHeader& PDU::basic_header() const {
-  ZX_DEBUG_ASSERT(!fragments_.is_empty());
+  ZX_DEBUG_ASSERT(!fragments_.empty());
   const auto& fragment = *fragments_.begin();
 
-  ZX_DEBUG_ASSERT(fragment.packet_boundary_flag() !=
+  ZX_DEBUG_ASSERT(fragment->packet_boundary_flag() !=
                   hci_spec::ACLPacketBoundaryFlag::kContinuingFragment);
-  return fragment.view().payload<BasicHeader>();
+  return fragment->view().payload<BasicHeader>();
 }
 
 void PDU::AppendFragment(hci::ACLDataPacketPtr fragment) {
   ZX_DEBUG_ASSERT(fragment);
   ZX_DEBUG_ASSERT(!is_valid() ||
-                  fragments_.begin()->connection_handle() == fragment->connection_handle());
+                  (*fragments_.begin())->connection_handle() == fragment->connection_handle());
   fragments_.push_back(std::move(fragment));
-  fragment_count_++;
 }
 
 }  // namespace bt::l2cap
diff --git a/pw_bluetooth_sapphire/host/l2cap/pdu.h b/pw_bluetooth_sapphire/host/l2cap/pdu.h
index 445b375..6e91953 100644
--- a/pw_bluetooth_sapphire/host/l2cap/pdu.h
+++ b/pw_bluetooth_sapphire/host/l2cap/pdu.h
@@ -8,6 +8,8 @@
 #include <endian.h>
 #include <zircon/assert.h>
 
+#include <list>
+
 #include <fbl/intrusive_double_list.h>
 #include <fbl/macros.h>
 
@@ -24,19 +26,13 @@
 // packet. A PDU cannot be populated directly and must be obtained from a
 // Recombiner or Fragmenter.
 //
-// A PDU instance is light-weight (it consists of a single unique_ptr via
-// LinkedList and a size_t field) and can be passed around by value.
+// A PDU instance is light-weight and can be passed around by value.
 // As the PDU uniquely owns its chain of fragments, a PDU is move-only.
-//
-// THREAD-SAFETY:
-//
-// This class is not thread-safe. External locking should be provided if an
-// instance will be accessed on multiple threads.
 class PDU final {
  public:
-  using FragmentList = LinkedList<hci::ACLDataPacket>;
+  using FragmentList = std::list<hci::ACLDataPacketPtr>;
 
-  PDU();
+  PDU() = default;
   ~PDU() = default;
 
   // Allow move operations.
@@ -45,14 +41,10 @@
 
   // An unpopulated PDU is considered invalid, which is the default-constructed
   // state.
-  bool is_valid() const {
-    ZX_DEBUG_ASSERT(fragments_.is_empty() && !fragment_count_ ||
-                    !fragments_.is_empty() && fragment_count_);
-    return !fragments_.is_empty();
-  }
+  bool is_valid() const { return !fragments_.empty(); }
 
   // The number of ACL data fragments that are currently a part of this PDU.
-  size_t fragment_count() const { return fragment_count_; }
+  size_t fragment_count() const { return fragments_.size(); }
 
   // Returns the number of bytes that are currently contained in this PDU,
   // excluding the Basic L2CAP header.
@@ -65,7 +57,7 @@
   // for.
   hci_spec::ConnectionHandle connection_handle() const {
     ZX_DEBUG_ASSERT(is_valid());
-    return fragments_.begin()->connection_handle();
+    return (*fragments_.begin())->connection_handle();
   }
 
   // This method will attempt to read |size| bytes of the basic-frame
@@ -104,9 +96,6 @@
   // assumes that validity checks on |fragment| have already been performed.
   void AppendFragment(hci::ACLDataPacketPtr fragment);
 
-  // The number of fragments currently stored in this PDU.
-  size_t fragment_count_;
-
   // ACL data fragments that currently form this PDU. In a complete PDU, it is
   // expected that the sum of the payload sizes of all elements in |fragments_|
   // is equal to the length of the frame (i.e. length() + sizeof(BasicHeader)).
diff --git a/pw_bluetooth_sapphire/host/l2cap/pdu_unittest.cc b/pw_bluetooth_sapphire/host/l2cap/pdu_unittest.cc
index c75655d..6e75f18 100644
--- a/pw_bluetooth_sapphire/host/l2cap/pdu_unittest.cc
+++ b/pw_bluetooth_sapphire/host/l2cap/pdu_unittest.cc
@@ -128,7 +128,7 @@
   auto fragments = pdu.ReleaseFragments();
 
   EXPECT_FALSE(pdu.is_valid());
-  ASSERT_FALSE(fragments.is_empty());
+  ASSERT_FALSE(fragments.empty());
   EXPECT_EQ(0u, pdu.fragment_count());
 
   // Directly count the elements in |fragments| to make sure the count is
@@ -145,7 +145,7 @@
 
                                   // Basic l2cap header
                                   0x04, 0x00, 0xFF, 0xFF, 'T', 'e', 's', 't'),
-                              fragments.begin()->view().data()));
+                              (*fragments.begin())->view().data()));
 }
 
 TEST(PduTest, ReadSingleFragment) {
diff --git a/pw_bluetooth_sapphire/host/l2cap/recombiner_unittest.cc b/pw_bluetooth_sapphire/host/l2cap/recombiner_unittest.cc
index d7e3154..494a7a6 100644
--- a/pw_bluetooth_sapphire/host/l2cap/recombiner_unittest.cc
+++ b/pw_bluetooth_sapphire/host/l2cap/recombiner_unittest.cc
@@ -81,7 +81,7 @@
   auto fragments = pdu.ReleaseFragments();
   size_t sum = 0;
   for (const auto& f : fragments) {
-    sum += f.view().payload_size();
+    sum += f->view().payload_size();
   }
   EXPECT_EQ(expected_payload.length() + sizeof(BasicHeader), sum);
 }
diff --git a/pw_bluetooth_sapphire/host/socket/socket_channel_relay.h b/pw_bluetooth_sapphire/host/socket/socket_channel_relay.h
index b4fe30a..094410f 100644
--- a/pw_bluetooth_sapphire/host/socket/socket_channel_relay.h
+++ b/pw_bluetooth_sapphire/host/socket/socket_channel_relay.h
@@ -160,7 +160,6 @@
   // SDU). This comes, however, at the cost of higher memory usage when the
   // number of SDUs is small. (libc++ uses a minimum of 4KB per deque.)
   //
-  // TODO(fxbug.dev/670): Switch to LinkedList.
   // TODO(fxbug.dev/709): We should set an upper bound on the size of this queue.
   std::deque<ByteBufferPtr> socket_write_queue_;
 
diff --git a/pw_bluetooth_sapphire/host/transport/acl_data_channel.cc b/pw_bluetooth_sapphire/host/transport/acl_data_channel.cc
index c3895af..3c010c1 100644
--- a/pw_bluetooth_sapphire/host/transport/acl_data_channel.cc
+++ b/pw_bluetooth_sapphire/host/transport/acl_data_channel.cc
@@ -40,7 +40,7 @@
   void SetDataRxHandler(ACLPacketHandler rx_callback) override;
   bool SendPacket(ACLDataPacketPtr data_packet, UniqueChannelId channel_id,
                   PacketPriority priority) override;
-  bool SendPackets(LinkedList<ACLDataPacket> packets, UniqueChannelId channel_id,
+  bool SendPackets(std::list<ACLDataPacketPtr> packets, UniqueChannelId channel_id,
                    PacketPriority priority) override;
   void RegisterLink(hci_spec::ConnectionHandle handle, bt::LinkType ll_type) override;
   void UnregisterLink(hci_spec::ConnectionHandle handle) override;
@@ -220,10 +220,6 @@
   // TODO(armansito): Use priority_queue based on L2CAP channel priority.
   // TODO(fxbug.dev/944): Keep a separate queue for each open connection. Benefits:
   //   * Helps address the packet-prioritization TODO above.
-  //   * Also: having separate queues, which know their own
-  //     bt::LinkType, would let us replace std::list<QueuedDataPacket>
-  //     with LinkedList<ACLDataPacket> which has a more efficient
-  //     memory layout.
   using InspectableDataPacketQueue = Inspectable<DataPacketQueue, inspect::UintProperty, size_t>;
   InspectableDataPacketQueue send_queue_{std::mem_fn(&DataPacketQueue::size)};
 
@@ -290,7 +286,6 @@
 }
 
 AclDataChannelImpl::~AclDataChannelImpl() {
-
   bt_log(INFO, "hci", "AclDataChannel shutting down");
 
   transport_->command_channel()->RemoveEventHandler(num_completed_packets_event_handler_id_);
@@ -331,23 +326,23 @@
 bool AclDataChannelImpl::SendPacket(ACLDataPacketPtr data_packet, UniqueChannelId channel_id,
                                     PacketPriority priority) {
   ZX_ASSERT(data_packet);
-  LinkedList<ACLDataPacket> packets;
+  std::list<ACLDataPacketPtr> packets;
   packets.push_back(std::move(data_packet));
   return SendPackets(std::move(packets), channel_id, priority);
 }
 
-bool AclDataChannelImpl::SendPackets(LinkedList<ACLDataPacket> packets, UniqueChannelId channel_id,
-                                     PacketPriority priority) {
-  if (packets.is_empty()) {
+bool AclDataChannelImpl::SendPackets(std::list<ACLDataPacketPtr> packets,
+                                     UniqueChannelId channel_id, PacketPriority priority) {
+  if (packets.empty()) {
     bt_log(DEBUG, "hci", "no packets to send!");
     return false;
   }
 
-  auto handle = packets.front().connection_handle();
+  auto handle = packets.front()->connection_handle();
 
   if (registered_links_.find(handle) == registered_links_.end()) {
     bt_log(TRACE, "hci", "dropping packets for unregistered connection (handle: %#.4x, count: %lu)",
-           handle, packets.size_slow());
+           handle, packets.size());
     return false;
   }
 
@@ -355,27 +350,28 @@
   // continuing fragment at the head. There is no check for whether |packets| have enough data to
   // form whole PDUs because queue management doesn't require that and it would break abstraction
   // even more.
-  ZX_ASSERT_MSG(packets.front().packet_boundary_flag() !=
+  ZX_ASSERT_MSG(packets.front()->packet_boundary_flag() !=
                     hci_spec::ACLPacketBoundaryFlag::kContinuingFragment,
                 "expected full PDU");
 
   for (const auto& packet : packets) {
     // This call assumes that all packets in each call are for the same connection
-    ZX_ASSERT_MSG(packet.connection_handle() == handle,
+    ZX_ASSERT_MSG(packet->connection_handle() == handle,
                   "expected only fragments for one connection (%#.4x, got %#.4x)", handle,
-                  packet.connection_handle());
+                  packet->connection_handle());
 
     // Make sure that all packets are within the MTU.
-    if (packet.view().payload_size() >
-        GetBufferMtu(registered_links_[packet.connection_handle()])) {
+    if (packet->view().payload_size() >
+        GetBufferMtu(registered_links_[packet->connection_handle()])) {
       bt_log(ERROR, "hci", "ACL data packet too large!");
       return false;
     }
   }
 
   auto insert_iter = SendQueueInsertLocationForPriority(priority);
-  for (int i = 0; !packets.is_empty(); i++) {
-    auto packet = packets.pop_front();
+  for (int i = 0; !packets.empty(); i++) {
+    auto packet = std::move(packets.front());
+    packets.pop_front();
     auto ll_type = registered_links_[packet->connection_handle()];
     const size_t payload_size = packet->view().payload_size();
     auto queue_packet = QueuedDataPacket(ll_type, channel_id, priority, std::move(packet),
diff --git a/pw_bluetooth_sapphire/host/transport/acl_data_channel.h b/pw_bluetooth_sapphire/host/transport/acl_data_channel.h
index 863b978..f20c568 100644
--- a/pw_bluetooth_sapphire/host/transport/acl_data_channel.h
+++ b/pw_bluetooth_sapphire/host/transport/acl_data_channel.h
@@ -111,7 +111,7 @@
   // |priority| indicates the order this packet should be dispatched off of the queue relative to
   // packets of other priorities. Note that high priority packets may still wait behind low priority
   // packets that have already been sent to the controller.
-  virtual bool SendPackets(LinkedList<ACLDataPacket> packets, UniqueChannelId channel_id,
+  virtual bool SendPackets(std::list<ACLDataPacketPtr> packets, UniqueChannelId channel_id,
                            PacketPriority priority) = 0;
 
   // Allowlist packets destined for the link identified by |handle| (of link type |ll_type|) for
diff --git a/pw_bluetooth_sapphire/host/transport/acl_data_channel_unittest.cc b/pw_bluetooth_sapphire/host/transport/acl_data_channel_unittest.cc
index 08f6de0..3ab3854 100644
--- a/pw_bluetooth_sapphire/host/transport/acl_data_channel_unittest.cc
+++ b/pw_bluetooth_sapphire/host/transport/acl_data_channel_unittest.cc
@@ -375,11 +375,12 @@
   acl_data_channel()->RegisterLink(kHandle, bt::LinkType::kACL);
 
   // Empty packet list.
-  EXPECT_FALSE(acl_data_channel()->SendPackets(
-      LinkedList<ACLDataPacket>(), l2cap::kInvalidChannelId, AclDataChannel::PacketPriority::kLow));
+  EXPECT_FALSE(acl_data_channel()->SendPackets(std::list<ACLDataPacketPtr>(),
+                                               l2cap::kInvalidChannelId,
+                                               AclDataChannel::PacketPriority::kLow));
 
   // Packet exceeds MTU
-  LinkedList<ACLDataPacket> packets;
+  std::list<ACLDataPacketPtr> packets;
   packets.push_back(ACLDataPacket::New(kHandle, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable,
                                        hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMTU + 1));
   EXPECT_FALSE(acl_data_channel()->SendPackets(std::move(packets), l2cap::kInvalidChannelId,
@@ -396,7 +397,7 @@
 
   acl_data_channel()->RegisterLink(kHandle, bt::LinkType::kACL);
 
-  LinkedList<ACLDataPacket> packets;
+  std::list<ACLDataPacketPtr> packets;
   packets.push_back(ACLDataPacket::New(kHandle,
                                        hci_spec::ACLPacketBoundaryFlag::kContinuingFragment,
                                        hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMTU));
@@ -416,7 +417,7 @@
   acl_data_channel()->RegisterLink(kHandle1, bt::LinkType::kACL);
 
   // Packet exceeds MTU
-  LinkedList<ACLDataPacket> packets;
+  std::list<ACLDataPacketPtr> packets;
   packets.push_back(ACLDataPacket::New(kHandle0,
                                        hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable,
                                        hci_spec::ACLBroadcastFlag::kPointToPoint, kMaxMTU));
@@ -456,7 +457,7 @@
   };
   test_device()->SetDataCallback(data_cb, dispatcher());
 
-  LinkedList<ACLDataPacket> packets;
+  std::list<ACLDataPacketPtr> packets;
   for (int i = 1; i <= kExpectedPacketCount; ++i) {
     auto packet = ACLDataPacket::New(kHandle, hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable,
                                      hci_spec::ACLBroadcastFlag::kPointToPoint, 1);
@@ -1318,7 +1319,7 @@
   // Send enough data that the first PDU sent in this loop gets dropped
   for (size_t i = 0; i < AclDataChannel::kMaxAclPacketsPerChannel + 1; ++i) {
     // Send two fragments per PDU
-    LinkedList<ACLDataPacket> packets;
+    std::list<ACLDataPacketPtr> packets;
     for (auto pbf : {hci_spec::ACLPacketBoundaryFlag::kFirstNonFlushable,
                      hci_spec::ACLPacketBoundaryFlag::kContinuingFragment}) {
       auto packet =
diff --git a/pw_bluetooth_sapphire/host/transport/mock_acl_data_channel.h b/pw_bluetooth_sapphire/host/transport/mock_acl_data_channel.h
index 438b929..fc3f919 100644
--- a/pw_bluetooth_sapphire/host/transport/mock_acl_data_channel.h
+++ b/pw_bluetooth_sapphire/host/transport/mock_acl_data_channel.h
@@ -19,7 +19,7 @@
   void set_le_buffer_info(DataBufferInfo info) { le_buffer_info_ = info; }
 
   using SendPacketsCallback = fit::function<bool(
-      LinkedList<ACLDataPacket> packets, UniqueChannelId channel_id, PacketPriority priority)>;
+      std::list<ACLDataPacketPtr> packets, UniqueChannelId channel_id, PacketPriority priority)>;
   void set_send_packets_cb(SendPacketsCallback cb) { send_packets_cb_ = std::move(cb); }
 
   using DropQueuedPacketsCallback = fit::function<void(AclPacketPredicate predicate)>;
@@ -55,7 +55,7 @@
                   PacketPriority priority) override {
     return false;
   }
-  bool SendPackets(LinkedList<ACLDataPacket> packets, UniqueChannelId channel_id,
+  bool SendPackets(std::list<ACLDataPacketPtr> packets, UniqueChannelId channel_id,
                    PacketPriority priority) override {
     if (send_packets_cb_) {
       return send_packets_cb_(std::move(packets), channel_id, priority);
diff --git a/pw_bluetooth_sapphire/host/transport/packet.h b/pw_bluetooth_sapphire/host/transport/packet.h
index e93a8f9..084ae11 100644
--- a/pw_bluetooth_sapphire/host/transport/packet.h
+++ b/pw_bluetooth_sapphire/host/transport/packet.h
@@ -12,7 +12,6 @@
 #include <fbl/macros.h>
 
 #include "src/connectivity/bluetooth/core/bt-host/common/byte_buffer.h"
-#include "src/connectivity/bluetooth/core/bt-host/common/linked_list.h"
 #include "src/connectivity/bluetooth/core/bt-host/common/packet_view.h"
 
 namespace bt::hci {
@@ -88,11 +87,10 @@
 //
 //   Packet is NOT thread-safe without external locking.
 
-// PacketBase provides basic view functionality and intrusively
-// linked-listability. Intended to be inherited by the Packet template and all
-// of its specializations.
+// PacketBase provides basic view functionality. Intended to be inherited by the Packet template and
+// all of its specializations.
 template <typename HeaderType, typename T>
-class PacketBase : public LinkedListable<T> {
+class PacketBase {
  public:
   virtual ~PacketBase() = default;
 
diff --git a/pw_bluetooth_sapphire/host/transport/slab_allocators_unittest.cc b/pw_bluetooth_sapphire/host/transport/slab_allocators_unittest.cc
index 9dc0f61..a57ada6 100644
--- a/pw_bluetooth_sapphire/host/transport/slab_allocators_unittest.cc
+++ b/pw_bluetooth_sapphire/host/transport/slab_allocators_unittest.cc
@@ -5,6 +5,7 @@
 #include "slab_allocators.h"
 
 #include <forward_list>
+#include <list>
 
 #include <gtest/gtest.h>
 
@@ -32,7 +33,7 @@
 
 TEST(SlabAllocatorsTest, CommandPacketFallBack) {
   size_t num_packets = 0;
-  LinkedList<Packet<hci_spec::CommandHeader>> packets;
+  std::list<std::unique_ptr<hci::CommandPacket>> packets;
 
   // Allocate a lot of small packets. We should be able to allocate two
   // allocators' worth of packets until we fail.
@@ -68,7 +69,7 @@
                                  kMaxNumSlabs * kNumMediumACLDataPackets +
                                  kMaxNumSlabs * kNumLargeACLDataPackets;
   const size_t kPayloadSize = 5;
-  LinkedList<Packet<hci_spec::ACLDataHeader>> packets;
+  std::list<hci::ACLDataPacketPtr> packets;
 
   for (size_t num_packets = 0; num_packets < kMaxSlabPackets; num_packets++) {
     auto packet = ACLDataPacket::New(kPayloadSize);
@@ -91,7 +92,7 @@
   // Maximum number of packets we can expect to obtain from the large slab allocator.
   const size_t kMaxSlabPackets = kMaxNumSlabs * kNumLargeACLDataPackets;
   const size_t kPayloadSize = kLargeACLDataPayloadSize;
-  LinkedList<Packet<hci_spec::ACLDataHeader>> packets;
+  std::list<hci::ACLDataPacketPtr> packets;
 
   for (size_t num_packets = 0; num_packets < kMaxSlabPackets; num_packets++) {
     auto packet = ACLDataPacket::New(kPayloadSize);