Merge "Merge QQ1A.191205.011 into stage-aosp-master" into stage-aosp-master
diff --git a/gd/cert/gd_cert_device.py b/gd/cert/gd_cert_device.py
index b730b50..b579258 100644
--- a/gd/cert/gd_cert_device.py
+++ b/gd/cert/gd_cert_device.py
@@ -69,7 +69,7 @@
         self.hal = hal_cert_pb2_grpc.HciHalCertStub(self.grpc_channel)
         self.controller_read_only_property = cert_rootservice_pb2_grpc.ReadOnlyPropertyStub(self.grpc_channel)
         self.hci = hci_cert_pb2_grpc.AclManagerCertStub(self.grpc_channel)
-        self.l2cap = l2cap_cert_pb2_grpc.L2capModuleCertStub(self.grpc_channel)
+        self.l2cap = l2cap_cert_pb2_grpc.L2capClassicModuleCertStub(self.grpc_channel)
 
         # Event streams
         self.hal.hci_event_stream = EventStream(self.hal.FetchHciEvent)
diff --git a/gd/cert/gd_device.py b/gd/cert/gd_device.py
index 2cd49b7..17454f3 100644
--- a/gd/cert/gd_device.py
+++ b/gd/cert/gd_device.py
@@ -70,7 +70,7 @@
         self.controller_read_only_property = facade_rootservice_pb2_grpc.ReadOnlyPropertyStub(self.grpc_channel)
         self.hci = hci_facade_pb2_grpc.AclManagerFacadeStub(self.grpc_channel)
         self.hci_classic_security = hci_facade_pb2_grpc.ClassicSecurityManagerFacadeStub(self.grpc_channel)
-        self.l2cap = l2cap_facade_pb2_grpc.L2capModuleFacadeStub(self.grpc_channel)
+        self.l2cap = l2cap_facade_pb2_grpc.L2capClassicModuleFacadeStub(self.grpc_channel)
 
         # Event streams
         self.hal.hci_event_stream = EventStream(self.hal.FetchHciEvent)
diff --git a/gd/hci/hci_packets.pdl b/gd/hci/hci_packets.pdl
index c048c80..e3ee19e 100644
--- a/gd/hci/hci_packets.pdl
+++ b/gd/hci/hci_packets.pdl
@@ -2600,23 +2600,177 @@
 }
 
 packet LeSetExtendedAdvertisingRandomAddress : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_RANDOM_ADDRESS) {
-  _payload_,  // placeholder (unimplemented)
+  advertising_handle : 8,
+  advertising_random_address : Address,
+}
+
+packet LeSetExtendedAdvertisingRandomAddressComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_ADVERTISING_RANDOM_ADDRESS) {
+  status : ErrorCode,
+}
+
+// The lower 4 bits of the advertising event properties
+enum LegacyAdvertisingProperties : 4 {
+  ADV_IND = 0x3,
+  ADV_DIRECT_IND_LOW = 0x5,
+  ADV_DIRECT_IND_HIGH = 0xD,
+  ADV_SCAN_IND = 0x2,
+  ADV_NONCONN_IND = 0,
+}
+
+enum PrimaryPhyType : 8 {
+  LE_1M = 0x01,
+  LE_CODED = 0x03,
+}
+
+enum SecondaryPhyType : 8 {
+  NO_PACKETS = 0x00,
+  LE_1M = 0x01,
+  LE_2M = 0x02,
+  LE_CODED = 0x03,
+}
+
+packet LeSetExtendedAdvertisingLegacyParameters : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_PARAMETERS) {
+  advertising_handle : 8,
+  advertising_event_legacy_properties : LegacyAdvertisingProperties,
+  _fixed_ = 0x1 : 1, // legacy bit set
+  _reserved_ : 11, // advertising_event_properties
+  primary_advertising_interval_min : 24, // 0x20 - 0xFFFFFF N * 0.625 ms
+  primary_advertising_interval_max : 24, // 0x20 - 0xFFFFFF N * 0.625 ms
+  primary_advertising_channel_map : 3,  // bit 0 - Channel 37, bit 1 - 38, bit 2 - 39
+  _reserved_ : 5,
+  own_address_type : OwnAddressType,
+  peer_address_type : PeerAddressType,
+  peer_address : Address,
+  advertising_filter_policy : AdvertisingFilterPolicy,
+  _reserved_ : 6,
+  advertising_tx_power : 8, // -127 to +20, 0x7F - no preference
+  primary_advertising_phy : PrimaryPhyType,
+  _reserved_ : 8, // secondary_advertising_max_skip
+  _reserved_ : 8, // secondary_advertising_phy
+  advertising_sid : 8, // SID subfield from the ADI field of the PDU
+  scan_request_notification_enable : Enable,
 }
 
 packet LeSetExtendedAdvertisingParameters : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_PARAMETERS) {
-  _payload_,  // placeholder (unimplemented)
+  advertising_handle : 8,
+  advertising_event_legacy_properties : 4,
+  _fixed_ = 0 : 1, // legacy bit cleared
+  advertising_event_properties : 3,
+  _reserved_ : 8,
+  primary_advertising_interval_min : 24, // 0x20 - 0xFFFFFF N * 0.625 ms
+  primary_advertising_interval_max : 24, // 0x20 - 0xFFFFFF N * 0.625 ms
+  primary_advertising_channel_map : 3,  // bit 0 - Channel 37, bit 1 - 38, bit 2 - 39
+  _reserved_ : 5,
+  own_address_type : OwnAddressType,
+  peer_address_type : PeerAddressType,
+  peer_address : Address,
+  advertising_filter_policy : AdvertisingFilterPolicy,
+  _reserved_ : 6,
+  advertising_tx_power : 8, // -127 to +20, 0x7F - no preference
+  primary_advertising_phy : PrimaryPhyType,
+  secondary_advertising_max_skip : 8, // 1 to 255, 0x00 - AUX_ADV_IND sent before next advertising event
+  secondary_advertising_phy : SecondaryPhyType,
+  advertising_sid : 8, // SID subfield from the ADI field of the PDU
+  scan_request_notification_enable : Enable,
+}
+
+packet LeSetExtendedAdvertisingParametersComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_ADVERTISING_PARAMETERS) {
+  status : ErrorCode,
+  selected_tx_power : 8, // -127 to +20
+}
+
+enum Operation : 3 {
+  INTERMEDIATE_FRAGMENT = 0,
+  FIRST_FRAGMENT = 1,
+  LAST_FRAGMENT = 2,
+  COMPLETE_ADVERTISMENT = 3,
+  UNCHANGED_DATA = 4,
+}
+
+enum FragmentPreference : 1 {
+  CONTROLLER_MAY_FRAGMENT = 0,
+  CONTROLLER_SHOULD_NOT = 1,
 }
 
 packet LeSetExtendedAdvertisingData : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_DATA) {
-  _payload_,  // placeholder (unimplemented)
+  advertising_handle : 8,
+  operation : Operation,
+  _reserved_ : 5,
+  fragment_preference : FragmentPreference,
+  _reserved_ : 7,
+  _size_(advertising_data) : 8,
+  advertising_data : GapData[],
+}
+
+packet LeSetExtendedAdvertisingDataRaw : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_DATA) {
+  advertising_handle : 8,
+  operation : Operation,
+  _reserved_ : 5,
+  fragment_preference : FragmentPreference,
+  _reserved_ : 7,
+  _size_(advertising_data) : 8,
+  advertising_data : 8[],
+}
+
+packet LeSetExtendedAdvertisingDataComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_ADVERTISING_DATA) {
+  status : ErrorCode,
 }
 
 packet LeSetExtendedAdvertisingScanResponse : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_SCAN_RESPONSE) {
-  _payload_,  // placeholder (unimplemented)
+  advertising_handle : 8,
+  operation : Operation,
+  _reserved_ : 5,
+  fragment_preference : FragmentPreference,
+  _reserved_ : 7,
+  _size_(scan_response_data) : 8,
+  scan_response_data : GapData[],
+}
+
+packet LeSetExtendedAdvertisingScanResponseRaw : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_SCAN_RESPONSE) {
+  advertising_handle : 8,
+  operation : Operation,
+  _reserved_ : 5,
+  fragment_preference : FragmentPreference,
+  _reserved_ : 7,
+  _size_(scan_response_data) : 8,
+  scan_response_data : 8[],
+}
+
+packet LeSetExtendedAdvertisingScanResponseComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_ADVERTISING_SCAN_RESPONSE) {
+  status : ErrorCode,
+}
+
+packet LeSetExtendedAdvertisingEnableDisableAll : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_ENABLE) {
+  _fixed_ = 0x00 : 8, // Enable::DISABLED
+  _fixed_ = 0x00 : 8, // Disable all sets
+}
+
+struct EnabledSet {
+  advertising_handle : 8,
+  duration : 16,
+  max_extended_advertising_events : 8,
+}
+
+struct DisabledSet {
+  advertising_handle : 8,
+  _fixed_ = 0x00 : 16, // duration
+  _fixed_ = 0x00 : 8, // max_extended_advertising_events
+}
+
+packet LeSetExtendedAdvertisingDisable : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_ENABLE) {
+  _fixed_ = 0x00 : 8, // Enable::DISABLED
+  _count_(disabled_sets) : 8,
+  disabled_sets : DisabledSet[],
 }
 
 packet LeSetExtendedAdvertisingEnable : LeAdvertisingCommand (op_code = LE_SET_EXTENDED_ADVERTISING_ENABLE) {
-  _payload_,  // placeholder (unimplemented)
+  enable : Enable,
+  _count_(enabled_sets) : 8,
+  enabled_sets : EnabledSet[],
+}
+
+packet LeSetExtendedAdvertisingEnableComplete : CommandComplete (command_op_code = LE_SET_EXTENDED_ADVERTISING_ENABLE) {
+  status : ErrorCode,
 }
 
 packet LeReadMaximumAdvertisingDataLength : CommandPacket (op_code = LE_READ_MAXIMUM_ADVERTISING_DATA_LENGTH) {
@@ -2636,23 +2790,52 @@
 }
 
 packet LeRemoveAdvertisingSet : LeAdvertisingCommand (op_code = LE_REMOVE_ADVERTISING_SET) {
-  _payload_,  // placeholder (unimplemented)
+  advertising_handle : 8,
+}
+
+packet LeRemoveAdvertisingSetComplete : CommandComplete (command_op_code = LE_REMOVE_ADVERTISING_SET) {
+  status : ErrorCode,
 }
 
 packet LeClearAdvertisingSets : LeAdvertisingCommand (op_code = LE_CLEAR_ADVERTISING_SETS) {
-  _payload_,  // placeholder (unimplemented)
+}
+
+packet LeClearAdvertisingSetsComplete : CommandComplete (command_op_code = LE_CLEAR_ADVERTISING_SETS) {
+  status : ErrorCode,
 }
 
 packet LeSetPeriodicAdvertisingParam : LeAdvertisingCommand (op_code = LE_SET_PERIODIC_ADVERTISING_PARAM) {
-  _payload_,  // placeholder (unimplemented)
+  advertising_handle : 8,
+  periodic_advertising_interval_min : 16, // 0x006 to 0xFFFF (7.5 ms to 82s)
+  periodic_advertising_interval_max : 16, // 0x006 to 0xFFFF (7.5 ms to 82s)
+  _reserved_ : 6,
+  include_tx_power : 1,
+  _reserved_ : 9,
+}
+
+packet LeSetPeriodicAdvertisingParamComplete : CommandComplete (command_op_code = LE_SET_PERIODIC_ADVERTISING_PARAM) {
+  status : ErrorCode,
 }
 
 packet LeSetPeriodicAdvertisingData : LeAdvertisingCommand (op_code = LE_SET_PERIODIC_ADVERTISING_DATA) {
-  _payload_,  // placeholder (unimplemented)
+  advertising_handle : 8,
+  operation : Operation,
+  _reserved_ : 5,
+  _size_(scan_response_data) : 8,
+  scan_response_data : 8[],
+}
+
+packet LeSetPeriodicAdvertisingDataComplete : CommandComplete (command_op_code = LE_SET_PERIODIC_ADVERTISING_DATA) {
+  status : ErrorCode,
 }
 
 packet LeSetPeriodicAdvertisingEnable : LeAdvertisingCommand (op_code = LE_SET_PERIODIC_ADVERTISING_ENABLE) {
-  _payload_,  // placeholder (unimplemented)
+  advertising_handle : 8,
+  enable : Enable,
+}
+
+packet LeSetPeriodicAdvertisingEnableComplete : CommandComplete (command_op_code = LE_SET_PERIODIC_ADVERTISING_ENABLE) {
+  status : ErrorCode,
 }
 
 struct PhyScanParameters {
@@ -3427,19 +3610,6 @@
   RESERVED = 0x3,
 }
 
-enum PrimaryPhyType : 8 {
-  LE_1M = 0x01,
-  LE_CODED = 0x03,
-}
-
-enum SecondaryPhyType : 8 {
-  NO_PACKETS = 0x00,
-  LE_1M = 0x01,
-  LE_2M = 0x02,
-  LE_CODED = 0x03,
-}
-
-
 struct LeExtendedAdvertisingReport {
   connectable : 1,
   scannable : 1,
diff --git a/gd/hci/hci_packets_test.cc b/gd/hci/hci_packets_test.cc
index 6eb0d13..48a929c 100644
--- a/gd/hci/hci_packets_test.cc
+++ b/gd/hci/hci_packets_test.cc
@@ -418,5 +418,181 @@
 };
 DEFINE_AND_INSTANTIATE_LeSetExtendedScanEnableCompleteReflectionTest(le_set_extended_scan_enable_complete);
 
+std::vector<uint8_t> le_extended_create_connection = {
+    0x43, 0x20, 0x2a, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x08,
+    0x30, 0x00, 0x18, 0x00, 0x28, 0x00, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x08, 0x30, 0x00, 0x18, 0x00, 0x28, 0x00, 0x00, 0x00, 0xf4, 0x01, 0x00, 0x00, 0x00, 0x00};
+DEFINE_AND_INSTANTIATE_LeExtendedCreateConnectionReflectionTest(le_extended_create_connection);
+
+TEST(HciPacketsTest, testLeExtendedCreateConnection) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(le_extended_create_connection);
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = LeExtendedCreateConnectionView::Create(
+      LeConnectionManagementCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+  ASSERT_TRUE(view.IsValid());
+}
+
+std::vector<uint8_t> le_set_extended_advertising_random_address = {
+    0x35, 0x20, 0x07, 0x00, 0x77, 0x58, 0xeb, 0xd3, 0x1c, 0x6e,
+};
+
+TEST(HciPacketsTest, testLeSetExtendedAdvertisingRandomAddress) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(le_set_extended_advertising_random_address);
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = LeSetExtendedAdvertisingRandomAddressView::Create(
+      LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+  ASSERT_TRUE(view.IsValid());
+  uint8_t random_address_bytes[] = {0x77, 0x58, 0xeb, 0xd3, 0x1c, 0x6e};
+  ASSERT_EQ(0, view.GetAdvertisingHandle());
+  ASSERT_EQ(Address(random_address_bytes), view.GetAdvertisingRandomAddress());
+}
+DEFINE_AND_INSTANTIATE_LeSetExtendedAdvertisingRandomAddressReflectionTest(le_set_extended_advertising_random_address);
+
+std::vector<uint8_t> le_set_extended_advertising_random_address_complete{
+    0x0e, 0x04, 0x01, 0x35, 0x20, 0x00,
+};
+DEFINE_AND_INSTANTIATE_LeSetExtendedAdvertisingRandomAddressCompleteReflectionTest(
+    le_set_extended_advertising_random_address_complete);
+
+std::vector<uint8_t> le_set_extended_advertising_data{
+    0x37, 0x20, 0x12, 0x00, 0x03, 0x01, 0x0e, 0x02, 0x01, 0x02, 0x0a,
+    0x09, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x20, 0x33, 0x20, 0x58,
+};
+TEST(HciPacketsTest, testLeSetExtendedAdvertisingData) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(le_set_extended_advertising_data);
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = LeSetExtendedAdvertisingDataRawView::Create(
+      LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+  ASSERT_TRUE(view.IsValid());
+  ASSERT_EQ(0, view.GetAdvertisingHandle());
+  ASSERT_EQ(Operation::COMPLETE_ADVERTISMENT, view.GetOperation());
+  ASSERT_EQ(FragmentPreference::CONTROLLER_SHOULD_NOT, view.GetFragmentPreference());
+  std::vector<uint8_t> advertising_data{
+      0x02, 0x01, 0x02, 0x0a, 0x09, 0x50, 0x69, 0x78, 0x65, 0x6c, 0x20, 0x33, 0x20, 0x58,
+  };
+  ASSERT_EQ(advertising_data, view.GetAdvertisingData());
+}
+
+DEFINE_AND_INSTANTIATE_LeSetExtendedAdvertisingDataRawReflectionTest(le_set_extended_advertising_data);
+
+std::vector<uint8_t> le_set_extended_advertising_data_complete{
+    0x0e, 0x04, 0x01, 0x37, 0x20, 0x00,
+};
+DEFINE_AND_INSTANTIATE_LeSetExtendedAdvertisingDataCompleteReflectionTest(le_set_extended_advertising_data_complete);
+
+std::vector<uint8_t> le_set_extended_advertising_parameters_set_0{
+    0x36, 0x20, 0x19, 0x00, 0x13, 0x00, 0x90, 0x01, 0x00, 0xc2, 0x01, 0x00,          0x07, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x01, 0x00, 0x00 /*0x01*/, 0x01, 0x00,
+};
+TEST(HciPacketsTest, testLeSetExtendedAdvertisingParametersLegacySet0) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(le_set_extended_advertising_parameters_set_0);
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = LeSetExtendedAdvertisingLegacyParametersView::Create(
+      LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+  ASSERT_TRUE(view.IsValid());
+  ASSERT_EQ(0, view.GetAdvertisingHandle());
+  ASSERT_EQ(400, view.GetPrimaryAdvertisingIntervalMin());
+  ASSERT_EQ(450, view.GetPrimaryAdvertisingIntervalMax());
+  ASSERT_EQ(0x7, view.GetPrimaryAdvertisingChannelMap());
+  ASSERT_EQ(OwnAddressType::RANDOM_DEVICE_ADDRESS, view.GetOwnAddressType());
+  ASSERT_EQ(PeerAddressType::PUBLIC_DEVICE_OR_IDENTITY_ADDRESS, view.GetPeerAddressType());
+  ASSERT_EQ(Address::kEmpty, view.GetPeerAddress());
+  ASSERT_EQ(AdvertisingFilterPolicy::ALL_DEVICES, view.GetAdvertisingFilterPolicy());
+  ASSERT_EQ(PrimaryPhyType::LE_1M, view.GetPrimaryAdvertisingPhy());
+  ASSERT_EQ(1, view.GetAdvertisingSid());
+  ASSERT_EQ(Enable::DISABLED, view.GetScanRequestNotificationEnable());
+}
+
+std::vector<uint8_t> le_set_extended_advertising_parameters_set_1{
+    0x36, 0x20, 0x19, 0x01, 0x13, 0x00, 0x90, 0x01, 0x00, 0xc2, 0x01, 0x00,          0x07, 0x01,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf9, 0x01, 0x00, 0x00 /*0x01*/, 0x01, 0x00,
+};
+TEST(HciPacketsTest, testLeSetExtendedAdvertisingParametersSet1) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(le_set_extended_advertising_parameters_set_1);
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = LeSetExtendedAdvertisingLegacyParametersView::Create(
+      LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+  ASSERT_TRUE(view.IsValid());
+  ASSERT_EQ(1, view.GetAdvertisingHandle());
+  ASSERT_EQ(400, view.GetPrimaryAdvertisingIntervalMin());
+  ASSERT_EQ(450, view.GetPrimaryAdvertisingIntervalMax());
+  ASSERT_EQ(0x7, view.GetPrimaryAdvertisingChannelMap());
+  ASSERT_EQ(OwnAddressType::RANDOM_DEVICE_ADDRESS, view.GetOwnAddressType());
+  ASSERT_EQ(PeerAddressType::PUBLIC_DEVICE_OR_IDENTITY_ADDRESS, view.GetPeerAddressType());
+  ASSERT_EQ(Address::kEmpty, view.GetPeerAddress());
+  ASSERT_EQ(AdvertisingFilterPolicy::ALL_DEVICES, view.GetAdvertisingFilterPolicy());
+  ASSERT_EQ(PrimaryPhyType::LE_1M, view.GetPrimaryAdvertisingPhy());
+  ASSERT_EQ(1, view.GetAdvertisingSid());
+  ASSERT_EQ(Enable::DISABLED, view.GetScanRequestNotificationEnable());
+}
+
+DEFINE_AND_INSTANTIATE_LeSetExtendedAdvertisingLegacyParametersReflectionTest(
+    le_set_extended_advertising_parameters_set_0, le_set_extended_advertising_parameters_set_1);
+
+std::vector<uint8_t> le_set_extended_advertising_parameters_complete{0x0e, 0x05, 0x01, 0x36, 0x20, 0x00, 0xf5};
+TEST(HciPacketsTest, testLeSetExtendedAdvertisingParametersComplete) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(le_set_extended_advertising_parameters_complete);
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = LeSetExtendedAdvertisingParametersCompleteView::Create(
+      CommandCompleteView::Create(EventPacketView::Create(packet_bytes_view)));
+  ASSERT_TRUE(view.IsValid());
+  ASSERT_EQ(static_cast<uint8_t>(-11), view.GetSelectedTxPower());
+}
+
+DEFINE_AND_INSTANTIATE_LeSetExtendedAdvertisingParametersCompleteReflectionTest(
+    le_set_extended_advertising_parameters_complete);
+
+std::vector<uint8_t> le_remove_advertising_set_1{
+    0x3c,
+    0x20,
+    0x01,
+    0x01,
+};
+TEST(HciPacketsTest, testLeRemoveAdvertisingSet1) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(le_remove_advertising_set_1);
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = LeRemoveAdvertisingSetView::Create(
+      LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+  ASSERT_TRUE(view.IsValid());
+  ASSERT_EQ(1, view.GetAdvertisingHandle());
+}
+
+DEFINE_AND_INSTANTIATE_LeRemoveAdvertisingSetReflectionTest(le_remove_advertising_set_1);
+
+std::vector<uint8_t> le_remove_advertising_set_complete{
+    0x0e, 0x04, 0x01, 0x3c, 0x20, 0x00,
+};
+DEFINE_AND_INSTANTIATE_LeRemoveAdvertisingSetCompleteReflectionTest(le_remove_advertising_set_complete);
+
+std::vector<uint8_t> le_set_extended_advertising_disable_1{
+    0x39, 0x20, 0x06, 0x00, 0x01, 0x01, 0x00, 0x00, 0x00,
+};
+TEST(HciPacketsTest, testLeSetExtendedAdvertisingDisable1) {
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes =
+      std::make_shared<std::vector<uint8_t>>(le_set_extended_advertising_disable_1);
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = LeSetExtendedAdvertisingDisableView::Create(
+      LeAdvertisingCommandView::Create(CommandPacketView::Create(packet_bytes_view)));
+  ASSERT_TRUE(view.IsValid());
+  auto disabled_set = view.GetDisabledSets();
+  ASSERT_EQ(1, disabled_set.size());
+  ASSERT_EQ(1, disabled_set[0].advertising_handle_);
+}
+
+DEFINE_AND_INSTANTIATE_LeSetExtendedAdvertisingDisableReflectionTest(le_set_extended_advertising_disable_1);
+
+std::vector<uint8_t> le_set_extended_advertising_enable_complete{
+    0x0e, 0x04, 0x01, 0x39, 0x20, 0x00,
+};
+DEFINE_AND_INSTANTIATE_LeSetExtendedAdvertisingEnableCompleteReflectionTest(
+    le_set_extended_advertising_enable_complete);
+
 }  // namespace hci
 }  // namespace bluetooth
diff --git a/gd/l2cap/Android.bp b/gd/l2cap/Android.bp
index 3d65446..60175e2 100644
--- a/gd/l2cap/Android.bp
+++ b/gd/l2cap/Android.bp
@@ -19,6 +19,7 @@
         "classic/l2cap_classic_module.cc",
         "internal/basic_mode_channel_data_controller.cc",
         "internal/enhanced_retransmission_mode_channel_data_controller.cc",
+        "internal/le_credit_based_channel_data_controller.cc",
         "internal/receiver.cc",
         "internal/scheduler_fifo.cc",
         "internal/sender.cc",
@@ -45,6 +46,7 @@
         "internal/basic_mode_channel_data_controller_test.cc",
         "internal/enhanced_retransmission_mode_channel_data_controller_test.cc",
         "internal/fixed_channel_allocator_test.cc",
+        "internal/le_credit_based_channel_data_controller_test.cc",
         "internal/receiver_test.cc",
         "internal/scheduler_fifo_test.cc",
         "internal/sender_test.cc",
diff --git a/gd/l2cap/classic/cert/api.proto b/gd/l2cap/classic/cert/api.proto
index 687686f..a80e648 100644
--- a/gd/l2cap/classic/cert/api.proto
+++ b/gd/l2cap/classic/cert/api.proto
@@ -7,6 +7,8 @@
 
 service L2capClassicModuleCert {
   rpc SendL2capPacket(L2capPacket) returns (google.protobuf.Empty) {}
+  rpc SendIFrame(IFrame) returns (SendIFrameResult) {}
+  rpc SendSFrame(SFrame) returns (SendSFrameResult) {}
 
   rpc SetupLink(SetupLinkRequest) returns (SetupLinkResponse) {}
   rpc DisconnectLink(DisconnectLinkRequest) returns (google.protobuf.Empty) {}
@@ -33,6 +35,30 @@
   bytes payload = 3;
 }
 
+message IFrame {
+  facade.BluetoothAddress remote = 1;
+  uint32 channel = 2;
+  uint32 sar = 3;
+  uint32 tx_seq = 4;
+  uint32 req_seq = 5;
+  uint32 f = 6;
+  uint32 sdu_size = 7;
+  bytes information = 8;
+}
+
+message SendIFrameResult {}
+
+message SFrame {
+  facade.BluetoothAddress remote = 1;
+  uint32 channel = 2;
+  uint32 req_seq = 3;
+  uint32 f = 4;
+  uint32 p = 5;
+  uint32 s = 6;
+}
+
+message SendSFrameResult {}
+
 message DisconnectLinkRequest {
   facade.BluetoothAddress remote = 1;
 }
diff --git a/gd/l2cap/classic/cert/cert.cc b/gd/l2cap/classic/cert/cert.cc
index 2de345c..abb3561 100644
--- a/gd/l2cap/classic/cert/cert.cc
+++ b/gd/l2cap/classic/cert/cert.cc
@@ -80,6 +80,37 @@
     return ::grpc::Status::OK;
   }
 
+  ::grpc::Status SendIFrame(::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::IFrame* request,
+                            ::bluetooth::l2cap::classic::cert::SendIFrameResult* response) override {
+    std::unique_ptr<RawBuilder> packet = std::make_unique<RawBuilder>();
+    auto req_string = request->information();
+    packet->AddOctets(std::vector<uint8_t>(req_string.begin(), req_string.end()));
+    std::unique_ptr<BasePacketBuilder> l2cap_builder;
+    auto f = static_cast<Final>(request->f());
+    if (request->sar() == static_cast<int>(SegmentationAndReassembly::START)) {
+      l2cap_builder = EnhancedInformationStartFrameBuilder::Create(
+          request->channel(), request->tx_seq(), f, request->req_seq(), request->sdu_size(), std::move(packet));
+    } else {
+      l2cap_builder = EnhancedInformationFrameBuilder::Create(
+          request->channel(), request->tx_seq(), f, request->req_seq(),
+          static_cast<SegmentationAndReassembly>(request->sar()), std::move(packet));
+    }
+    outgoing_packet_queue_.push(std::move(l2cap_builder));
+    send_packet_from_queue();
+    return ::grpc::Status::OK;
+  }
+
+  ::grpc::Status SendSFrame(::grpc::ServerContext* context, const ::bluetooth::l2cap::classic::cert::SFrame* request,
+                            ::bluetooth::l2cap::classic::cert::SendSFrameResult* response) override {
+    auto f = static_cast<Final>(request->f());
+    auto p = static_cast<Poll>(request->p());
+    auto s = static_cast<SupervisoryFunction>(request->s());
+    auto builder = EnhancedSupervisoryFrameBuilder::Create(request->channel(), s, p, f, request->req_seq());
+    outgoing_packet_queue_.push(std::move(builder));
+    send_packet_from_queue();
+    return ::grpc::Status::OK;
+  }
+
   ::grpc::Status SendConnectionRequest(::grpc::ServerContext* context, const cert::ConnectionRequest* request,
                                        ::google::protobuf::Empty* response) override {
     auto builder = ConnectionRequestBuilder::Create(request->signal_id(), request->psm(), request->scid());
diff --git a/gd/l2cap/classic/cert/simple_l2cap_test.py b/gd/l2cap/classic/cert/simple_l2cap_test.py
index 38d39b6..607cfb2 100644
--- a/gd/l2cap/classic/cert/simple_l2cap_test.py
+++ b/gd/l2cap/classic/cert/simple_l2cap_test.py
@@ -215,6 +215,7 @@
             log = log.data_packet
             if (log.channel == scid):
                 log.payload = basic_frame_to_enhanced_information_frame(log.payload)
+                self.cert_device.l2cap.SendSFrame(l2cap_cert_pb2.SFrame(channel=self.scid_dcid_map[scid], req_seq=1, s=0))
             data_received.append((log.channel, log.payload))
         event_handler.on(lambda log : log.HasField("data_packet"), on_data_received)
         logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest())
@@ -222,10 +223,12 @@
         assert (2, b"123") in data_received
 
         self.device_under_test.l2cap.SendDynamicChannelPacket(l2cap_facade_pb2.DynamicChannelPacket(psm=0x33, payload=b'abc'*34))
+
+        self.cert_device.l2cap.SendIFrame(l2cap_cert_pb2.IFrame(channel=self.scid_dcid_map[scid], req_seq=1, tx_seq=0, sar=0, information=b"abcd"))
+
         logs = self.cert_device.l2cap.FetchL2capLog(l2cap_cert_pb2.FetchL2capLogRequest())
         event_handler.execute(logs)
         assert (scid, b"abc"*34) in data_received
-        self.cert_device.l2cap.StopFetchingL2capLog(l2cap_cert_pb2.StopFetchingL2capLogRequest())
 
     def test_connect_and_send_data(self):
         self.device_under_test.l2cap.RegisterChannel(l2cap_facade_pb2.RegisterChannelRequest(channel=2))
diff --git a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
index 2a1b930..97f9702 100644
--- a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.cc
@@ -168,20 +168,20 @@
     }
   }
 
-  void recv_i_frame(Final f, uint8_t tx_seq, uint8_t req_seq, SegmentationAndReassembly sar,
+  void recv_i_frame(Final f, uint8_t tx_seq, uint8_t req_seq, SegmentationAndReassembly sar, uint16_t sdu_size,
                     const packet::PacketView<true>& payload) {
     if (rx_state_ == RxState::RECV) {
       if (f == Final::NOT_SET && with_expected_tx_seq(tx_seq) && with_valid_req_seq(req_seq) && with_valid_f_bit(f) &&
           !local_busy()) {
         increment_expected_tx_seq();
         pass_to_tx(req_seq, f);
-        data_indication(sar, payload);
+        data_indication(sar, sdu_size, payload);
         send_ack(Final::NOT_SET);
       } else if (f == Final::POLL_RESPONSE && with_expected_tx_seq(tx_seq) && with_valid_req_seq(req_seq) &&
                  with_valid_f_bit(f) && !local_busy()) {
         increment_expected_tx_seq();
         pass_to_tx(req_seq, f);
-        data_indication(sar, payload);
+        data_indication(sar, sdu_size, payload);
         if (!rej_actioned_) {
           retransmit_i_frames(req_seq);
           send_pending_i_frames();
@@ -216,14 +216,14 @@
       if (f == Final::NOT_SET && with_expected_tx_seq(tx_seq) && with_valid_req_seq(req_seq) && with_valid_f_bit(f)) {
         increment_expected_tx_seq();
         pass_to_tx(req_seq, f);
-        data_indication(sar, payload);
+        data_indication(sar, sdu_size, payload);
         send_ack(Final::NOT_SET);
         rx_state_ = RxState::RECV;
       } else if (f == Final::POLL_RESPONSE && with_expected_tx_seq(tx_seq) && with_valid_req_seq(req_seq) &&
                  with_valid_f_bit(f)) {
         increment_expected_tx_seq();
         pass_to_tx(req_seq, f);
-        data_indication(sar, payload);
+        data_indication(sar, sdu_size, payload);
         if (!rej_actioned_) {
           retransmit_i_frames(req_seq);
           send_pending_i_frames();
@@ -682,8 +682,8 @@
     recv_f_bit(f);
   }
 
-  void data_indication(SegmentationAndReassembly sar, const packet::PacketView<true>& segment) {
-    controller_->stage_for_reassembly(sar, segment);
+  void data_indication(SegmentationAndReassembly sar, uint16_t sdu_size, const packet::PacketView<true>& segment) {
+    controller_->stage_for_reassembly(sar, sdu_size, segment);
     buffer_seq_ = (buffer_seq_ + 1) % kMaxTxWin;
   }
 
@@ -825,8 +825,21 @@
       LOG_WARN("Received invalid frame");
       return;
     }
-    pimpl_->recv_i_frame(i_frame_view.GetF(), i_frame_view.GetTxSeq(), i_frame_view.GetReqSeq(), i_frame_view.GetSar(),
-                         i_frame_view.GetPayload());
+    Final f = i_frame_view.GetF();
+    uint8_t tx_seq = i_frame_view.GetTxSeq();
+    uint8_t req_seq = i_frame_view.GetReqSeq();
+    auto sar = i_frame_view.GetSar();
+    if (sar == SegmentationAndReassembly::START) {
+      auto i_frame_start_view = EnhancedInformationStartFrameView::Create(i_frame_view);
+      if (!i_frame_start_view.IsValid()) {
+        LOG_WARN("Received invalid I-Frame START");
+        return;
+      }
+      pimpl_->recv_i_frame(f, tx_seq, req_seq, sar, i_frame_start_view.GetL2capSduLength(),
+                           i_frame_start_view.GetPayload());
+    } else {
+      pimpl_->recv_i_frame(f, tx_seq, req_seq, sar, 0, i_frame_view.GetPayload());
+    }
   } else if (type == FrameType::S_FRAME) {
     auto s_frame_view = EnhancedSupervisoryFrameView::Create(standard_frame_view);
     if (!s_frame_view.IsValid()) {
@@ -872,8 +885,21 @@
       LOG_WARN("Received invalid frame");
       return;
     }
-    pimpl_->recv_i_frame(i_frame_view.GetF(), i_frame_view.GetTxSeq(), i_frame_view.GetReqSeq(), i_frame_view.GetSar(),
-                         i_frame_view.GetPayload());
+    Final f = i_frame_view.GetF();
+    uint8_t tx_seq = i_frame_view.GetTxSeq();
+    uint8_t req_seq = i_frame_view.GetReqSeq();
+    auto sar = i_frame_view.GetSar();
+    if (sar == SegmentationAndReassembly::START) {
+      auto i_frame_start_view = EnhancedInformationStartFrameWithFcsView::Create(i_frame_view);
+      if (!i_frame_start_view.IsValid()) {
+        LOG_WARN("Received invalid I-Frame START");
+        return;
+      }
+      pimpl_->recv_i_frame(f, tx_seq, req_seq, sar, i_frame_start_view.GetL2capSduLength(),
+                           i_frame_start_view.GetPayload());
+    } else {
+      pimpl_->recv_i_frame(f, tx_seq, req_seq, sar, 0, i_frame_view.GetPayload());
+    }
   } else if (type == FrameType::S_FRAME) {
     auto s_frame_view = EnhancedSupervisoryFrameWithFcsView::Create(standard_frame_view);
     if (!s_frame_view.IsValid()) {
@@ -908,10 +934,16 @@
   return next;
 }
 
-void ErtmController::stage_for_reassembly(SegmentationAndReassembly sar,
+void ErtmController::stage_for_reassembly(SegmentationAndReassembly sar, uint16_t sdu_size,
                                           const packet::PacketView<kLittleEndian>& payload) {
   switch (sar) {
     case SegmentationAndReassembly::UNSEGMENTED:
+      if (sar_state_ != SegmentationAndReassembly::END) {
+        LOG_WARN("Received invalid SAR");
+        close_channel();
+        return;
+      }
+      // TODO: Enforce MTU
       enqueue_buffer_.Enqueue(std::make_unique<packet::PacketView<kLittleEndian>>(payload), handler_);
       break;
     case SegmentationAndReassembly::START:
@@ -920,8 +952,10 @@
         close_channel();
         return;
       }
+      // TODO: Enforce MTU
       sar_state_ = SegmentationAndReassembly::START;
       reassembly_stage_ = payload;
+      remaining_sdu_continuation_packet_size_ = sdu_size - payload.size();
       break;
     case SegmentationAndReassembly::CONTINUATION:
       if (sar_state_ == SegmentationAndReassembly::END) {
@@ -930,6 +964,7 @@
         return;
       }
       reassembly_stage_.AppendPacketView(payload);
+      remaining_sdu_continuation_packet_size_ -= payload.size();
       break;
     case SegmentationAndReassembly::END:
       if (sar_state_ == SegmentationAndReassembly::END) {
@@ -937,9 +972,17 @@
         close_channel();
         return;
       }
+      sar_state_ = SegmentationAndReassembly::END;
+      remaining_sdu_continuation_packet_size_ -= payload.size();
+      if (remaining_sdu_continuation_packet_size_ != 0) {
+        LOG_WARN("Received invalid END I-Frame");
+        reassembly_stage_ = PacketViewForReassembly(std::make_shared<std::vector<uint8_t>>());
+        remaining_sdu_continuation_packet_size_ = 0;
+        close_channel();
+        return;
+      }
       reassembly_stage_.AppendPacketView(payload);
       enqueue_buffer_.Enqueue(std::make_unique<packet::PacketView<kLittleEndian>>(reassembly_stage_), handler_);
-      sar_state_ = SegmentationAndReassembly::END;
       break;
   }
 }
diff --git a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h
index 14deea3..dd1ca64 100644
--- a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller.h
@@ -59,7 +59,19 @@
   os::Handler* handler_;
   std::queue<std::unique_ptr<packet::BasePacketBuilder>> pdu_queue_;
   Scheduler* scheduler_;
+
+  // Configuration options
   bool fcs_enabled_ = false;
+  uint16_t local_tx_window_ = 10;
+  uint16_t local_max_transmit_ = 20;
+  uint16_t local_retransmit_timeout_ms_ = 2000;
+  uint16_t local_monitor_timeout_ms_ = 12000;
+
+  uint16_t remote_tx_window_ = 10;
+  uint16_t remote_mps_ = 1010;
+
+  uint16_t size_each_packet_ =
+      (remote_mps_ - 4 /* basic L2CAP header */ - 2 /* SDU length */ - 2 /* Extended control */ - 2 /* FCS */);
 
   class PacketViewForReassembly : public packet::PacketView<kLittleEndian> {
    public:
@@ -83,8 +95,10 @@
 
   PacketViewForReassembly reassembly_stage_{std::make_shared<std::vector<uint8_t>>()};
   SegmentationAndReassembly sar_state_ = SegmentationAndReassembly::END;
+  uint16_t remaining_sdu_continuation_packet_size_ = 0;
 
-  void stage_for_reassembly(SegmentationAndReassembly sar, const packet::PacketView<kLittleEndian>& payload);
+  void stage_for_reassembly(SegmentationAndReassembly sar, uint16_t sdu_size,
+                            const packet::PacketView<kLittleEndian>& payload);
   void send_pdu(std::unique_ptr<packet::BasePacketBuilder> pdu);
 
   void close_channel();
@@ -92,18 +106,6 @@
   void on_pdu_no_fcs(const packet::PacketView<true>& pdu);
   void on_pdu_fcs(const packet::PacketView<true>& pdu);
 
-  // Configuration options
-  uint16_t local_tx_window_ = 10;
-  uint16_t local_max_transmit_ = 20;
-  uint16_t local_retransmit_timeout_ms_ = 2000;
-  uint16_t local_monitor_timeout_ms_ = 12000;
-
-  uint16_t remote_tx_window_ = 10;
-  uint16_t remote_mps_ = 1010;
-
-  uint16_t size_each_packet_ =
-      (remote_mps_ - 4 /* basic L2CAP header */ - 2 /* SDU length */ - 2 /* Extended control */ - 2 /* FCS */);
-
   struct impl;
   std::unique_ptr<impl> pimpl_;
 };
diff --git a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller_test.cc b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller_test.cc
index 04ef82b..e1b5818 100644
--- a/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller_test.cc
+++ b/gd/l2cap/internal/enhanced_retransmission_mode_channel_data_controller_test.cc
@@ -108,6 +108,54 @@
   EXPECT_EQ(data, "abcd");
 }
 
+TEST_F(ErtmDataControllerTest, reassemble_valid_sdu) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  ErtmController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  auto segment1 = CreateSdu({'a'});
+  auto segment2 = CreateSdu({'b', 'c'});
+  auto segment3 = CreateSdu({'d', 'e', 'f'});
+  auto builder1 = EnhancedInformationStartFrameBuilder::Create(1, 0, Final::NOT_SET, 0, 6, std::move(segment1));
+  auto base_view = GetPacketView(std::move(builder1));
+  controller.OnPdu(base_view);
+  auto builder2 = EnhancedInformationFrameBuilder::Create(1, 1, Final::NOT_SET, 0,
+                                                          SegmentationAndReassembly::CONTINUATION, std::move(segment2));
+  base_view = GetPacketView(std::move(builder2));
+  controller.OnPdu(base_view);
+  auto builder3 = EnhancedInformationFrameBuilder::Create(1, 2, Final::NOT_SET, 0, SegmentationAndReassembly::END,
+                                                          std::move(segment3));
+  base_view = GetPacketView(std::move(builder3));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto payload = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_NE(payload, nullptr);
+  std::string data = std::string(payload->begin(), payload->end());
+  EXPECT_EQ(data, "abcdef");
+}
+
+TEST_F(ErtmDataControllerTest, reassemble_invalid_sdu_size_in_start_frame) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  ErtmController controller{1, 1, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  auto segment1 = CreateSdu({'a'});
+  auto segment2 = CreateSdu({'b', 'c'});
+  auto segment3 = CreateSdu({'d', 'e', 'f'});
+  auto builder1 = EnhancedInformationStartFrameBuilder::Create(1, 0, Final::NOT_SET, 0, 10, std::move(segment1));
+  auto base_view = GetPacketView(std::move(builder1));
+  controller.OnPdu(base_view);
+  auto builder2 = EnhancedInformationFrameBuilder::Create(1, 1, Final::NOT_SET, 0,
+                                                          SegmentationAndReassembly::CONTINUATION, std::move(segment2));
+  base_view = GetPacketView(std::move(builder2));
+  controller.OnPdu(base_view);
+  auto builder3 = EnhancedInformationFrameBuilder::Create(1, 2, Final::NOT_SET, 0, SegmentationAndReassembly::END,
+                                                          std::move(segment3));
+  base_view = GetPacketView(std::move(builder3));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto payload = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_EQ(payload, nullptr);
+}
+
 TEST_F(ErtmDataControllerTest, transmit_with_fcs) {
   common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
   testing::MockScheduler scheduler;
diff --git a/gd/l2cap/internal/le_credit_based_channel_data_controller.cc b/gd/l2cap/internal/le_credit_based_channel_data_controller.cc
new file mode 100644
index 0000000..eb510c1
--- /dev/null
+++ b/gd/l2cap/internal/le_credit_based_channel_data_controller.cc
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2019 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 "l2cap/internal/le_credit_based_channel_data_controller.h"
+
+#include "l2cap/l2cap_packets.h"
+#include "packet/fragmenting_inserter.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+LeCreditBasedDataController::LeCreditBasedDataController(Cid cid, Cid remote_cid, UpperQueueDownEnd* channel_queue_end,
+                                                         os::Handler* handler, Scheduler* scheduler)
+    : cid_(cid), remote_cid_(remote_cid), enqueue_buffer_(channel_queue_end), handler_(handler), scheduler_(scheduler) {
+}
+
+void LeCreditBasedDataController::OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) {
+  auto sdu_size = sdu->size();
+  if (sdu_size == 0) {
+    LOG_WARN("Received empty SDU");
+    return;
+  }
+  if (sdu_size > mtu_) {
+    LOG_WARN("Received sdu_size %d > mtu %d", static_cast<int>(sdu_size), mtu_);
+  }
+  std::vector<std::unique_ptr<packet::RawBuilder>> segments;
+  // TODO: We don't need to waste 2 bytes for continuation segment.
+  packet::FragmentingInserter fragmenting_inserter(mps_ - 2, std::back_insert_iterator(segments));
+  sdu->Serialize(fragmenting_inserter);
+  fragmenting_inserter.finalize();
+  std::unique_ptr<BasicFrameBuilder> builder;
+  builder = FirstLeInformationFrameBuilder::Create(remote_cid_, sdu_size, std::move(segments[0]));
+  pdu_queue_.emplace(std::move(builder));
+  for (auto i = 1; i < segments.size(); i++) {
+    builder = BasicFrameBuilder::Create(remote_cid_, std::move(segments[i]));
+    pdu_queue_.emplace(std::move(builder));
+  }
+  scheduler_->OnPacketsReady(cid_, segments.size());
+}
+
+void LeCreditBasedDataController::OnPdu(packet::PacketView<true> pdu) {
+  auto basic_frame_view = BasicFrameView::Create(pdu);
+  if (!basic_frame_view.IsValid()) {
+    LOG_WARN("Received invalid frame");
+    return;
+  }
+  if (basic_frame_view.size() > mps_) {
+    LOG_WARN("Received frame size %d > mps %d, dropping the packet", static_cast<int>(basic_frame_view.size()), mps_);
+    return;
+  }
+  if (remaining_sdu_continuation_packet_size_ == 0) {
+    auto start_frame_view = FirstLeInformationFrameView::Create(basic_frame_view);
+    if (!start_frame_view.IsValid()) {
+      LOG_WARN("Received invalid frame");
+      return;
+    }
+    auto payload = start_frame_view.GetPayload();
+    auto sdu_size = start_frame_view.GetL2capSduLength();
+    remaining_sdu_continuation_packet_size_ = sdu_size - payload.size();
+    reassembly_stage_ = payload;
+  } else {
+    auto payload = basic_frame_view.GetPayload();
+    remaining_sdu_continuation_packet_size_ -= payload.size();
+    reassembly_stage_.AppendPacketView(payload);
+  }
+  if (remaining_sdu_continuation_packet_size_ == 0) {
+    enqueue_buffer_.Enqueue(std::make_unique<PacketView<kLittleEndian>>(reassembly_stage_), handler_);
+  } else if (remaining_sdu_continuation_packet_size_ < 0 || reassembly_stage_.size() > mtu_) {
+    LOG_WARN("Received larger SDU size than expected");
+    reassembly_stage_ = PacketViewForReassembly(std::make_shared<std::vector<uint8_t>>());
+    remaining_sdu_continuation_packet_size_ = 0;
+    // TODO: Close channel
+  }
+}
+
+std::unique_ptr<packet::BasePacketBuilder> LeCreditBasedDataController::GetNextPacket() {
+  auto next = std::move(pdu_queue_.front());
+  pdu_queue_.pop();
+  return next;
+}
+
+void LeCreditBasedDataController::SetMtu(Mtu mtu) {
+  mtu_ = mtu;
+}
+
+void LeCreditBasedDataController::SetMps(uint16_t mps) {
+  mps_ = mps;
+}
+
+void LeCreditBasedDataController::OnCredit(uint16_t credits) {
+  int total_credits = credits_ + credits;
+  credits_ = total_credits > 0xffff ? 0xffff : total_credits;
+}
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/le_credit_based_channel_data_controller.h b/gd/l2cap/internal/le_credit_based_channel_data_controller.h
new file mode 100644
index 0000000..9374cd6
--- /dev/null
+++ b/gd/l2cap/internal/le_credit_based_channel_data_controller.h
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2019 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>
+#include <unordered_map>
+#include <utility>
+
+#include "common/bidi_queue.h"
+#include "l2cap/cid.h"
+#include "l2cap/internal/channel_impl.h"
+#include "l2cap/internal/data_controller.h"
+#include "l2cap/internal/scheduler.h"
+#include "l2cap/l2cap_packets.h"
+#include "l2cap/mtu.h"
+#include "os/handler.h"
+#include "os/queue.h"
+#include "packet/base_packet_builder.h"
+#include "packet/packet_view.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+
+class LeCreditBasedDataController : public DataController {
+ public:
+  using UpperEnqueue = packet::PacketView<packet::kLittleEndian>;
+  using UpperDequeue = packet::BasePacketBuilder;
+  using UpperQueueDownEnd = common::BidiQueueEnd<UpperEnqueue, UpperDequeue>;
+  LeCreditBasedDataController(Cid cid, Cid remote_cid, UpperQueueDownEnd* channel_queue_end, os::Handler* handler,
+                              Scheduler* scheduler);
+
+  void OnSdu(std::unique_ptr<packet::BasePacketBuilder> sdu) override;
+  void OnPdu(packet::PacketView<true> pdu) override;
+  std::unique_ptr<packet::BasePacketBuilder> GetNextPacket() override;
+
+  void EnableFcs(bool enabled) override {}
+  void SetRetransmissionAndFlowControlOptions(const RetransmissionAndFlowControlConfigurationOption& option) override {}
+
+  // TODO: Set MTU and MPS from signalling channel
+  void SetMtu(Mtu mtu);
+  void SetMps(uint16_t mps);
+  // TODO: Handle credits
+  void OnCredit(uint16_t credits);
+
+ private:
+  Cid cid_;
+  Cid remote_cid_;
+  os::EnqueueBuffer<UpperEnqueue> enqueue_buffer_;
+  os::Handler* handler_;
+  std::queue<std::unique_ptr<packet::BasePacketBuilder>> pdu_queue_;
+  Scheduler* scheduler_;
+  Mtu mtu_ = 512;
+  uint16_t mps_ = 251;
+  uint16_t credits_ = 0;
+
+  class PacketViewForReassembly : public packet::PacketView<kLittleEndian> {
+   public:
+    PacketViewForReassembly(const PacketView& packetView) : PacketView(packetView) {}
+    void AppendPacketView(packet::PacketView<kLittleEndian> to_append) {
+      Append(to_append);
+    }
+  };
+  PacketViewForReassembly reassembly_stage_{std::make_shared<std::vector<uint8_t>>()};
+  uint16_t remaining_sdu_continuation_packet_size_ = 0;
+};
+
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/l2cap/internal/le_credit_based_channel_data_controller_test.cc b/gd/l2cap/internal/le_credit_based_channel_data_controller_test.cc
new file mode 100644
index 0000000..071e976
--- /dev/null
+++ b/gd/l2cap/internal/le_credit_based_channel_data_controller_test.cc
@@ -0,0 +1,175 @@
+/*
+ * Copyright 2019 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 "l2cap/internal/le_credit_based_channel_data_controller.h"
+
+#include <gtest/gtest.h>
+
+#include "l2cap/internal/scheduler_mock.h"
+#include "l2cap/l2cap_packets.h"
+#include "packet/raw_builder.h"
+
+namespace bluetooth {
+namespace l2cap {
+namespace internal {
+namespace {
+
+std::unique_ptr<packet::BasePacketBuilder> CreateSdu(std::vector<uint8_t> payload) {
+  auto raw_builder = std::make_unique<packet::RawBuilder>();
+  raw_builder->AddOctets(payload);
+  return raw_builder;
+}
+
+PacketView<kLittleEndian> GetPacketView(std::unique_ptr<packet::BasePacketBuilder> packet) {
+  auto bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter i(*bytes);
+  bytes->reserve(packet->size());
+  packet->Serialize(i);
+  return packet::PacketView<packet::kLittleEndian>(bytes);
+}
+
+void sync_handler(os::Handler* handler) {
+  std::promise<void> promise;
+  auto future = promise.get_future();
+  handler->Post(common::BindOnce(&std::promise<void>::set_value, common::Unretained(&promise)));
+  auto status = future.wait_for(std::chrono::milliseconds(300));
+  EXPECT_EQ(status, std::future_status::ready);
+}
+
+class LeCreditBasedDataControllerTest : public ::testing::Test {
+ protected:
+  void SetUp() override {
+    thread_ = new os::Thread("test_thread", os::Thread::Priority::NORMAL);
+    user_handler_ = new os::Handler(thread_);
+    queue_handler_ = new os::Handler(thread_);
+  }
+
+  void TearDown() override {
+    queue_handler_->Clear();
+    user_handler_->Clear();
+    delete queue_handler_;
+    delete user_handler_;
+    delete thread_;
+  }
+
+  os::Thread* thread_ = nullptr;
+  os::Handler* user_handler_ = nullptr;
+  os::Handler* queue_handler_ = nullptr;
+};
+
+TEST_F(LeCreditBasedDataControllerTest, transmit_unsegmented) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  LeCreditBasedDataController controller{0x41, 0x41, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  EXPECT_CALL(scheduler, OnPacketsReady(0x41, 1));
+  controller.OnSdu(CreateSdu({'a', 'b', 'c', 'd'}));
+  auto next_packet = controller.GetNextPacket();
+  EXPECT_NE(next_packet, nullptr);
+  auto view = GetPacketView(std::move(next_packet));
+  auto pdu_view = BasicFrameView::Create(view);
+  EXPECT_TRUE(pdu_view.IsValid());
+  auto first_le_info_view = FirstLeInformationFrameView::Create(pdu_view);
+  EXPECT_TRUE(first_le_info_view.IsValid());
+  auto payload = first_le_info_view.GetPayload();
+  std::string data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "abcd");
+}
+
+TEST_F(LeCreditBasedDataControllerTest, transmit_segmented) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  LeCreditBasedDataController controller{0x41, 0x41, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  controller.SetMps(4);
+  EXPECT_CALL(scheduler, OnPacketsReady(0x41, 2));
+  // Should be divided into 'ab', and 'cd'
+  controller.OnSdu(CreateSdu({'a', 'b', 'c', 'd'}));
+  auto next_packet = controller.GetNextPacket();
+  EXPECT_NE(next_packet, nullptr);
+  auto view = GetPacketView(std::move(next_packet));
+  auto pdu_view = BasicFrameView::Create(view);
+  EXPECT_TRUE(pdu_view.IsValid());
+  auto first_le_info_view = FirstLeInformationFrameView::Create(pdu_view);
+  EXPECT_TRUE(first_le_info_view.IsValid());
+  auto payload = first_le_info_view.GetPayload();
+  std::string data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "ab");
+  EXPECT_EQ(first_le_info_view.GetL2capSduLength(), 4);
+
+  next_packet = controller.GetNextPacket();
+  EXPECT_NE(next_packet, nullptr);
+  view = GetPacketView(std::move(next_packet));
+  pdu_view = BasicFrameView::Create(view);
+  EXPECT_TRUE(pdu_view.IsValid());
+  payload = pdu_view.GetPayload();
+  data = std::string(payload.begin(), payload.end());
+  EXPECT_EQ(data, "cd");
+}
+
+TEST_F(LeCreditBasedDataControllerTest, receive_unsegmented) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  LeCreditBasedDataController controller{0x41, 0x41, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  auto segment = CreateSdu({'a', 'b', 'c', 'd'});
+  auto builder = FirstLeInformationFrameBuilder::Create(0x41, 4, std::move(segment));
+  auto base_view = GetPacketView(std::move(builder));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto payload = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_NE(payload, nullptr);
+  std::string data = std::string(payload->begin(), payload->end());
+  EXPECT_EQ(data, "abcd");
+}
+
+TEST_F(LeCreditBasedDataControllerTest, receive_segmented) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  LeCreditBasedDataController controller{0x41, 0x41, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  auto segment1 = CreateSdu({'a', 'b', 'c', 'd'});
+  auto builder1 = FirstLeInformationFrameBuilder::Create(0x41, 7, std::move(segment1));
+  auto base_view = GetPacketView(std::move(builder1));
+  controller.OnPdu(base_view);
+  auto segment2 = CreateSdu({'e', 'f', 'g'});
+  auto builder2 = BasicFrameBuilder::Create(0x41, std::move(segment2));
+  base_view = GetPacketView(std::move(builder2));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto payload = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_NE(payload, nullptr);
+  std::string data = std::string(payload->begin(), payload->end());
+  EXPECT_EQ(data, "abcdefg");
+}
+
+TEST_F(LeCreditBasedDataControllerTest, receive_segmented_with_wrong_sdu_length) {
+  common::BidiQueue<Scheduler::UpperEnqueue, Scheduler::UpperDequeue> channel_queue{10};
+  testing::MockScheduler scheduler;
+  LeCreditBasedDataController controller{0x41, 0x41, channel_queue.GetDownEnd(), queue_handler_, &scheduler};
+  auto segment1 = CreateSdu({'a', 'b', 'c', 'd'});
+  auto builder1 = FirstLeInformationFrameBuilder::Create(0x41, 5, std::move(segment1));
+  auto base_view = GetPacketView(std::move(builder1));
+  controller.OnPdu(base_view);
+  auto segment2 = CreateSdu({'e', 'f', 'g'});
+  auto builder2 = BasicFrameBuilder::Create(0x41, std::move(segment2));
+  base_view = GetPacketView(std::move(builder2));
+  controller.OnPdu(base_view);
+  sync_handler(queue_handler_);
+  auto payload = channel_queue.GetUpEnd()->TryDequeue();
+  EXPECT_EQ(payload, nullptr);
+}
+
+}  // namespace
+}  // namespace internal
+}  // namespace l2cap
+}  // namespace bluetooth
diff --git a/gd/packet/fragmenting_inserter_unittest.cc b/gd/packet/fragmenting_inserter_unittest.cc
index 6179959..c2f1124 100644
--- a/gd/packet/fragmenting_inserter_unittest.cc
+++ b/gd/packet/fragmenting_inserter_unittest.cc
@@ -91,6 +91,36 @@
   ASSERT_EQ(checksum, observer.GetValue());
 }
 
+TEST(FragmentingInserterTest, testMtuBoundaries) {
+  constexpr size_t kPacketSize = 1024;
+  auto counts = RawBuilder();
+  for (size_t i = 0; i < kPacketSize; i++) {
+    counts.AddOctets1(static_cast<uint8_t>(i));
+  }
+
+  std::vector<std::unique_ptr<RawBuilder>> fragments_mtu_is_kPacketSize;
+  FragmentingInserter it(kPacketSize, std::back_insert_iterator(fragments_mtu_is_kPacketSize));
+  counts.Serialize(it);
+  it.finalize();
+  ASSERT_EQ(1, fragments_mtu_is_kPacketSize.size());
+  ASSERT_EQ(kPacketSize, fragments_mtu_is_kPacketSize[0]->size());
+
+  std::vector<std::unique_ptr<RawBuilder>> fragments_mtu_is_less;
+  FragmentingInserter it_less(kPacketSize - 1, std::back_insert_iterator(fragments_mtu_is_less));
+  counts.Serialize(it_less);
+  it_less.finalize();
+  ASSERT_EQ(2, fragments_mtu_is_less.size());
+  ASSERT_EQ(kPacketSize - 1, fragments_mtu_is_less[0]->size());
+  ASSERT_EQ(1, fragments_mtu_is_less[1]->size());
+
+  std::vector<std::unique_ptr<RawBuilder>> fragments_mtu_is_more;
+  FragmentingInserter it_more(kPacketSize + 1, std::back_insert_iterator(fragments_mtu_is_more));
+  counts.Serialize(it_more);
+  it_more.finalize();
+  ASSERT_EQ(1, fragments_mtu_is_more.size());
+  ASSERT_EQ(kPacketSize, fragments_mtu_is_more[0]->size());
+}
+
 constexpr size_t kPacketSize = 128;
 class FragmentingTest : public ::testing::TestWithParam<size_t> {
  public:
diff --git a/gd/packet/parser/fields/custom_field_fixed_size.cc b/gd/packet/parser/fields/custom_field_fixed_size.cc
index 029f0aa..687d48d 100644
--- a/gd/packet/parser/fields/custom_field_fixed_size.cc
+++ b/gd/packet/parser/fields/custom_field_fixed_size.cc
@@ -55,6 +55,10 @@
   // Do nothing.
 }
 
+void CustomFieldFixedSize::GenInserter(std::ostream& s) const {
+  s << "insert(" << GetName() << "_, i);";
+}
+
 void CustomFieldFixedSize::GenValidator(std::ostream&) const {
   // Do nothing.
 }
diff --git a/gd/packet/parser/fields/custom_field_fixed_size.h b/gd/packet/parser/fields/custom_field_fixed_size.h
index bd19eb0..97acff9 100644
--- a/gd/packet/parser/fields/custom_field_fixed_size.h
+++ b/gd/packet/parser/fields/custom_field_fixed_size.h
@@ -37,6 +37,8 @@
 
   virtual void GenParameterValidator(std::ostream&) const override;
 
+  virtual void GenInserter(std::ostream& s) const override;
+
   virtual void GenValidator(std::ostream&) const override;
 
   std::string type_name_;
diff --git a/gd/packet/parser/fields/scalar_field.cc b/gd/packet/parser/fields/scalar_field.cc
index c6d2ecf..320534a 100644
--- a/gd/packet/parser/fields/scalar_field.cc
+++ b/gd/packet/parser/fields/scalar_field.cc
@@ -121,8 +121,6 @@
 void ScalarField::GenInserter(std::ostream& s) const {
   if (GetSize().bits() == 8) {
     s << "i.insert_byte(" << GetName() << "_);";
-  } else if (GetSize().bits() % 8 == 0) {
-    s << "insert(" << GetName() << "_, i);";
   } else {
     s << "insert(" << GetName() << "_, i," << GetSize().bits() << ");";
   }
diff --git a/gd/packet/parser/test/generated_packet_test.cc b/gd/packet/parser/test/generated_packet_test.cc
index 06f60b5..f8454a2 100644
--- a/gd/packet/parser/test/generated_packet_test.cc
+++ b/gd/packet/parser/test/generated_packet_test.cc
@@ -1825,6 +1825,58 @@
     ASSERT_EQ(ltv_vector[i].value_, an_array[i].value_);
   }
 }
+
+vector<uint8_t> byte_sized{
+    0x11,                                            // 1
+    0x21, 0x22,                                      // 2
+    0x31, 0x32, 0x33,                                // 3
+    0x41, 0x42, 0x43, 0x44,                          // 4
+    0x51, 0x52, 0x53, 0x54, 0x55,                    // 5
+    0x61, 0x62, 0x63, 0x64, 0x65, 0x66,              // 6
+    0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,        // 7
+    0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88,  // 8
+};
+
+TEST(GeneratedPacketTest, testByteSizedFields) {
+  uint64_t array[9]{
+      0xbadbadbad,
+      0x11,                // 1
+      0x2221,              // 2
+      0x333231,            // 3
+      0x44434241,          // 4
+      0x5554535251,        // 5
+      0x666564636261,      // 6
+      0x77767574737271,    // 7
+      0x8887868584838281,  // 8
+  };
+  auto packet =
+      ByteSizedFieldsBuilder::Create(array[1], array[2], array[3], array[4], array[5], array[6], array[7], array[8]);
+  ASSERT_EQ(byte_sized.size(), packet->size());
+
+  std::shared_ptr<std::vector<uint8_t>> packet_bytes = std::make_shared<std::vector<uint8_t>>();
+  BitInserter it(*packet_bytes);
+  packet->Serialize(it);
+
+  ASSERT_EQ(byte_sized.size(), packet_bytes->size());
+  for (size_t i = 0; i < byte_sized.size(); i++) {
+    ASSERT_EQ(byte_sized[i], packet_bytes->at(i));
+  }
+
+  PacketView<kLittleEndian> packet_bytes_view(packet_bytes);
+  auto view = ByteSizedFieldsView::Create(packet_bytes_view);
+  ASSERT_TRUE(view.IsValid());
+  ASSERT_EQ(array[1], view.GetOne());
+  ASSERT_EQ(array[2], view.GetTwo());
+  ASSERT_EQ(array[3], view.GetThree());
+  ASSERT_EQ(array[4], view.GetFour());
+  ASSERT_EQ(array[5], view.GetFive());
+  ASSERT_EQ(array[6], view.GetSix());
+  ASSERT_EQ(array[7], view.GetSeven());
+  ASSERT_EQ(array[8], view.GetEight());
+}
+
+DEFINE_AND_INSTANTIATE_ByteSizedFieldsReflectionTest(byte_sized);
+
 }  // namespace parser
 }  // namespace packet
 }  // namespace bluetooth
diff --git a/gd/packet/parser/test/test_packets.pdl b/gd/packet/parser/test/test_packets.pdl
index 3c24509..2b612cc 100644
--- a/gd/packet/parser/test/test_packets.pdl
+++ b/gd/packet/parser/test/test_packets.pdl
@@ -394,3 +394,14 @@
   one_array : LengthTypeValueStruct[],
   _padding_[40],
 }
+
+packet ByteSizedFields {
+  one : 8,
+  two : 16,
+  three : 24,
+  four : 32,
+  five : 40,
+  six : 48,
+  seven : 56,
+  eight : 64,
+}
diff --git a/gd/packet/raw_builder.cc b/gd/packet/raw_builder.cc
index ec5159b..ec2ff68 100644
--- a/gd/packet/raw_builder.cc
+++ b/gd/packet/raw_builder.cc
@@ -17,6 +17,7 @@
 #include "packet/raw_builder.h"
 
 #include <algorithm>
+#include <utility>
 
 #include "os/log.h"
 
@@ -27,7 +28,7 @@
 namespace packet {
 
 RawBuilder::RawBuilder(size_t max_bytes) : max_bytes_(max_bytes) {}
-RawBuilder::RawBuilder(std::vector<uint8_t> vec) : max_bytes_(vec.size()), payload_(vec) {}
+RawBuilder::RawBuilder(std::vector<uint8_t> vec) : payload_(std::move(vec)) {}
 
 bool RawBuilder::AddOctets(size_t octets, const vector<uint8_t>& bytes) {
   if (payload_.size() + octets > max_bytes_) return false;
diff --git a/gd/packet/raw_builder.h b/gd/packet/raw_builder.h
index 9b0e959..1c9552a 100644
--- a/gd/packet/raw_builder.h
+++ b/gd/packet/raw_builder.h
@@ -64,7 +64,7 @@
   // - the new size of the payload is still <= |max_bytes_|
   bool AddOctets(size_t octets, uint64_t value);
 
-  size_t max_bytes_{255};
+  size_t max_bytes_{0xffff};
 
   // Underlying containers for storing the actual packet
   std::vector<uint8_t> payload_;
diff --git a/gd/packet/raw_builder_unittest.cc b/gd/packet/raw_builder_unittest.cc
index 5210fb6..64ca0ed 100644
--- a/gd/packet/raw_builder_unittest.cc
+++ b/gd/packet/raw_builder_unittest.cc
@@ -65,5 +65,48 @@
   ASSERT_EQ(count, packet);
 }
 
+TEST(RawBuilderTest, buildStartingWithVector) {
+  std::vector<uint8_t> count_first(count.begin(), count.begin() + 0x8);
+  std::unique_ptr<RawBuilder> count_builder = std::make_unique<RawBuilder>(count_first);
+  count_builder->AddOctets4(0x0b0a0908);
+  count_builder->AddOctets2(0x0d0c);
+  count_builder->AddOctets1(0x0e);
+  count_builder->AddOctets1(0x0f);
+  count_builder->AddOctets8(0x1716151413121110);
+  std::vector<uint8_t> count_last(count.begin() + 0x18, count.end());
+  count_builder->AddOctets(count_last);
+
+  ASSERT_EQ(count.size(), count_builder->size());
+
+  std::vector<uint8_t> packet;
+  BitInserter it(packet);
+
+  count_builder->Serialize(it);
+
+  ASSERT_EQ(count, packet);
+}
+
+TEST(RawBuilderTest, testMaxBytes) {
+  const size_t kMaxBytes = count.size();
+  std::unique_ptr<RawBuilder> count_builder = std::make_unique<RawBuilder>(kMaxBytes);
+  ASSERT_TRUE(count_builder->AddOctets(count));
+  ASSERT_FALSE(count_builder->AddOctets4(0x0b0a0908));
+  ASSERT_FALSE(count_builder->AddOctets2(0x0d0c));
+  ASSERT_FALSE(count_builder->AddOctets1(0x0e));
+  ASSERT_FALSE(count_builder->AddOctets1(0x0f));
+  ASSERT_FALSE(count_builder->AddOctets8(0x1716151413121110));
+  std::vector<uint8_t> count_last(count.begin() + 0x18, count.end());
+  ASSERT_FALSE(count_builder->AddOctets(count_last));
+
+  ASSERT_EQ(count.size(), count_builder->size());
+
+  std::vector<uint8_t> packet;
+  BitInserter it(packet);
+
+  count_builder->Serialize(it);
+
+  ASSERT_EQ(count, packet);
+}
+
 }  // namespace packet
 }  // namespace bluetooth